232
def print_clients(clients, keywords):
233
def valuetostring(value, keyword):
234
if type(value) is dbus.Boolean:
235
return "Yes" if value else "No"
236
if keyword in ("Timeout", "Interval", "ApprovalDelay",
237
"ApprovalDuration", "ExtendedTimeout"):
238
return milliseconds_to_string(value)
241
# Create format string to print table rows
242
format_string = " ".join("{{{key}:{width}}}".format(
243
width = max(len(tablewords[key]),
244
max(len(valuetostring(client[key], key))
245
for client in clients)),
249
print(format_string.format(**tablewords))
250
for client in clients:
251
print(format_string.format(**{
252
key: valuetostring(client[key], key)
253
for key in keywords }))
271
## Classes for commands.
273
# Abstract classes first
274
class Command(object):
275
"""Abstract class for commands"""
276
def run(self, mandos, clients):
277
"""Normal commands should implement run_on_one_client(), but
278
commands which want to operate on all clients at the same time
279
can override this run() method instead."""
281
for client, properties in clients.items():
282
self.run_on_one_client(client, properties)
284
class PrintCmd(Command):
285
"""Abstract class for commands printing client details"""
286
all_keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
287
"Created", "Interval", "Host", "KeyID",
288
"Fingerprint", "CheckerRunning", "LastEnabled",
289
"ApprovalPending", "ApprovedByDefault",
290
"LastApprovalRequest", "ApprovalDelay",
291
"ApprovalDuration", "Checker", "ExtendedTimeout",
292
"Expires", "LastCheckerStatus")
293
def run(self, mandos, clients):
294
print(self.output(clients))
296
class PropertyCmd(Command):
297
"""Abstract class for Actions for setting one client property"""
298
def run_on_one_client(self, client, properties):
299
"""Set the Client's D-Bus property"""
300
client.Set(client_interface, self.property, self.value_to_set,
301
dbus_interface=dbus.PROPERTIES_IFACE)
303
class ValueArgumentMixIn(object):
304
"""Mixin class for commands taking a value as argument"""
305
def __init__(self, value):
306
self.value_to_set = value
308
class MillisecondsValueArgumentMixIn(ValueArgumentMixIn):
309
"""Mixin class for commands taking a value argument as
312
def value_to_set(self):
315
def value_to_set(self, value):
316
"""When setting, convert value to a datetime.timedelta"""
317
self._vts = string_to_delta(value).total_seconds() * 1000
319
# Actual (non-abstract) command classes
321
class PrintTableCmd(PrintCmd):
322
def __init__(self, verbose=False):
323
self.verbose = verbose
325
def output(self, clients):
327
keywords = self.all_keywords
329
keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK")
330
return str(self.TableOfClients(clients.values(), keywords))
332
class TableOfClients(object):
335
"Enabled": "Enabled",
336
"Timeout": "Timeout",
337
"LastCheckedOK": "Last Successful Check",
338
"LastApprovalRequest": "Last Approval Request",
339
"Created": "Created",
340
"Interval": "Interval",
342
"Fingerprint": "Fingerprint",
344
"CheckerRunning": "Check Is Running",
345
"LastEnabled": "Last Enabled",
346
"ApprovalPending": "Approval Is Pending",
347
"ApprovedByDefault": "Approved By Default",
348
"ApprovalDelay": "Approval Delay",
349
"ApprovalDuration": "Approval Duration",
350
"Checker": "Checker",
351
"ExtendedTimeout": "Extended Timeout",
352
"Expires": "Expires",
353
"LastCheckerStatus": "Last Checker Status",
356
def __init__(self, clients, keywords, tableheaders=None):
357
self.clients = clients
358
self.keywords = keywords
359
if tableheaders is not None:
360
self.tableheaders = tableheaders
363
return "\n".join(self.rows())
365
if sys.version_info.major == 2:
366
__unicode__ = __str__
368
return str(self).encode(locale.getpreferredencoding())
371
format_string = self.row_formatting_string()
372
rows = [self.header_line(format_string)]
373
rows.extend(self.client_line(client, format_string)
374
for client in self.clients)
377
def row_formatting_string(self):
378
"Format string used to format table rows"
379
return " ".join("{{{key}:{width}}}".format(
380
width=max(len(self.tableheaders[key]),
381
*(len(self.string_from_client(client, key))
382
for client in self.clients)),
384
for key in self.keywords)
386
def string_from_client(self, client, key):
387
return self.valuetostring(client[key], key)
390
def valuetostring(value, keyword):
391
if isinstance(value, dbus.Boolean):
392
return "Yes" if value else "No"
393
if keyword in ("Timeout", "Interval", "ApprovalDelay",
394
"ApprovalDuration", "ExtendedTimeout"):
395
return milliseconds_to_string(value)
398
def header_line(self, format_string):
399
return format_string.format(**self.tableheaders)
401
def client_line(self, client, format_string):
402
return format_string.format(
403
**{key: self.string_from_client(client, key)
404
for key in self.keywords})
408
class DumpJSONCmd(PrintCmd):
409
def output(self, clients):
410
data = {client["Name"]:
411
{key: self.dbus_boolean_to_bool(client[key])
412
for key in self.all_keywords}
413
for client in clients.values()}
414
return json.dumps(data, indent=4, separators=(',', ': '))
416
def dbus_boolean_to_bool(value):
417
if isinstance(value, dbus.Boolean):
421
class IsEnabledCmd(Command):
422
def run_on_one_client(self, client, properties):
423
if self.is_enabled(client, properties):
426
def is_enabled(self, client, properties):
427
return bool(properties["Enabled"])
429
class RemoveCmd(Command):
430
def run_on_one_client(self, client, properties):
431
self.mandos.RemoveClient(client.__dbus_object_path__)
433
class ApproveCmd(Command):
434
def run_on_one_client(self, client, properties):
435
client.Approve(dbus.Boolean(True),
436
dbus_interface=client_interface)
438
class DenyCmd(Command):
439
def run_on_one_client(self, client, properties):
440
client.Approve(dbus.Boolean(False),
441
dbus_interface=client_interface)
443
class EnableCmd(PropertyCmd):
445
value_to_set = dbus.Boolean(True)
447
class DisableCmd(PropertyCmd):
449
value_to_set = dbus.Boolean(False)
451
class BumpTimeoutCmd(PropertyCmd):
452
property = "LastCheckedOK"
455
class StartCheckerCmd(PropertyCmd):
456
property = "CheckerRunning"
457
value_to_set = dbus.Boolean(True)
459
class StopCheckerCmd(PropertyCmd):
460
property = "CheckerRunning"
461
value_to_set = dbus.Boolean(False)
463
class ApproveByDefaultCmd(PropertyCmd):
464
property = "ApprovedByDefault"
465
value_to_set = dbus.Boolean(True)
467
class DenyByDefaultCmd(PropertyCmd):
468
property = "ApprovedByDefault"
469
value_to_set = dbus.Boolean(False)
471
class SetCheckerCmd(PropertyCmd, ValueArgumentMixIn):
474
class SetHostCmd(PropertyCmd, ValueArgumentMixIn):
477
class SetSecretCmd(PropertyCmd, ValueArgumentMixIn):
480
class SetTimeoutCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
483
class SetExtendedTimeoutCmd(PropertyCmd,
484
MillisecondsValueArgumentMixIn):
485
property = "ExtendedTimeout"
487
class SetIntervalCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
488
property = "Interval"
490
class SetApprovalDelayCmd(PropertyCmd,
491
MillisecondsValueArgumentMixIn):
492
property = "ApprovalDelay"
494
class SetApprovalDurationCmd(PropertyCmd,
495
MillisecondsValueArgumentMixIn):
496
property = "ApprovalDuration"
256
498
def has_actions(options):
257
499
return any((options.enable,
321
569
parser.add_argument("-s", "--secret",
322
570
type=argparse.FileType(mode="rb"),
323
571
help="Set password blob (file) for client")
324
parser.add_argument("-A", "--approve", action="store_true",
325
help="Approve any current client request")
326
parser.add_argument("-D", "--deny", action="store_true",
327
help="Deny any current client request")
572
approve_deny = parser.add_mutually_exclusive_group()
573
approve_deny.add_argument(
574
"-A", "--approve", action="store_true",
575
help="Approve any current client request")
576
approve_deny.add_argument("-D", "--deny", action="store_true",
577
help="Deny any current client request")
328
578
parser.add_argument("--check", action="store_true",
329
579
help="Run self-test")
330
580
parser.add_argument("client", nargs="*", help="Client name")
583
def commands_and_clients_from_options(options):
587
if options.dump_json:
588
commands.append(DumpJSONCmd())
591
commands.append(EnableCmd())
594
commands.append(DisableCmd())
596
if options.bump_timeout:
597
commands.append(BumpTimeoutCmd(options.bump_timeout))
599
if options.start_checker:
600
commands.append(StartCheckerCmd())
602
if options.stop_checker:
603
commands.append(StopCheckerCmd())
605
if options.is_enabled:
606
commands.append(IsEnabledCmd())
609
commands.append(RemoveCmd())
611
if options.checker is not None:
612
commands.append(SetCheckerCmd())
614
if options.timeout is not None:
615
commands.append(SetTimeoutCmd(options.timeout))
617
if options.extended_timeout:
619
SetExtendedTimeoutCmd(options.extended_timeout))
621
if options.interval is not None:
622
command.append(SetIntervalCmd(options.interval))
624
if options.approved_by_default is not None:
625
if options.approved_by_default:
626
command.append(ApproveByDefaultCmd())
628
command.append(DenyByDefaultCmd())
630
if options.approval_delay is not None:
631
command.append(SetApprovalDelayCmd(options.approval_delay))
633
if options.approval_duration is not None:
635
SetApprovalDurationCmd(options.approval_duration))
637
if options.host is not None:
638
command.append(SetHostCmd(options.host))
640
if options.secret is not None:
641
command.append(SetSecretCmd(options.secret))
644
commands.append(ApproveCmd())
647
commands.append(DenyCmd())
649
# If no command option has been given, show table of clients,
650
# optionally verbosely
652
commands.append(PrintTableCmd(verbose=options.verbose))
654
return commands, options.client
658
parser = argparse.ArgumentParser()
660
add_command_line_options(parser)
331
662
options = parser.parse_args()
333
664
if has_actions(options) and not (options.client or options.all):
334
665
parser.error("Options require clients names or --all.")
335
666
if options.verbose and has_actions(options):
336
667
parser.error("--verbose can only be used alone.")
337
if options.dump_json and (options.verbose or has_actions(options)):
668
if options.dump_json and (options.verbose
669
or has_actions(options)):
338
670
parser.error("--dump-json can only be used alone.")
339
671
if options.all and not has_actions(options):
340
672
parser.error("--all requires an action.")
343
fail_count, test_count = doctest.testmod()
344
sys.exit(os.EX_OK if fail_count == 0 else 1)
673
if options.is_enabled and len(options.client) > 1:
674
parser.error("--is-enabled requires exactly one client")
676
commands, clientnames = commands_and_clients_from_options(options)
347
679
bus = dbus.SystemBus()
348
680
mandos_dbus_objc = bus.get_object(busname, server_path)
349
681
except dbus.exceptions.DBusException:
350
print("Could not connect to Mandos server", file=sys.stderr)
682
log.critical("Could not connect to Mandos server")
353
685
mandos_serv = dbus.Interface(mandos_dbus_objc,
354
dbus_interface = server_interface)
686
dbus_interface=server_interface)
355
687
mandos_serv_object_manager = dbus.Interface(
356
mandos_dbus_objc, dbus_interface = dbus.OBJECT_MANAGER_IFACE)
358
#block stderr since dbus library prints to stderr
359
null = os.open(os.path.devnull, os.O_RDWR)
360
stderrcopy = os.dup(sys.stderr.fileno())
361
os.dup2(null, sys.stderr.fileno())
688
mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
690
# Filter out log message from dbus module
691
dbus_logger = logging.getLogger("dbus.proxies")
692
class NullFilter(logging.Filter):
693
def filter(self, record):
695
dbus_filter = NullFilter()
365
mandos_clients = { path: ifs_and_props[client_interface]
366
for path, ifs_and_props in
367
mandos_serv_object_manager
368
.GetManagedObjects().items()
369
if client_interface in ifs_and_props }
372
os.dup2(stderrcopy, sys.stderr.fileno())
697
dbus_logger.addFilter(dbus_filter)
698
mandos_clients = {path: ifs_and_props[client_interface]
699
for path, ifs_and_props in
700
mandos_serv_object_manager
701
.GetManagedObjects().items()
702
if client_interface in ifs_and_props}
374
703
except dbus.exceptions.DBusException as e:
375
print("Access denied: Accessing mandos server through D-Bus: {}"
376
.format(e), file=sys.stderr)
704
log.critical("Failed to access Mandos server through D-Bus:"
708
# restore dbus logger
709
dbus_logger.removeFilter(dbus_filter)
379
711
# Compile dict of (clients: properties) to process
382
if options.all or not options.client:
383
clients = { bus.get_object(busname, path): properties
384
for path, properties in mandos_clients.items() }
715
clients = {bus.get_object(busname, path): properties
716
for path, properties in mandos_clients.items()}
386
for name in options.client:
718
for name in clientnames:
387
719
for path, client in mandos_clients.items():
388
720
if client["Name"] == name:
389
721
client_objc = bus.get_object(busname, path)
390
722
clients[client_objc] = client
393
print("Client not found on server: {!r}"
394
.format(name), file=sys.stderr)
725
log.critical("Client not found on server: %r", name)
397
if not has_actions(options) and clients:
398
if options.verbose or options.dump_json:
399
keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
400
"Created", "Interval", "Host", "Fingerprint",
401
"CheckerRunning", "LastEnabled",
402
"ApprovalPending", "ApprovedByDefault",
403
"LastApprovalRequest", "ApprovalDelay",
404
"ApprovalDuration", "Checker",
405
"ExtendedTimeout", "Expires",
408
keywords = defaultkeywords
410
if options.dump_json:
411
json.dump({client["Name"]: {key:
413
if isinstance(client[key],
416
for key in keywords }
417
for client in clients.values() },
418
fp = sys.stdout, indent = 4,
419
separators = (',', ': '))
422
print_clients(clients.values(), keywords)
424
# Process each client in the list by all selected options
425
for client in clients:
427
def set_client_prop(prop, value):
428
"""Set a Client D-Bus property"""
429
client.Set(client_interface, prop, value,
430
dbus_interface=dbus.PROPERTIES_IFACE)
432
def set_client_prop_ms(prop, value):
433
"""Set a Client D-Bus property, converted
434
from a string to milliseconds."""
435
set_client_prop(prop,
436
string_to_delta(value).total_seconds()
440
mandos_serv.RemoveClient(client.__dbus_object_path__)
442
set_client_prop("Enabled", dbus.Boolean(True))
444
set_client_prop("Enabled", dbus.Boolean(False))
445
if options.bump_timeout:
446
set_client_prop("LastCheckedOK", "")
447
if options.start_checker:
448
set_client_prop("CheckerRunning", dbus.Boolean(True))
449
if options.stop_checker:
450
set_client_prop("CheckerRunning", dbus.Boolean(False))
451
if options.is_enabled:
452
sys.exit(0 if client.Get(client_interface,
455
dbus.PROPERTIES_IFACE)
457
if options.checker is not None:
458
set_client_prop("Checker", options.checker)
459
if options.host is not None:
460
set_client_prop("Host", options.host)
461
if options.interval is not None:
462
set_client_prop_ms("Interval", options.interval)
463
if options.approval_delay is not None:
464
set_client_prop_ms("ApprovalDelay",
465
options.approval_delay)
466
if options.approval_duration is not None:
467
set_client_prop_ms("ApprovalDuration",
468
options.approval_duration)
469
if options.timeout is not None:
470
set_client_prop_ms("Timeout", options.timeout)
471
if options.extended_timeout is not None:
472
set_client_prop_ms("ExtendedTimeout",
473
options.extended_timeout)
474
if options.secret is not None:
475
set_client_prop("Secret",
476
dbus.ByteArray(options.secret.read()))
477
if options.approved_by_default is not None:
478
set_client_prop("ApprovedByDefault",
480
.approved_by_default))
482
client.Approve(dbus.Boolean(True),
483
dbus_interface=client_interface)
485
client.Approve(dbus.Boolean(False),
486
dbus_interface=client_interface)
728
# Run all commands on clients
729
for command in commands:
730
command.run(mandos_serv, clients)
733
class Test_milliseconds_to_string(unittest.TestCase):
735
self.assertEqual(milliseconds_to_string(93785000),
737
def test_no_days(self):
738
self.assertEqual(milliseconds_to_string(7385000), "02:03:05")
739
def test_all_zero(self):
740
self.assertEqual(milliseconds_to_string(0), "00:00:00")
741
def test_no_fractional_seconds(self):
742
self.assertEqual(milliseconds_to_string(400), "00:00:00")
743
self.assertEqual(milliseconds_to_string(900), "00:00:00")
744
self.assertEqual(milliseconds_to_string(1900), "00:00:01")
746
class Test_string_to_delta(unittest.TestCase):
747
def test_handles_basic_rfc3339(self):
748
self.assertEqual(string_to_delta("PT2H"),
749
datetime.timedelta(0, 7200))
750
def test_falls_back_to_pre_1_6_1_with_warning(self):
751
# assertLogs only exists in Python 3.4
752
if hasattr(self, "assertLogs"):
753
with self.assertLogs(log, logging.WARNING):
754
value = string_to_delta("2h")
756
class WarningFilter(logging.Filter):
757
"""Don't show, but record the presence of, warnings"""
758
def filter(self, record):
759
is_warning = record.levelno >= logging.WARNING
760
self.found = is_warning or getattr(self, "found",
762
return not is_warning
763
warning_filter = WarningFilter()
764
log.addFilter(warning_filter)
766
value = string_to_delta("2h")
768
log.removeFilter(warning_filter)
769
self.assertTrue(getattr(warning_filter, "found", False))
770
self.assertEqual(value, datetime.timedelta(0, 7200))
773
class TestCmd(unittest.TestCase):
774
"""Abstract class for tests of command classes"""
777
class MockClient(object):
778
def __init__(self, name, **attributes):
779
self.__dbus_object_path__ = "objpath_{}".format(name)
780
self.attributes = attributes
781
self.attributes["Name"] = name
783
def Set(self, interface, property, value, dbus_interface):
784
testcase.assertEqual(interface, client_interface)
785
testcase.assertEqual(dbus_interface,
786
dbus.PROPERTIES_IFACE)
787
self.attributes[property] = value
788
self.calls.append(("Set", (interface, property, value,
790
def Get(self, interface, property, dbus_interface):
791
testcase.assertEqual(interface, client_interface)
792
testcase.assertEqual(dbus_interface,
793
dbus.PROPERTIES_IFACE)
794
self.calls.append(("Get", (interface, property,
796
return self.attributes[property]
797
def Approve(self, approve, dbus_interface):
798
testcase.assertEqual(dbus_interface, client_interface)
799
self.calls.append(("Approve", (approve,
801
self.client = MockClient(
803
KeyID=("92ed150794387c03ce684574b1139a65"
804
"94a34f895daaaf09fd8ea90a27cddb12"),
806
Host="foo.example.org",
807
Enabled=dbus.Boolean(True),
809
LastCheckedOK="2019-02-03T00:00:00",
810
Created="2019-01-02T00:00:00",
812
Fingerprint=("778827225BA7DE539C5A"
813
"7CFA59CFF7CDBD9A5920"),
814
CheckerRunning=dbus.Boolean(False),
815
LastEnabled="2019-01-03T00:00:00",
816
ApprovalPending=dbus.Boolean(False),
817
ApprovedByDefault=dbus.Boolean(True),
818
LastApprovalRequest="",
820
ApprovalDuration=1000,
821
Checker="fping -q -- %(host)s",
822
ExtendedTimeout=900000,
823
Expires="2019-02-04T00:00:00",
825
self.other_client = MockClient(
827
KeyID=("0558568eedd67d622f5c83b35a115f79"
828
"6ab612cff5ad227247e46c2b020f441c"),
831
Enabled=dbus.Boolean(True),
833
LastCheckedOK="2019-02-04T00:00:00",
834
Created="2019-01-03T00:00:00",
836
Fingerprint=("3E393AEAEFB84C7E89E2"
837
"F547B3A107558FCA3A27"),
838
CheckerRunning=dbus.Boolean(True),
839
LastEnabled="2019-01-04T00:00:00",
840
ApprovalPending=dbus.Boolean(False),
841
ApprovedByDefault=dbus.Boolean(False),
842
LastApprovalRequest="2019-01-03T00:00:00",
844
ApprovalDuration=1000,
846
ExtendedTimeout=900000,
847
Expires="2019-02-05T00:00:00",
848
LastCheckerStatus=-2)
849
self.clients = collections.OrderedDict(
851
(self.client, self.client.attributes),
852
(self.other_client, self.other_client.attributes),
854
self.one_client = {self.client: self.client.attributes}
856
class TestPrintTableCmd(TestCmd):
857
def test_normal(self):
858
output = PrintTableCmd().output(self.clients)
859
expected_output = """
860
Name Enabled Timeout Last Successful Check
861
foo Yes 00:05:00 2019-02-03T00:00:00
862
barbar Yes 00:05:00 2019-02-04T00:00:00
864
self.assertEqual(output, expected_output)
865
def test_verbose(self):
866
output = PrintTableCmd(verbose=True).output(self.clients)
867
expected_output = """
868
Name Enabled Timeout Last Successful Check Created Interval Host Key ID Fingerprint Check Is Running Last Enabled Approval Is Pending Approved By Default Last Approval Request Approval Delay Approval Duration Checker Extended Timeout Expires Last Checker Status
869
foo Yes 00:05:00 2019-02-03T00:00:00 2019-01-02T00:00:00 00:02:00 foo.example.org 92ed150794387c03ce684574b1139a6594a34f895daaaf09fd8ea90a27cddb12 778827225BA7DE539C5A7CFA59CFF7CDBD9A5920 No 2019-01-03T00:00:00 No Yes 00:00:00 00:00:01 fping -q -- %(host)s 00:15:00 2019-02-04T00:00:00 0
870
barbar Yes 00:05:00 2019-02-04T00:00:00 2019-01-03T00:00:00 00:02:00 192.0.2.3 0558568eedd67d622f5c83b35a115f796ab612cff5ad227247e46c2b020f441c 3E393AEAEFB84C7E89E2F547B3A107558FCA3A27 Yes 2019-01-04T00:00:00 No No 2019-01-03T00:00:00 00:00:30 00:00:01 : 00:15:00 2019-02-05T00:00:00 -2
872
self.assertEqual(output, expected_output)
873
def test_one_client(self):
874
output = PrintTableCmd().output(self.one_client)
875
expected_output = """
876
Name Enabled Timeout Last Successful Check
877
foo Yes 00:05:00 2019-02-03T00:00:00
879
self.assertEqual(output, expected_output)
881
class TestDumpJSONCmd(TestCmd):
883
self.expected_json = {
886
"KeyID": ("92ed150794387c03ce684574b1139a65"
887
"94a34f895daaaf09fd8ea90a27cddb12"),
888
"Host": "foo.example.org",
891
"LastCheckedOK": "2019-02-03T00:00:00",
892
"Created": "2019-01-02T00:00:00",
894
"Fingerprint": ("778827225BA7DE539C5A"
895
"7CFA59CFF7CDBD9A5920"),
896
"CheckerRunning": False,
897
"LastEnabled": "2019-01-03T00:00:00",
898
"ApprovalPending": False,
899
"ApprovedByDefault": True,
900
"LastApprovalRequest": "",
902
"ApprovalDuration": 1000,
903
"Checker": "fping -q -- %(host)s",
904
"ExtendedTimeout": 900000,
905
"Expires": "2019-02-04T00:00:00",
906
"LastCheckerStatus": 0,
910
"KeyID": ("0558568eedd67d622f5c83b35a115f79"
911
"6ab612cff5ad227247e46c2b020f441c"),
915
"LastCheckedOK": "2019-02-04T00:00:00",
916
"Created": "2019-01-03T00:00:00",
918
"Fingerprint": ("3E393AEAEFB84C7E89E2"
919
"F547B3A107558FCA3A27"),
920
"CheckerRunning": True,
921
"LastEnabled": "2019-01-04T00:00:00",
922
"ApprovalPending": False,
923
"ApprovedByDefault": False,
924
"LastApprovalRequest": "2019-01-03T00:00:00",
925
"ApprovalDelay": 30000,
926
"ApprovalDuration": 1000,
928
"ExtendedTimeout": 900000,
929
"Expires": "2019-02-05T00:00:00",
930
"LastCheckerStatus": -2,
933
return super(TestDumpJSONCmd, self).setUp()
934
def test_normal(self):
935
json_data = json.loads(DumpJSONCmd().output(self.clients))
936
self.assertDictEqual(json_data, self.expected_json)
937
def test_one_client(self):
938
clients = self.one_client
939
json_data = json.loads(DumpJSONCmd().output(clients))
940
expected_json = {"foo": self.expected_json["foo"]}
941
self.assertDictEqual(json_data, expected_json)
943
class TestIsEnabledCmd(TestCmd):
944
def test_is_enabled(self):
945
self.assertTrue(all(IsEnabledCmd().is_enabled(client, properties)
946
for client, properties in self.clients.items()))
947
def test_is_enabled_run_exits_successfully(self):
948
with self.assertRaises(SystemExit) as e:
949
IsEnabledCmd().run(None, self.one_client)
950
if e.exception.code is not None:
951
self.assertEqual(e.exception.code, 0)
953
self.assertIsNone(e.exception.code)
954
def test_is_enabled_run_exits_with_failure(self):
955
self.client.attributes["Enabled"] = dbus.Boolean(False)
956
with self.assertRaises(SystemExit) as e:
957
IsEnabledCmd().run(None, self.one_client)
958
if isinstance(e.exception.code, int):
959
self.assertNotEqual(e.exception.code, 0)
961
self.assertIsNotNone(e.exception.code)
963
class TestRemoveCmd(TestCmd):
964
def test_remove(self):
965
class MockMandos(object):
968
def RemoveClient(self, dbus_path):
969
self.calls.append(("RemoveClient", (dbus_path,)))
970
mandos = MockMandos()
971
super(TestRemoveCmd, self).setUp()
972
RemoveCmd().run(mandos, self.clients)
973
self.assertEqual(len(mandos.calls), 2)
974
for client in self.clients:
975
self.assertIn(("RemoveClient",
976
(client.__dbus_object_path__,)),
979
class TestApproveCmd(TestCmd):
980
def test_approve(self):
981
ApproveCmd().run(None, self.clients)
982
for client in self.clients:
983
self.assertIn(("Approve", (True, client_interface)),
986
class TestDenyCmd(TestCmd):
988
DenyCmd().run(None, self.clients)
989
for client in self.clients:
990
self.assertIn(("Approve", (False, client_interface)),
995
def should_only_run_tests():
996
parser = argparse.ArgumentParser(add_help=False)
997
parser.add_argument("--check", action='store_true')
998
args, unknown_args = parser.parse_known_args()
999
run_tests = args.check
1001
# Remove --check argument from sys.argv
1002
sys.argv[1:] = unknown_args
1005
# Add all tests from doctest strings
1006
def load_tests(loader, tests, none):
1008
tests.addTests(doctest.DocTestSuite())
489
1011
if __name__ == "__main__":
1012
if should_only_run_tests():
1013
# Call using ./tdd-python-script --check [--verbose]