431
438
class RemoveCmd(Command):
432
439
def run_on_one_client(self, client, properties):
440
log.debug("D-Bus: %s:%s:%s.RemoveClient(%r)", busname,
441
server_path, server_interface,
442
str(client.__dbus_object_path__))
433
443
self.mandos.RemoveClient(client.__dbus_object_path__)
435
445
class ApproveCmd(Command):
436
446
def run_on_one_client(self, client, properties):
447
log.debug("D-Bus: %s:%s.Approve(True)",
448
client.__dbus_object_path__, client_interface)
437
449
client.Approve(dbus.Boolean(True),
438
450
dbus_interface=client_interface)
440
452
class DenyCmd(Command):
441
453
def run_on_one_client(self, client, properties):
454
log.debug("D-Bus: %s:%s.Approve(False)",
455
client.__dbus_object_path__, client_interface)
442
456
client.Approve(dbus.Boolean(False),
443
457
dbus_interface=client_interface)
505
519
MillisecondsValueArgumentMixIn):
506
520
property = "ApprovalDuration"
508
def has_actions(options):
509
return any((options.enable,
511
options.bump_timeout,
512
options.start_checker,
513
options.stop_checker,
516
options.checker is not None,
517
options.timeout is not None,
518
options.extended_timeout is not None,
519
options.interval is not None,
520
options.approved_by_default is not None,
521
options.approval_delay is not None,
522
options.approval_duration is not None,
523
options.host is not None,
524
options.secret is not None,
528
522
def add_command_line_options(parser):
529
523
parser.add_argument("--version", action="version",
530
524
version="%(prog)s {}".format(version),
668
parser = argparse.ArgumentParser()
670
add_command_line_options(parser)
672
options = parser.parse_args()
663
def check_option_syntax(parser, options):
664
"""Apply additional restrictions on options, not expressible in
667
def has_actions(options):
668
return any((options.enable,
670
options.bump_timeout,
671
options.start_checker,
672
options.stop_checker,
675
options.checker is not None,
676
options.timeout is not None,
677
options.extended_timeout is not None,
678
options.interval is not None,
679
options.approved_by_default is not None,
680
options.approval_delay is not None,
681
options.approval_duration is not None,
682
options.host is not None,
683
options.secret is not None,
674
687
if has_actions(options) and not (options.client or options.all):
675
688
parser.error("Options require clients names or --all.")
1093
1128
class TestSetSecretCmd(TestValueArgumentPropertyCmd):
1094
1129
command = SetSecretCmd
1095
1130
property = "Secret"
1096
values_to_set = [open("/dev/null", "rb"),
1131
values_to_set = [io.BytesIO(b""),
1097
1132
io.BytesIO(b"secret\0xyzzy\nbar")]
1098
1133
values_to_get = [b"", b"secret\0xyzzy\nbar"]
1100
1135
class TestSetTimeoutCmd(TestValueArgumentPropertyCmd):
1101
1136
command = SetTimeoutCmd
1102
1137
property = "Timeout"
1103
values_to_set = ["P0D", "PT5M", "PT1S", "PT120S", "P1Y"]
1104
values_to_get = [0, 300000, 1000, 120000, 31449600000]
1138
values_to_set = [datetime.timedelta(),
1139
datetime.timedelta(minutes=5),
1140
datetime.timedelta(seconds=1),
1141
datetime.timedelta(weeks=1),
1142
datetime.timedelta(weeks=52)]
1143
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1106
1145
class TestSetExtendedTimeoutCmd(TestValueArgumentPropertyCmd):
1107
1146
command = SetExtendedTimeoutCmd
1108
1147
property = "ExtendedTimeout"
1109
values_to_set = ["P0D", "PT5M", "PT1S", "PT120S", "P1Y"]
1110
values_to_get = [0, 300000, 1000, 120000, 31449600000]
1148
values_to_set = [datetime.timedelta(),
1149
datetime.timedelta(minutes=5),
1150
datetime.timedelta(seconds=1),
1151
datetime.timedelta(weeks=1),
1152
datetime.timedelta(weeks=52)]
1153
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1112
1155
class TestSetIntervalCmd(TestValueArgumentPropertyCmd):
1113
1156
command = SetIntervalCmd
1114
1157
property = "Interval"
1115
values_to_set = ["P0D", "PT5M", "PT1S", "PT120S", "P1Y"]
1116
values_to_get = [0, 300000, 1000, 120000, 31449600000]
1158
values_to_set = [datetime.timedelta(),
1159
datetime.timedelta(minutes=5),
1160
datetime.timedelta(seconds=1),
1161
datetime.timedelta(weeks=1),
1162
datetime.timedelta(weeks=52)]
1163
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1118
1165
class TestSetApprovalDelayCmd(TestValueArgumentPropertyCmd):
1119
1166
command = SetApprovalDelayCmd
1120
1167
property = "ApprovalDelay"
1121
values_to_set = ["P0D", "PT5M", "PT1S", "PT120S", "P1Y"]
1122
values_to_get = [0, 300000, 1000, 120000, 31449600000]
1168
values_to_set = [datetime.timedelta(),
1169
datetime.timedelta(minutes=5),
1170
datetime.timedelta(seconds=1),
1171
datetime.timedelta(weeks=1),
1172
datetime.timedelta(weeks=52)]
1173
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1124
1175
class TestSetApprovalDurationCmd(TestValueArgumentPropertyCmd):
1125
1176
command = SetApprovalDurationCmd
1126
1177
property = "ApprovalDuration"
1127
values_to_set = ["P0D", "PT5M", "PT1S", "PT120S", "P1Y"]
1128
values_to_get = [0, 300000, 1000, 120000, 31449600000]
1178
values_to_set = [datetime.timedelta(),
1179
datetime.timedelta(minutes=5),
1180
datetime.timedelta(seconds=1),
1181
datetime.timedelta(weeks=1),
1182
datetime.timedelta(weeks=52)]
1183
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1130
1185
class Test_command_from_options(unittest.TestCase):
1131
1186
def setUp(self):
1149
1205
self.assert_command_from_args(["--verbose"], PrintTableCmd,
1208
def test_print_table_verbose_short(self):
1209
self.assert_command_from_args(["-v"], PrintTableCmd,
1152
1212
def test_enable(self):
1153
1213
self.assert_command_from_args(["--enable", "foo"], EnableCmd)
1215
def test_enable_short(self):
1216
self.assert_command_from_args(["-e", "foo"], EnableCmd)
1155
1218
def test_disable(self):
1156
1219
self.assert_command_from_args(["--disable", "foo"],
1222
def test_disable_short(self):
1223
self.assert_command_from_args(["-d", "foo"], DisableCmd)
1159
1225
def test_bump_timeout(self):
1160
1226
self.assert_command_from_args(["--bump-timeout", "foo"],
1161
1227
BumpTimeoutCmd)
1229
def test_bump_timeout_short(self):
1230
self.assert_command_from_args(["-b", "foo"], BumpTimeoutCmd)
1163
1232
def test_start_checker(self):
1164
1233
self.assert_command_from_args(["--start-checker", "foo"],
1165
1234
StartCheckerCmd)
1172
1241
self.assert_command_from_args(["--remove", "foo"],
1244
def test_remove_short(self):
1245
self.assert_command_from_args(["-r", "foo"], RemoveCmd)
1175
1247
def test_checker(self):
1176
1248
self.assert_command_from_args(["--checker", ":", "foo"],
1177
1249
SetCheckerCmd, value_to_set=":")
1251
def test_checker_empty(self):
1252
self.assert_command_from_args(["--checker", "", "foo"],
1253
SetCheckerCmd, value_to_set="")
1255
def test_checker_short(self):
1256
self.assert_command_from_args(["-c", ":", "foo"],
1257
SetCheckerCmd, value_to_set=":")
1179
1259
def test_timeout(self):
1180
1260
self.assert_command_from_args(["--timeout", "PT5M", "foo"],
1182
1262
value_to_set=300000)
1264
def test_timeout_short(self):
1265
self.assert_command_from_args(["-t", "PT5M", "foo"],
1267
value_to_set=300000)
1184
1269
def test_extended_timeout(self):
1185
1270
self.assert_command_from_args(["--extended-timeout", "PT15M",
1229
1324
"foo"], SetSecretCmd,
1230
1325
value_to_set=value)
1327
def test_secret_devnull_short(self):
1328
self.assert_command_from_args(["-s", os.path.devnull, "foo"],
1329
SetSecretCmd, value_to_set=b"")
1331
def test_secret_tempfile_short(self):
1332
with tempfile.NamedTemporaryFile(mode="r+b") as f:
1333
value = b"secret\0xyzzy\nbar"
1336
self.assert_command_from_args(["-s", f.name, "foo"],
1232
1340
def test_approve(self):
1233
1341
self.assert_command_from_args(["--approve", "foo"],
1344
def test_approve_short(self):
1345
self.assert_command_from_args(["-A", "foo"], ApproveCmd)
1236
1347
def test_deny(self):
1237
1348
self.assert_command_from_args(["--deny", "foo"], DenyCmd)
1350
def test_deny_short(self):
1351
self.assert_command_from_args(["-D", "foo"], DenyCmd)
1239
1353
def test_dump_json(self):
1240
1354
self.assert_command_from_args(["--dump-json"], DumpJSONCmd)
1243
1357
self.assert_command_from_args(["--is-enabled", "foo"],
1360
def test_is_enabled_short(self):
1361
self.assert_command_from_args(["-V", "foo"], IsEnabledCmd)
1363
def test_deny_before_remove(self):
1364
options = self.parser.parse_args(["--deny", "--remove", "foo"])
1365
check_option_syntax(self.parser, options)
1366
commands = commands_from_options(options)
1367
self.assertEqual(len(commands), 2)
1368
self.assertIsInstance(commands[0], DenyCmd)
1369
self.assertIsInstance(commands[1], RemoveCmd)
1371
def test_deny_before_remove_reversed(self):
1372
options = self.parser.parse_args(["--remove", "--deny", "--all"])
1373
check_option_syntax(self.parser, options)
1374
commands = commands_from_options(options)
1375
self.assertEqual(len(commands), 2)
1376
self.assertIsInstance(commands[0], DenyCmd)
1377
self.assertIsInstance(commands[1], RemoveCmd)
1380
class Test_check_option_syntax(unittest.TestCase):
1381
# This mostly corresponds to the definition from has_actions() in
1382
# check_option_syntax()
1384
# The actual values set here are not that important, but we do
1385
# at least stick to the correct types, even though they are
1389
"bump_timeout": True,
1390
"start_checker": True,
1391
"stop_checker": True,
1395
"timeout": datetime.timedelta(),
1396
"extended_timeout": datetime.timedelta(),
1397
"interval": datetime.timedelta(),
1398
"approved_by_default": True,
1399
"approval_delay": datetime.timedelta(),
1400
"approval_duration": datetime.timedelta(),
1402
"secret": io.BytesIO(b"x"),
1408
self.parser = argparse.ArgumentParser()
1409
add_command_line_options(self.parser)
1411
@contextlib.contextmanager
1412
def assertParseError(self):
1413
with self.assertRaises(SystemExit) as e:
1414
with self.temporarily_suppress_stderr():
1416
# Exit code from argparse is guaranteed to be "2". Reference:
1417
# https://docs.python.org/3/library/argparse.html#exiting-methods
1418
self.assertEqual(e.exception.code, 2)
1421
@contextlib.contextmanager
1422
def temporarily_suppress_stderr():
1423
null = os.open(os.path.devnull, os.O_RDWR)
1424
stderrcopy = os.dup(sys.stderr.fileno())
1425
os.dup2(null, sys.stderr.fileno())
1431
os.dup2(stderrcopy, sys.stderr.fileno())
1432
os.close(stderrcopy)
1434
def check_option_syntax(self, options):
1435
check_option_syntax(self.parser, options)
1437
def test_actions_requires_client_or_all(self):
1438
for action, value in self.actions.items():
1439
options = self.parser.parse_args()
1440
setattr(options, action, value)
1441
with self.assertParseError():
1442
self.check_option_syntax(options)
1444
def test_actions_conflicts_with_verbose(self):
1445
for action, value in self.actions.items():
1446
options = self.parser.parse_args()
1447
setattr(options, action, value)
1448
options.verbose = True
1449
with self.assertParseError():
1450
self.check_option_syntax(options)
1452
def test_dump_json_conflicts_with_verbose(self):
1453
options = self.parser.parse_args()
1454
options.dump_json = True
1455
options.verbose = True
1456
with self.assertParseError():
1457
self.check_option_syntax(options)
1459
def test_dump_json_conflicts_with_action(self):
1460
for action, value in self.actions.items():
1461
options = self.parser.parse_args()
1462
setattr(options, action, value)
1463
options.dump_json = True
1464
with self.assertParseError():
1465
self.check_option_syntax(options)
1467
def test_all_can_not_be_alone(self):
1468
options = self.parser.parse_args()
1470
with self.assertParseError():
1471
self.check_option_syntax(options)
1473
def test_all_is_ok_with_any_action(self):
1474
for action, value in self.actions.items():
1475
options = self.parser.parse_args()
1476
setattr(options, action, value)
1478
self.check_option_syntax(options)
1480
def test_is_enabled_fails_without_client(self):
1481
options = self.parser.parse_args()
1482
options.is_enabled = True
1483
with self.assertParseError():
1484
self.check_option_syntax(options)
1486
def test_is_enabled_works_with_one_client(self):
1487
options = self.parser.parse_args()
1488
options.is_enabled = True
1489
options.client = ["foo"]
1490
self.check_option_syntax(options)
1492
def test_is_enabled_fails_with_two_clients(self):
1493
options = self.parser.parse_args()
1494
options.is_enabled = True
1495
options.client = ["foo", "barbar"]
1496
with self.assertParseError():
1497
self.check_option_syntax(options)
1499
def test_remove_can_only_be_combined_with_action_deny(self):
1500
for action, value in self.actions.items():
1501
if action in {"remove", "deny"}:
1503
options = self.parser.parse_args()
1504
setattr(options, action, value)
1506
options.remove = True
1507
with self.assertParseError():
1508
self.check_option_syntax(options)
1248
1512
def should_only_run_tests():