273
## Classes for commands.
275
# Abstract classes first
276
class Command(object):
277
"""Abstract class for commands"""
278
def run(self, mandos, clients):
279
"""Normal commands should implement run_on_one_client(), but
280
commands which want to operate on all clients at the same time
281
can override this run() method instead."""
283
for client, properties in clients.items():
284
self.run_on_one_client(client, properties)
286
class PrintCmd(Command):
287
"""Abstract class for commands printing client details"""
288
all_keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
289
"Created", "Interval", "Host", "KeyID",
290
"Fingerprint", "CheckerRunning", "LastEnabled",
291
"ApprovalPending", "ApprovedByDefault",
292
"LastApprovalRequest", "ApprovalDelay",
293
"ApprovalDuration", "Checker", "ExtendedTimeout",
294
"Expires", "LastCheckerStatus")
295
def run(self, mandos, clients):
296
print(self.output(clients))
298
class PropertyCmd(Command):
299
"""Abstract class for Actions for setting one client property"""
300
def run_on_one_client(self, client, properties):
301
"""Set the Client's D-Bus property"""
302
client.Set(client_interface, self.property, self.value_to_set,
303
dbus_interface=dbus.PROPERTIES_IFACE)
305
class ValueArgumentMixIn(object):
306
"""Mixin class for commands taking a value as argument"""
307
def __init__(self, value):
308
self.value_to_set = value
310
class MillisecondsValueArgumentMixIn(ValueArgumentMixIn):
311
"""Mixin class for commands taking a value argument as
314
def value_to_set(self):
317
def value_to_set(self, value):
318
"""When setting, convert value to a datetime.timedelta"""
319
self._vts = int(round(value.total_seconds() * 1000))
321
# Actual (non-abstract) command classes
323
class PrintTableCmd(PrintCmd):
324
def __init__(self, verbose=False):
325
self.verbose = verbose
327
def output(self, clients):
328
default_keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK")
329
keywords = default_keywords
331
keywords = self.all_keywords
332
return str(self.TableOfClients(clients.values(), keywords))
334
class TableOfClients(object):
337
"Enabled": "Enabled",
338
"Timeout": "Timeout",
339
"LastCheckedOK": "Last Successful Check",
340
"LastApprovalRequest": "Last Approval Request",
341
"Created": "Created",
342
"Interval": "Interval",
344
"Fingerprint": "Fingerprint",
346
"CheckerRunning": "Check Is Running",
347
"LastEnabled": "Last Enabled",
348
"ApprovalPending": "Approval Is Pending",
349
"ApprovedByDefault": "Approved By Default",
350
"ApprovalDelay": "Approval Delay",
351
"ApprovalDuration": "Approval Duration",
352
"Checker": "Checker",
353
"ExtendedTimeout": "Extended Timeout",
354
"Expires": "Expires",
355
"LastCheckerStatus": "Last Checker Status",
358
def __init__(self, clients, keywords, tableheaders=None):
359
self.clients = clients
360
self.keywords = keywords
361
if tableheaders is not None:
362
self.tableheaders = tableheaders
365
return "\n".join(self.rows())
367
if sys.version_info.major == 2:
368
__unicode__ = __str__
370
return str(self).encode(locale.getpreferredencoding())
373
format_string = self.row_formatting_string()
374
rows = [self.header_line(format_string)]
375
rows.extend(self.client_line(client, format_string)
376
for client in self.clients)
379
def row_formatting_string(self):
380
"Format string used to format table rows"
381
return " ".join("{{{key}:{width}}}".format(
382
width=max(len(self.tableheaders[key]),
383
*(len(self.string_from_client(client, key))
384
for client in self.clients)),
386
for key in self.keywords)
388
def string_from_client(self, client, key):
389
return self.valuetostring(client[key], key)
392
def valuetostring(value, keyword):
393
if isinstance(value, dbus.Boolean):
394
return "Yes" if value else "No"
395
if keyword in ("Timeout", "Interval", "ApprovalDelay",
396
"ApprovalDuration", "ExtendedTimeout"):
397
return milliseconds_to_string(value)
400
def header_line(self, format_string):
401
return format_string.format(**self.tableheaders)
403
def client_line(self, client, format_string):
404
return format_string.format(
405
**{key: self.string_from_client(client, key)
406
for key in self.keywords})
410
class DumpJSONCmd(PrintCmd):
411
def output(self, clients):
412
data = {client["Name"]:
413
{key: self.dbus_boolean_to_bool(client[key])
414
for key in self.all_keywords}
415
for client in clients.values()}
416
return json.dumps(data, indent=4, separators=(',', ': '))
418
def dbus_boolean_to_bool(value):
419
if isinstance(value, dbus.Boolean):
423
class IsEnabledCmd(Command):
424
def run_on_one_client(self, client, properties):
425
if self.is_enabled(client, properties):
428
def is_enabled(self, client, properties):
429
return bool(properties["Enabled"])
431
class RemoveCmd(Command):
432
def run_on_one_client(self, client, properties):
433
self.mandos.RemoveClient(client.__dbus_object_path__)
435
class ApproveCmd(Command):
436
def run_on_one_client(self, client, properties):
437
client.Approve(dbus.Boolean(True),
438
dbus_interface=client_interface)
440
class DenyCmd(Command):
441
def run_on_one_client(self, client, properties):
442
client.Approve(dbus.Boolean(False),
443
dbus_interface=client_interface)
445
class EnableCmd(PropertyCmd):
447
value_to_set = dbus.Boolean(True)
449
class DisableCmd(PropertyCmd):
451
value_to_set = dbus.Boolean(False)
453
class BumpTimeoutCmd(PropertyCmd):
454
property = "LastCheckedOK"
457
class StartCheckerCmd(PropertyCmd):
458
property = "CheckerRunning"
459
value_to_set = dbus.Boolean(True)
461
class StopCheckerCmd(PropertyCmd):
462
property = "CheckerRunning"
463
value_to_set = dbus.Boolean(False)
465
class ApproveByDefaultCmd(PropertyCmd):
466
property = "ApprovedByDefault"
467
value_to_set = dbus.Boolean(True)
469
class DenyByDefaultCmd(PropertyCmd):
470
property = "ApprovedByDefault"
471
value_to_set = dbus.Boolean(False)
473
class SetCheckerCmd(PropertyCmd, ValueArgumentMixIn):
476
class SetHostCmd(PropertyCmd, ValueArgumentMixIn):
479
class SetSecretCmd(PropertyCmd, ValueArgumentMixIn):
481
def value_to_set(self):
484
def value_to_set(self, value):
485
"""When setting, read data from supplied file object"""
486
self._vts = value.read()
490
class SetTimeoutCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
493
class SetExtendedTimeoutCmd(PropertyCmd,
494
MillisecondsValueArgumentMixIn):
495
property = "ExtendedTimeout"
497
class SetIntervalCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
498
property = "Interval"
500
class SetApprovalDelayCmd(PropertyCmd,
501
MillisecondsValueArgumentMixIn):
502
property = "ApprovalDelay"
504
class SetApprovalDurationCmd(PropertyCmd,
505
MillisecondsValueArgumentMixIn):
506
property = "ApprovalDuration"
508
def add_command_line_options(parser):
509
parser.add_argument("--version", action="version",
510
version="%(prog)s {}".format(version),
511
help="show version number and exit")
512
parser.add_argument("-a", "--all", action="store_true",
513
help="Select all clients")
514
parser.add_argument("-v", "--verbose", action="store_true",
515
help="Print all fields")
516
parser.add_argument("-j", "--dump-json", action="store_true",
517
help="Dump client data in JSON format")
518
enable_disable = parser.add_mutually_exclusive_group()
519
enable_disable.add_argument("-e", "--enable", action="store_true",
520
help="Enable client")
521
enable_disable.add_argument("-d", "--disable",
523
help="disable client")
524
parser.add_argument("-b", "--bump-timeout", action="store_true",
525
help="Bump timeout for client")
526
start_stop_checker = parser.add_mutually_exclusive_group()
527
start_stop_checker.add_argument("--start-checker",
529
help="Start checker for client")
530
start_stop_checker.add_argument("--stop-checker",
532
help="Stop checker for client")
533
parser.add_argument("-V", "--is-enabled", action="store_true",
534
help="Check if client is enabled")
535
parser.add_argument("-r", "--remove", action="store_true",
536
help="Remove client")
537
parser.add_argument("-c", "--checker",
538
help="Set checker command for client")
539
parser.add_argument("-t", "--timeout", type=string_to_delta,
540
help="Set timeout for client")
541
parser.add_argument("--extended-timeout", type=string_to_delta,
542
help="Set extended timeout for client")
543
parser.add_argument("-i", "--interval", type=string_to_delta,
544
help="Set checker interval for client")
545
approve_deny_default = parser.add_mutually_exclusive_group()
546
approve_deny_default.add_argument(
547
"--approve-by-default", action="store_true",
548
default=None, dest="approved_by_default",
549
help="Set client to be approved by default")
550
approve_deny_default.add_argument(
551
"--deny-by-default", action="store_false",
552
dest="approved_by_default",
553
help="Set client to be denied by default")
554
parser.add_argument("--approval-delay", type=string_to_delta,
555
help="Set delay before client approve/deny")
556
parser.add_argument("--approval-duration", type=string_to_delta,
557
help="Set duration of one client approval")
558
parser.add_argument("-H", "--host", help="Set host for client")
559
parser.add_argument("-s", "--secret",
560
type=argparse.FileType(mode="rb"),
561
help="Set password blob (file) for client")
562
approve_deny = parser.add_mutually_exclusive_group()
563
approve_deny.add_argument(
564
"-A", "--approve", action="store_true",
565
help="Approve any current client request")
566
approve_deny.add_argument("-D", "--deny", action="store_true",
567
help="Deny any current client request")
568
parser.add_argument("--check", action="store_true",
569
help="Run self-test")
570
parser.add_argument("client", nargs="*", help="Client name")
573
def commands_from_options(options):
577
if options.dump_json:
578
commands.append(DumpJSONCmd())
581
commands.append(EnableCmd())
584
commands.append(DisableCmd())
586
if options.bump_timeout:
587
commands.append(BumpTimeoutCmd())
589
if options.start_checker:
590
commands.append(StartCheckerCmd())
592
if options.stop_checker:
593
commands.append(StopCheckerCmd())
595
if options.is_enabled:
596
commands.append(IsEnabledCmd())
599
commands.append(RemoveCmd())
601
if options.checker is not None:
602
commands.append(SetCheckerCmd(options.checker))
604
if options.timeout is not None:
605
commands.append(SetTimeoutCmd(options.timeout))
607
if options.extended_timeout:
609
SetExtendedTimeoutCmd(options.extended_timeout))
611
if options.interval is not None:
612
commands.append(SetIntervalCmd(options.interval))
614
if options.approved_by_default is not None:
615
if options.approved_by_default:
616
commands.append(ApproveByDefaultCmd())
618
commands.append(DenyByDefaultCmd())
620
if options.approval_delay is not None:
621
commands.append(SetApprovalDelayCmd(options.approval_delay))
623
if options.approval_duration is not None:
625
SetApprovalDurationCmd(options.approval_duration))
627
if options.host is not None:
628
commands.append(SetHostCmd(options.host))
630
if options.secret is not None:
631
commands.append(SetSecretCmd(options.secret))
634
commands.append(ApproveCmd())
637
commands.append(DenyCmd())
639
# If no command option has been given, show table of clients,
640
# optionally verbosely
642
commands.append(PrintTableCmd(verbose=options.verbose))
441
647
def check_option_syntax(parser, options):
442
"""Apply additional restrictions on options, not expressible in
445
def has_commands(options, commands=None):
447
commands = (command.Enable,
450
command.StartChecker,
456
command.SetExtendedTimeout,
458
command.ApproveByDefault,
459
command.DenyByDefault,
460
command.SetApprovalDelay,
461
command.SetApprovalDuration,
466
return any(isinstance(cmd, commands)
467
for cmd in options.commands)
469
if has_commands(options) and not (options.client or options.all):
649
def has_actions(options):
650
return any((options.enable,
652
options.bump_timeout,
653
options.start_checker,
654
options.stop_checker,
657
options.checker is not None,
658
options.timeout is not None,
659
options.extended_timeout is not None,
660
options.interval is not None,
661
options.approved_by_default is not None,
662
options.approval_delay is not None,
663
options.approval_duration is not None,
664
options.host is not None,
665
options.secret is not None,
669
if has_actions(options) and not (options.client or options.all):
470
670
parser.error("Options require clients names or --all.")
471
if options.verbose and has_commands(options):
671
if options.verbose and has_actions(options):
472
672
parser.error("--verbose can only be used alone.")
473
if (has_commands(options, (command.DumpJSON,))
474
and (options.verbose or len(options.commands) > 1)):
673
if options.dump_json and (options.verbose
674
or has_actions(options)):
475
675
parser.error("--dump-json can only be used alone.")
476
if options.all and not has_commands(options):
676
if options.all and not has_actions(options):
477
677
parser.error("--all requires an action.")
478
if (has_commands(options, (command.IsEnabled,))
479
and len(options.client) > 1):
678
if options.is_enabled and len(options.client) > 1:
480
679
parser.error("--is-enabled requires exactly one client")
481
if (len(options.commands) > 1
482
and has_commands(options, (command.Remove,))
483
and not has_commands(options, (command.Deny,))):
484
parser.error("--remove can only be combined with --deny")
491
object_manager_iface = "org.freedesktop.DBus.ObjectManager"
493
def get_managed_objects(self, busname, objectpath):
494
return self.call_method("GetManagedObjects", busname,
496
self.object_manager_iface)
498
properties_iface = "org.freedesktop.DBus.Properties"
500
def set_property(self, busname, objectpath, interface, key,
502
self.call_method("Set", busname, objectpath,
503
self.properties_iface, interface, key,
506
def call_method(self, methodname, busname, objectpath,
508
raise NotImplementedError()
510
class MandosBus(SystemBus):
511
busname_domain = "se.recompile"
512
busname = busname_domain + ".Mandos"
514
server_interface = busname_domain + ".Mandos"
515
client_interface = busname_domain + ".Mandos.Client"
518
def get_clients_and_properties(self):
519
managed_objects = self.get_managed_objects(
520
self.busname, self.server_path)
521
return {objpath: properties[self.client_interface]
522
for objpath, properties in managed_objects.items()
523
if self.client_interface in properties}
525
def set_client_property(self, objectpath, key, value):
526
return self.set_property(self.busname, objectpath,
527
self.client_interface, key,
530
def call_client_method(self, objectpath, method, *args):
531
return self.call_method(method, self.busname, objectpath,
532
self.client_interface, *args)
534
def call_server_method(self, method, *args):
535
return self.call_method(method, self.busname,
537
self.server_interface, *args)
539
class Error(Exception):
542
class ConnectFailed(Error):
546
class dbus_python_adapter:
548
class SystemBus(dbus.MandosBus):
549
"""Use dbus-python"""
551
def __init__(self, module=dbus_python):
552
self.dbus_python = module
553
self.bus = self.dbus_python.SystemBus()
555
@contextlib.contextmanager
556
def convert_exception(self, exception_class=dbus.Error):
559
except self.dbus_python.exceptions.DBusException as e:
560
# This does what "raise from" would do
561
exc = exception_class(*e.args)
565
def call_method(self, methodname, busname, objectpath,
567
proxy_object = self.get_object(busname, objectpath)
568
log.debug("D-Bus: %s:%s:%s.%s(%s)", busname, objectpath,
569
interface, methodname,
570
", ".join(repr(a) for a in args))
571
method = getattr(proxy_object, methodname)
572
with self.convert_exception():
573
with dbus_python_adapter.SilenceLogger(
575
value = method(*args, dbus_interface=interface)
576
return self.type_filter(value)
578
def get_object(self, busname, objectpath):
579
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
581
with self.convert_exception(dbus.ConnectFailed):
582
return self.bus.get_object(busname, objectpath)
584
def type_filter(self, value):
585
"""Convert the most bothersome types to Python types"""
586
if isinstance(value, self.dbus_python.Boolean):
588
if isinstance(value, self.dbus_python.ObjectPath):
590
# Also recurse into dictionaries
591
if isinstance(value, self.dbus_python.Dictionary):
592
return {self.type_filter(key):
593
self.type_filter(subval)
594
for key, subval in value.items()}
597
def set_client_property(self, objectpath, key, value):
599
if not isinstance(value, bytes):
600
value = value.encode("utf-8")
601
value = self.dbus_python.ByteArray(value)
602
return self.set_property(self.busname, objectpath,
603
self.client_interface, key,
607
"Simple context manager to silence a particular logger"
609
def __init__(self, loggername):
610
self.logger = logging.getLogger(loggername)
613
self.logger.addFilter(self.nullfilter)
615
class NullFilter(logging.Filter):
616
def filter(self, record):
619
nullfilter = NullFilter()
621
def __exit__(self, exc_type, exc_val, exc_tb):
622
self.logger.removeFilter(self.nullfilter)
624
class CachingBus(SystemBus):
625
"""A caching layer for dbus_python_adapter.SystemBus"""
627
def __init__(self, *args, **kwargs):
628
self.object_cache = {}
629
super(dbus_python_adapter.CachingBus,
630
self).__init__(*args, **kwargs)
632
def get_object(self, busname, objectpath):
634
return self.object_cache[(busname, objectpath)]
637
dbus_python_adapter.CachingBus,
638
self).get_object(busname, objectpath)
639
self.object_cache[(busname, objectpath)] = new_object
643
class pydbus_adapter:
644
class SystemBus(dbus.MandosBus):
645
def __init__(self, module=pydbus):
647
self.bus = self.pydbus.SystemBus()
649
@contextlib.contextmanager
650
def convert_exception(self, exception_class=dbus.Error):
653
except gi.repository.GLib.Error as e:
654
# This does what "raise from" would do
655
exc = exception_class(*e.args)
659
def call_method(self, methodname, busname, objectpath,
661
proxy_object = self.get(busname, objectpath)
662
log.debug("D-Bus: %s:%s:%s.%s(%s)", busname, objectpath,
663
interface, methodname,
664
", ".join(repr(a) for a in args))
665
method = getattr(proxy_object[interface], methodname)
666
with self.convert_exception():
669
def get(self, busname, objectpath):
670
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
672
with self.convert_exception(dbus.ConnectFailed):
673
if sys.version_info.major <= 2:
674
with warnings.catch_warnings():
675
warnings.filterwarnings(
676
"ignore", "", DeprecationWarning,
677
r"^xml\.etree\.ElementTree$")
678
return self.bus.get(busname, objectpath)
680
return self.bus.get(busname, objectpath)
682
def set_property(self, busname, objectpath, interface, key,
684
proxy_object = self.get(busname, objectpath)
685
log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", busname,
686
objectpath, self.properties_iface, interface,
688
setattr(proxy_object[interface], key, value)
690
class CachingBus(SystemBus):
691
"""A caching layer for pydbus_adapter.SystemBus"""
693
def __init__(self, *args, **kwargs):
694
self.object_cache = {}
695
super(pydbus_adapter.CachingBus,
696
self).__init__(*args, **kwargs)
698
def get(self, busname, objectpath):
700
return self.object_cache[(busname, objectpath)]
702
new_object = (super(pydbus_adapter.CachingBus, self)
703
.get(busname, objectpath))
704
self.object_cache[(busname, objectpath)] = new_object
708
class dbussy_adapter:
709
class SystemBus(dbus.SystemBus):
712
def __init__(self, dbussy, ravel):
715
self.bus = ravel.system_bus()
717
@contextlib.contextmanager
718
def convert_exception(self, exception_class=dbus.Error):
721
except self.dbussy.DBusError as e:
722
# This does what "raise from" would do
723
exc = exception_class(*e.args)
727
def call_method(self, methodname, busname, objectpath,
729
proxy_object = self.get_object(busname, objectpath)
730
log.debug("D-Bus: %s:%s:%s.%s(%s)", busname, objectpath,
731
interface, methodname,
732
", ".join(repr(a) for a in args))
733
iface = proxy_object.get_interface(interface)
734
method = getattr(iface, methodname)
735
with self.convert_exception(dbus.Error):
736
value = method(*args)
737
# DBussy returns values either as an empty list or as a
738
# list of one element with the return value
740
return self.type_filter(value[0])
742
def get_object(self, busname, objectpath):
743
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
745
with self.convert_exception(dbus.ConnectFailed):
746
return self.bus[busname][objectpath]
748
def type_filter(self, value):
749
"""Convert the most bothersome types to Python types"""
750
# A D-Bus Variant value is represented as the Python type
751
# Tuple[dbussy.DBUS.Signature, Any]
752
if isinstance(value, tuple):
754
and isinstance(value[0],
755
self.dbussy.DBUS.Signature)):
756
return self.type_filter(value[1])
757
elif isinstance(value, self.dbussy.DBUS.ObjectPath):
759
# Also recurse into dictionaries
760
elif isinstance(value, dict):
761
return {self.type_filter(key):
762
self.type_filter(subval)
763
for key, subval in value.items()}
766
def set_property(self, busname, objectpath, interface, key,
768
proxy_object = self.get_object(busname, objectpath)
769
log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", busname,
770
objectpath, self.properties_iface, interface,
773
# DBussy wants a Byte Array to be a sequence of
774
# values, not a byte string
776
setattr(proxy_object.get_interface(interface), key, value)
778
class MandosBus(SystemBus, dbus.MandosBus):
781
class CachingBus(MandosBus):
782
"""A caching layer for dbussy_adapter.MandosBus"""
784
def __init__(self, *args, **kwargs):
785
self.object_cache = {}
786
super(dbussy_adapter.CachingBus, self).__init__(*args,
789
def get_object(self, busname, objectpath):
791
return self.object_cache[(busname, objectpath)]
794
dbussy_adapter.CachingBus,
795
self).get_object(busname, objectpath)
796
self.object_cache[(busname, objectpath)] = new_object
800
def commands_from_options(options):
802
commands = list(options.commands)
804
def find_cmd(cmd, commands):
806
for i, c in enumerate(commands):
807
if isinstance(c, cmd):
811
# If command.Remove is present, move any instances of command.Deny
812
# to occur ahead of command.Remove.
813
index_of_remove = find_cmd(command.Remove, commands)
814
before_remove = commands[:index_of_remove]
815
after_remove = commands[index_of_remove:]
817
for cmd in after_remove:
818
if isinstance(cmd, command.Deny):
819
before_remove.append(cmd)
821
cleaned_after.append(cmd)
822
if cleaned_after != after_remove:
823
commands = before_remove + cleaned_after
825
# If no command option has been given, show table of clients,
826
# optionally verbosely
828
commands.append(command.PrintTable(verbose=options.verbose))
834
"""A namespace for command classes"""
837
"""Abstract base class for commands"""
839
def run(self, clients, bus=None):
840
"""Normal commands should implement run_on_one_client(),
841
but commands which want to operate on all clients at the same time can
842
override this run() method instead.
845
for client, properties in clients.items():
846
self.run_on_one_client(client, properties)
848
class IsEnabled(Base):
849
def run(self, clients, bus=None):
850
properties = next(iter(clients.values()))
851
if properties["Enabled"]:
856
def run_on_one_client(self, client, properties):
857
self.bus.call_client_method(client, "Approve", True)
860
def run_on_one_client(self, client, properties):
861
self.bus.call_client_method(client, "Approve", False)
864
def run(self, clients, bus):
865
for clientpath in frozenset(clients.keys()):
866
bus.call_server_method("RemoveClient", clientpath)
869
"""Abstract class for commands outputting client details"""
870
all_keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
871
"Created", "Interval", "Host", "KeyID",
872
"Fingerprint", "CheckerRunning",
873
"LastEnabled", "ApprovalPending",
874
"ApprovedByDefault", "LastApprovalRequest",
875
"ApprovalDelay", "ApprovalDuration",
876
"Checker", "ExtendedTimeout", "Expires",
879
class DumpJSON(Output):
880
def run(self, clients, bus=None):
881
data = {properties["Name"]:
882
{key: properties[key]
883
for key in self.all_keywords}
884
for properties in clients.values()}
885
print(json.dumps(data, indent=4, separators=(",", ": ")))
887
class PrintTable(Output):
888
def __init__(self, verbose=False):
889
self.verbose = verbose
891
def run(self, clients, bus=None):
892
default_keywords = ("Name", "Enabled", "Timeout",
894
keywords = default_keywords
896
keywords = self.all_keywords
897
print(self.TableOfClients(clients.values(), keywords))
899
class TableOfClients:
902
"Enabled": "Enabled",
903
"Timeout": "Timeout",
904
"LastCheckedOK": "Last Successful Check",
905
"LastApprovalRequest": "Last Approval Request",
906
"Created": "Created",
907
"Interval": "Interval",
909
"Fingerprint": "Fingerprint",
911
"CheckerRunning": "Check Is Running",
912
"LastEnabled": "Last Enabled",
913
"ApprovalPending": "Approval Is Pending",
914
"ApprovedByDefault": "Approved By Default",
915
"ApprovalDelay": "Approval Delay",
916
"ApprovalDuration": "Approval Duration",
917
"Checker": "Checker",
918
"ExtendedTimeout": "Extended Timeout",
919
"Expires": "Expires",
920
"LastCheckerStatus": "Last Checker Status",
923
def __init__(self, clients, keywords):
924
self.clients = clients
925
self.keywords = keywords
928
return "\n".join(self.rows())
930
if sys.version_info.major == 2:
931
__unicode__ = __str__
934
return str(self).encode(
935
locale.getpreferredencoding())
938
format_string = self.row_formatting_string()
939
rows = [self.header_line(format_string)]
940
rows.extend(self.client_line(client, format_string)
941
for client in self.clients)
944
def row_formatting_string(self):
945
"Format string used to format table rows"
946
return " ".join("{{{key}:{width}}}".format(
947
width=max(len(self.tableheaders[key]),
948
*(len(self.string_from_client(client,
950
for client in self.clients)),
952
for key in self.keywords)
954
def string_from_client(self, client, key):
955
return self.valuetostring(client[key], key)
958
def valuetostring(cls, value, keyword):
959
if isinstance(value, bool):
960
return "Yes" if value else "No"
961
if keyword in ("Timeout", "Interval", "ApprovalDelay",
962
"ApprovalDuration", "ExtendedTimeout"):
963
return cls.milliseconds_to_string(value)
966
def header_line(self, format_string):
967
return format_string.format(**self.tableheaders)
969
def client_line(self, client, format_string):
970
return format_string.format(
971
**{key: self.string_from_client(client, key)
972
for key in self.keywords})
975
def milliseconds_to_string(ms):
976
td = datetime.timedelta(0, 0, 0, ms)
977
return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
978
.format(days="{}T".format(td.days)
980
hours=td.seconds // 3600,
981
minutes=(td.seconds % 3600) // 60,
982
seconds=td.seconds % 60))
984
class PropertySetter(Base):
985
"Abstract class for Actions for setting one client property"
987
def run_on_one_client(self, client, properties=None):
988
"""Set the Client's D-Bus property"""
989
self.bus.set_client_property(client, self.propname,
994
raise NotImplementedError()
996
class Enable(PropertySetter):
1000
class Disable(PropertySetter):
1001
propname = "Enabled"
1002
value_to_set = False
1004
class BumpTimeout(PropertySetter):
1005
propname = "LastCheckedOK"
1008
class StartChecker(PropertySetter):
1009
propname = "CheckerRunning"
1012
class StopChecker(PropertySetter):
1013
propname = "CheckerRunning"
1014
value_to_set = False
1016
class ApproveByDefault(PropertySetter):
1017
propname = "ApprovedByDefault"
1020
class DenyByDefault(PropertySetter):
1021
propname = "ApprovedByDefault"
1022
value_to_set = False
1024
class PropertySetterValue(PropertySetter):
1025
"""Abstract class for PropertySetter recieving a value as
1026
constructor argument instead of a class attribute."""
1028
def __init__(self, value):
1029
self.value_to_set = value
1032
def argparse(cls, argtype):
1034
return cls(argtype(arg))
1037
class SetChecker(PropertySetterValue):
1038
propname = "Checker"
1040
class SetHost(PropertySetterValue):
1043
class SetSecret(PropertySetterValue):
1047
def value_to_set(self):
1050
@value_to_set.setter
1051
def value_to_set(self, value):
1052
"""When setting, read data from supplied file object"""
1053
self._vts = value.read()
1056
class PropertySetterValueMilliseconds(PropertySetterValue):
1057
"""Abstract class for PropertySetterValue taking a value
1058
argument as a datetime.timedelta() but should store it as
1062
def value_to_set(self):
1065
@value_to_set.setter
1066
def value_to_set(self, value):
1067
"When setting, convert value from a datetime.timedelta"
1068
self._vts = int(round(value.total_seconds() * 1000))
1070
class SetTimeout(PropertySetterValueMilliseconds):
1071
propname = "Timeout"
1073
class SetExtendedTimeout(PropertySetterValueMilliseconds):
1074
propname = "ExtendedTimeout"
1076
class SetInterval(PropertySetterValueMilliseconds):
1077
propname = "Interval"
1079
class SetApprovalDelay(PropertySetterValueMilliseconds):
1080
propname = "ApprovalDelay"
1082
class SetApprovalDuration(PropertySetterValueMilliseconds):
1083
propname = "ApprovalDuration"
683
parser = argparse.ArgumentParser()
685
add_command_line_options(parser)
687
options = parser.parse_args()
689
check_option_syntax(parser, options)
691
clientnames = options.client
694
bus = dbus.SystemBus()
695
mandos_dbus_objc = bus.get_object(busname, server_path)
696
except dbus.exceptions.DBusException:
697
log.critical("Could not connect to Mandos server")
700
mandos_serv = dbus.Interface(mandos_dbus_objc,
701
dbus_interface=server_interface)
702
mandos_serv_object_manager = dbus.Interface(
703
mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
705
# Filter out log message from dbus module
706
dbus_logger = logging.getLogger("dbus.proxies")
707
class NullFilter(logging.Filter):
708
def filter(self, record):
710
dbus_filter = NullFilter()
712
dbus_logger.addFilter(dbus_filter)
713
mandos_clients = {path: ifs_and_props[client_interface]
714
for path, ifs_and_props in
715
mandos_serv_object_manager
716
.GetManagedObjects().items()
717
if client_interface in ifs_and_props}
718
except dbus.exceptions.DBusException as e:
719
log.critical("Failed to access Mandos server through D-Bus:"
723
# restore dbus logger
724
dbus_logger.removeFilter(dbus_filter)
726
# Compile dict of (clients: properties) to process
730
clients = {bus.get_object(busname, path): properties
731
for path, properties in mandos_clients.items()}
733
for name in clientnames:
734
for path, client in mandos_clients.items():
735
if client["Name"] == name:
736
client_objc = bus.get_object(busname, path)
737
clients[client_objc] = client
740
log.critical("Client not found on server: %r", name)
743
# Run all commands on clients
744
commands = commands_from_options(options)
745
for command in commands:
746
command.run(mandos_serv, clients)
1087
class TestCaseWithAssertLogs(unittest.TestCase):
1088
"""unittest.TestCase.assertLogs only exists in Python 3.4"""
1090
if not hasattr(unittest.TestCase, "assertLogs"):
1091
@contextlib.contextmanager
1092
def assertLogs(self, logger, level=logging.INFO):
1093
capturing_handler = self.CapturingLevelHandler(level)
1094
old_level = logger.level
1095
old_propagate = logger.propagate
1096
logger.addHandler(capturing_handler)
1097
logger.setLevel(level)
1098
logger.propagate = False
1100
yield capturing_handler.watcher
1102
logger.propagate = old_propagate
1103
logger.removeHandler(capturing_handler)
1104
logger.setLevel(old_level)
1105
self.assertGreater(len(capturing_handler.watcher.records),
1108
class CapturingLevelHandler(logging.Handler):
1109
def __init__(self, level, *args, **kwargs):
1110
logging.Handler.__init__(self, *args, **kwargs)
1111
self.watcher = self.LoggingWatcher([], [])
1112
def emit(self, record):
1113
self.watcher.records.append(record)
1114
self.watcher.output.append(self.format(record))
1116
LoggingWatcher = collections.namedtuple("LoggingWatcher",
1122
"""Class for objects which exist only to be unique objects, since
1123
unittest.mock.sentinel only exists in Python 3.3"""
1126
class Test_string_to_delta(TestCaseWithAssertLogs):
1127
# Just test basic RFC 3339 functionality here, the doc string for
1128
# rfc3339_duration_to_delta() already has more comprehensive
1129
# tests, which are run by doctest.
1131
def test_rfc3339_zero_seconds(self):
1132
self.assertEqual(datetime.timedelta(),
1133
string_to_delta("PT0S"))
1135
def test_rfc3339_zero_days(self):
1136
self.assertEqual(datetime.timedelta(), string_to_delta("P0D"))
1138
def test_rfc3339_one_second(self):
1139
self.assertEqual(datetime.timedelta(0, 1),
1140
string_to_delta("PT1S"))
1142
def test_rfc3339_two_hours(self):
1143
self.assertEqual(datetime.timedelta(0, 7200),
1144
string_to_delta("PT2H"))
749
class Test_milliseconds_to_string(unittest.TestCase):
751
self.assertEqual(milliseconds_to_string(93785000),
753
def test_no_days(self):
754
self.assertEqual(milliseconds_to_string(7385000), "02:03:05")
755
def test_all_zero(self):
756
self.assertEqual(milliseconds_to_string(0), "00:00:00")
757
def test_no_fractional_seconds(self):
758
self.assertEqual(milliseconds_to_string(400), "00:00:00")
759
self.assertEqual(milliseconds_to_string(900), "00:00:00")
760
self.assertEqual(milliseconds_to_string(1900), "00:00:01")
762
class Test_string_to_delta(unittest.TestCase):
763
def test_handles_basic_rfc3339(self):
764
self.assertEqual(string_to_delta("PT0S"),
765
datetime.timedelta())
766
self.assertEqual(string_to_delta("P0D"),
767
datetime.timedelta())
768
self.assertEqual(string_to_delta("PT1S"),
769
datetime.timedelta(0, 1))
770
self.assertEqual(string_to_delta("PT2H"),
771
datetime.timedelta(0, 7200))
1146
772
def test_falls_back_to_pre_1_6_1_with_warning(self):
1147
with self.assertLogs(log, logging.WARNING):
1148
value = string_to_delta("2h")
1149
self.assertEqual(datetime.timedelta(0, 7200), value)
1152
class Test_check_option_syntax(unittest.TestCase):
1154
self.parser = argparse.ArgumentParser()
1155
add_command_line_options(self.parser)
1157
def test_actions_requires_client_or_all(self):
1158
for action, value in self.actions.items():
1159
args = self.actionargs(action, value)
1160
with self.assertParseError():
1161
self.parse_args(args)
1163
# This mostly corresponds to the definition from has_commands() in
1164
# check_option_syntax()
1168
"--bump-timeout": None,
1169
"--start-checker": None,
1170
"--stop-checker": None,
1171
"--is-enabled": None,
1174
"--timeout": "PT0S",
1175
"--extended-timeout": "PT0S",
1176
"--interval": "PT0S",
1177
"--approve-by-default": None,
1178
"--deny-by-default": None,
1179
"--approval-delay": "PT0S",
1180
"--approval-duration": "PT0S",
1181
"--host": "hostname",
1182
"--secret": "/dev/null",
1188
def actionargs(action, value, *args):
1189
if value is not None:
1190
return [action, value] + list(args)
773
# assertLogs only exists in Python 3.4
774
if hasattr(self, "assertLogs"):
775
with self.assertLogs(log, logging.WARNING):
776
value = string_to_delta("2h")
1192
return [action] + list(args)
1194
@contextlib.contextmanager
1195
def assertParseError(self):
1196
with self.assertRaises(SystemExit) as e:
1197
with self.redirect_stderr_to_devnull():
1199
# Exit code from argparse is guaranteed to be "2". Reference:
1200
# https://docs.python.org/3/library
1201
# /argparse.html#exiting-methods
1202
self.assertEqual(2, e.exception.code)
1204
def parse_args(self, args):
1205
options = self.parser.parse_args(args)
1206
check_option_syntax(self.parser, options)
1209
@contextlib.contextmanager
1210
def redirect_stderr_to_devnull():
1211
old_stderr = sys.stderr
1212
with contextlib.closing(open(os.devnull, "w")) as null:
778
class WarningFilter(logging.Filter):
779
"""Don't show, but record the presence of, warnings"""
780
def filter(self, record):
781
is_warning = record.levelno >= logging.WARNING
782
self.found = is_warning or getattr(self, "found",
784
return not is_warning
785
warning_filter = WarningFilter()
786
log.addFilter(warning_filter)
788
value = string_to_delta("2h")
1217
sys.stderr = old_stderr
1219
def check_option_syntax(self, options):
1220
check_option_syntax(self.parser, options)
1222
def test_actions_all_conflicts_with_verbose(self):
1223
for action, value in self.actions.items():
1224
args = self.actionargs(action, value, "--all",
1226
with self.assertParseError():
1227
self.parse_args(args)
1229
def test_actions_with_client_conflicts_with_verbose(self):
1230
for action, value in self.actions.items():
1231
args = self.actionargs(action, value, "--verbose",
1233
with self.assertParseError():
1234
self.parse_args(args)
1236
def test_dump_json_conflicts_with_verbose(self):
1237
args = ["--dump-json", "--verbose"]
1238
with self.assertParseError():
1239
self.parse_args(args)
1241
def test_dump_json_conflicts_with_action(self):
1242
for action, value in self.actions.items():
1243
args = self.actionargs(action, value, "--dump-json")
1244
with self.assertParseError():
1245
self.parse_args(args)
1247
def test_all_can_not_be_alone(self):
1249
with self.assertParseError():
1250
self.parse_args(args)
1252
def test_all_is_ok_with_any_action(self):
1253
for action, value in self.actions.items():
1254
args = self.actionargs(action, value, "--all")
1255
self.parse_args(args)
1257
def test_any_action_is_ok_with_one_client(self):
1258
for action, value in self.actions.items():
1259
args = self.actionargs(action, value, "client")
1260
self.parse_args(args)
1262
def test_one_client_with_all_actions_except_is_enabled(self):
1263
for action, value in self.actions.items():
1264
if action == "--is-enabled":
1266
args = self.actionargs(action, value, "client")
1267
self.parse_args(args)
1269
def test_two_clients_with_all_actions_except_is_enabled(self):
1270
for action, value in self.actions.items():
1271
if action == "--is-enabled":
1273
args = self.actionargs(action, value, "client1",
1275
self.parse_args(args)
1277
def test_two_clients_are_ok_with_actions_except_is_enabled(self):
1278
for action, value in self.actions.items():
1279
if action == "--is-enabled":
1281
args = self.actionargs(action, value, "client1",
1283
self.parse_args(args)
1285
def test_is_enabled_fails_without_client(self):
1286
args = ["--is-enabled"]
1287
with self.assertParseError():
1288
self.parse_args(args)
1290
def test_is_enabled_fails_with_two_clients(self):
1291
args = ["--is-enabled", "client1", "client2"]
1292
with self.assertParseError():
1293
self.parse_args(args)
1295
def test_remove_can_only_be_combined_with_action_deny(self):
1296
for action, value in self.actions.items():
1297
if action in {"--remove", "--deny"}:
1299
args = self.actionargs(action, value, "--all",
1301
with self.assertParseError():
1302
self.parse_args(args)
1305
class Test_dbus_exceptions(unittest.TestCase):
1307
def test_dbus_ConnectFailed_is_Error(self):
1308
with self.assertRaises(dbus.Error):
1309
raise dbus.ConnectFailed()
1312
class Test_dbus_MandosBus(unittest.TestCase):
1314
class MockMandosBus(dbus.MandosBus):
1316
self._name = "se.recompile.Mandos"
1317
self._server_path = "/"
1318
self._server_interface = "se.recompile.Mandos"
1319
self._client_interface = "se.recompile.Mandos.Client"
1321
self.call_method_return = Unique()
1323
def call_method(self, methodname, busname, objectpath,
1325
self.calls.append((methodname, busname, objectpath,
1327
return self.call_method_return
1330
self.bus = self.MockMandosBus()
1332
def test_set_client_property(self):
1333
self.bus.set_client_property("objectpath", "key", "value")
1334
expected_call = ("Set", self.bus._name, "objectpath",
1335
"org.freedesktop.DBus.Properties",
1336
(self.bus._client_interface, "key", "value"))
1337
self.assertIn(expected_call, self.bus.calls)
1339
def test_call_client_method(self):
1340
ret = self.bus.call_client_method("objectpath", "methodname")
1341
self.assertIs(self.bus.call_method_return, ret)
1342
expected_call = ("methodname", self.bus._name, "objectpath",
1343
self.bus._client_interface, ())
1344
self.assertIn(expected_call, self.bus.calls)
1346
def test_call_client_method_with_args(self):
1347
args = (Unique(), Unique())
1348
ret = self.bus.call_client_method("objectpath", "methodname",
1350
self.assertIs(self.bus.call_method_return, ret)
1351
expected_call = ("methodname", self.bus._name, "objectpath",
1352
self.bus._client_interface,
1354
self.assertIn(expected_call, self.bus.calls)
1356
def test_get_clients_and_properties(self):
1359
self.bus._client_interface: {
1363
"irrelevant_interface": {
1364
"key": "othervalue",
1368
"other_objectpath": {
1369
"other_irrelevant_interface": {
1375
expected_clients_and_properties = {
1381
self.bus.call_method_return = managed_objects
1382
ret = self.bus.get_clients_and_properties()
1383
self.assertDictEqual(expected_clients_and_properties, ret)
1384
expected_call = ("GetManagedObjects", self.bus._name,
1385
self.bus._server_path,
1386
"org.freedesktop.DBus.ObjectManager", ())
1387
self.assertIn(expected_call, self.bus.calls)
1389
def test_call_server_method(self):
1390
ret = self.bus.call_server_method("methodname")
1391
self.assertIs(self.bus.call_method_return, ret)
1392
expected_call = ("methodname", self.bus._name,
1393
self.bus._server_path,
1394
self.bus._server_interface, ())
1395
self.assertIn(expected_call, self.bus.calls)
1397
def test_call_server_method_with_args(self):
1398
args = (Unique(), Unique())
1399
ret = self.bus.call_server_method("methodname", *args)
1400
self.assertIs(self.bus.call_method_return, ret)
1401
expected_call = ("methodname", self.bus._name,
1402
self.bus._server_path,
1403
self.bus._server_interface,
1405
self.assertIn(expected_call, self.bus.calls)
1408
class Test_dbus_python_adapter_SystemBus(TestCaseWithAssertLogs):
1410
def MockDBusPython_func(self, func):
1411
class mock_dbus_python:
1412
"""mock dbus-python module"""
1414
"""Pseudo-namespace"""
1415
class DBusException(Exception):
1419
def get_object(busname, objectpath):
1420
DBusObject = collections.namedtuple(
1421
"DBusObject", ("methodname", "Set"))
1422
def method(*args, **kwargs):
1423
self.assertEqual({"dbus_interface":
1427
def set_property(interface, key, value,
1428
dbus_interface=None):
1430
"org.freedesktop.DBus.Properties",
1432
self.assertEqual("Secret", key)
1433
return func(interface, key, value,
1434
dbus_interface=dbus_interface)
1435
return DBusObject(methodname=method,
1438
def __init__(self, value):
1439
self.value = bool(value)
1442
if sys.version_info.major == 2:
1443
__nonzero__ = __bool__
1444
class ObjectPath(str):
1446
class Dictionary(dict):
1448
class ByteArray(bytes):
1450
return mock_dbus_python
1452
def call_method(self, bus, methodname, busname, objectpath,
1454
with self.assertLogs(log, logging.DEBUG):
1455
return bus.call_method(methodname, busname, objectpath,
1458
def test_call_method_returns(self):
1459
expected_method_return = Unique()
1460
method_args = (Unique(), Unique())
1462
self.assertEqual(len(method_args), len(args))
1463
for marg, arg in zip(method_args, args):
1464
self.assertIs(marg, arg)
1465
return expected_method_return
1466
mock_dbus_python = self.MockDBusPython_func(func)
1467
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1468
ret = self.call_method(bus, "methodname", "busname",
1469
"objectpath", "interface",
1471
self.assertIs(ret, expected_method_return)
1473
def test_call_method_filters_bool_true(self):
1475
return method_return
1476
mock_dbus_python = self.MockDBusPython_func(func)
1477
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1478
method_return = mock_dbus_python.Boolean(True)
1479
ret = self.call_method(bus, "methodname", "busname",
1480
"objectpath", "interface")
1481
self.assertTrue(ret)
1482
self.assertNotIsInstance(ret, mock_dbus_python.Boolean)
1484
def test_call_method_filters_bool_false(self):
1486
return method_return
1487
mock_dbus_python = self.MockDBusPython_func(func)
1488
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1489
method_return = mock_dbus_python.Boolean(False)
1490
ret = self.call_method(bus, "methodname", "busname",
1491
"objectpath", "interface")
1492
self.assertFalse(ret)
1493
self.assertNotIsInstance(ret, mock_dbus_python.Boolean)
1495
def test_call_method_filters_objectpath(self):
1497
return method_return
1498
mock_dbus_python = self.MockDBusPython_func(func)
1499
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1500
method_return = mock_dbus_python.ObjectPath("objectpath")
1501
ret = self.call_method(bus, "methodname", "busname",
1502
"objectpath", "interface")
1503
self.assertEqual("objectpath", ret)
1504
self.assertIsNot("objectpath", ret)
1505
self.assertNotIsInstance(ret, mock_dbus_python.ObjectPath)
1507
def test_call_method_filters_booleans_in_dict(self):
1509
return method_return
1510
mock_dbus_python = self.MockDBusPython_func(func)
1511
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1512
method_return = mock_dbus_python.Dictionary(
1513
{mock_dbus_python.Boolean(True):
1514
mock_dbus_python.Boolean(False),
1515
mock_dbus_python.Boolean(False):
1516
mock_dbus_python.Boolean(True)})
1517
ret = self.call_method(bus, "methodname", "busname",
1518
"objectpath", "interface")
1519
expected_method_return = {True: False,
1521
self.assertEqual(expected_method_return, ret)
1522
self.assertNotIsInstance(ret, mock_dbus_python.Dictionary)
1524
def test_call_method_filters_objectpaths_in_dict(self):
1526
return method_return
1527
mock_dbus_python = self.MockDBusPython_func(func)
1528
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1529
method_return = mock_dbus_python.Dictionary(
1530
{mock_dbus_python.ObjectPath("objectpath_key_1"):
1531
mock_dbus_python.ObjectPath("objectpath_value_1"),
1532
mock_dbus_python.ObjectPath("objectpath_key_2"):
1533
mock_dbus_python.ObjectPath("objectpath_value_2")})
1534
ret = self.call_method(bus, "methodname", "busname",
1535
"objectpath", "interface")
1536
expected_method_return = {str(key): str(value)
1538
method_return.items()}
1539
self.assertEqual(expected_method_return, ret)
1540
self.assertIsInstance(ret, dict)
1541
self.assertNotIsInstance(ret, mock_dbus_python.Dictionary)
1543
def test_call_method_filters_dict_in_dict(self):
1545
return method_return
1546
mock_dbus_python = self.MockDBusPython_func(func)
1547
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1548
method_return = mock_dbus_python.Dictionary(
1549
{"key1": mock_dbus_python.Dictionary({"key11": "value11",
1550
"key12": "value12"}),
1551
"key2": mock_dbus_python.Dictionary({"key21": "value21",
1552
"key22": "value22"})})
1553
ret = self.call_method(bus, "methodname", "busname",
1554
"objectpath", "interface")
1555
expected_method_return = {
1556
"key1": {"key11": "value11",
1557
"key12": "value12"},
1558
"key2": {"key21": "value21",
1559
"key22": "value22"},
1561
self.assertEqual(expected_method_return, ret)
1562
self.assertIsInstance(ret, dict)
1563
self.assertNotIsInstance(ret, mock_dbus_python.Dictionary)
1564
for key, value in ret.items():
1565
self.assertIsInstance(value, dict)
1566
self.assertEqual(expected_method_return[key], value)
1567
self.assertNotIsInstance(value,
1568
mock_dbus_python.Dictionary)
1570
def test_call_method_filters_dict_three_deep(self):
1572
return method_return
1573
mock_dbus_python = self.MockDBusPython_func(func)
1574
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1575
method_return = mock_dbus_python.Dictionary(
1577
mock_dbus_python.Dictionary(
1579
mock_dbus_python.Dictionary(
1581
mock_dbus_python.Boolean(True),
1585
ret = self.call_method(bus, "methodname", "busname",
1586
"objectpath", "interface")
1587
expected_method_return = {"key1": {"key2": {"key3": True}}}
1588
self.assertEqual(expected_method_return, ret)
1589
self.assertIsInstance(ret, dict)
1590
self.assertNotIsInstance(ret, mock_dbus_python.Dictionary)
1591
self.assertIsInstance(ret["key1"], dict)
1592
self.assertNotIsInstance(ret["key1"],
1593
mock_dbus_python.Dictionary)
1594
self.assertIsInstance(ret["key1"]["key2"], dict)
1595
self.assertNotIsInstance(ret["key1"]["key2"],
1596
mock_dbus_python.Dictionary)
1597
self.assertTrue(ret["key1"]["key2"]["key3"])
1598
self.assertNotIsInstance(ret["key1"]["key2"]["key3"],
1599
mock_dbus_python.Boolean)
1601
def test_call_method_handles_exception(self):
1602
dbus_logger = logging.getLogger("dbus.proxies")
1605
dbus_logger.error("Test")
1606
raise mock_dbus_python.exceptions.DBusException()
1608
mock_dbus_python = self.MockDBusPython_func(func)
1609
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1611
class CountingHandler(logging.Handler):
1613
def emit(self, record):
1616
counting_handler = CountingHandler()
1618
dbus_logger.addHandler(counting_handler)
1621
with self.assertRaises(dbus.Error) as e:
1622
self.call_method(bus, "methodname", "busname",
1623
"objectpath", "interface")
1625
dbus_logger.removeFilter(counting_handler)
1627
self.assertNotIsInstance(e.exception, dbus.ConnectFailed)
1629
# Make sure the dbus logger was suppressed
1630
self.assertEqual(0, counting_handler.count)
1632
def test_Set_Secret_sends_bytearray(self):
1634
def func(*args, **kwargs):
1635
ret[0] = (args, kwargs)
1636
mock_dbus_python = self.MockDBusPython_func(func)
1637
bus = dbus_python_adapter.SystemBus(mock_dbus_python)
1638
bus.set_client_property("objectpath", "Secret", "value")
1639
expected_call = (("se.recompile.Mandos.Client", "Secret",
1640
mock_dbus_python.ByteArray(b"value")),
1642
"org.freedesktop.DBus.Properties"})
1643
self.assertEqual(expected_call, ret[0])
1644
if sys.version_info.major == 2:
1645
self.assertIsInstance(ret[0][0][-1],
1646
mock_dbus_python.ByteArray)
1648
def test_get_object_converts_to_correct_exception(self):
1649
bus = dbus_python_adapter.SystemBus(
1650
self.fake_dbus_python_raises_exception_on_connect)
1651
with self.assertRaises(dbus.ConnectFailed):
1652
self.call_method(bus, "methodname", "busname",
1653
"objectpath", "interface")
1655
class fake_dbus_python_raises_exception_on_connect:
1656
"""fake dbus-python module"""
1658
"""Pseudo-namespace"""
1659
class DBusException(Exception):
1664
def get_object(busname, objectpath):
1665
raise cls.exceptions.DBusException()
1666
Bus = collections.namedtuple("Bus", ["get_object"])
1667
return Bus(get_object=get_object)
1670
class Test_dbus_python_adapter_CachingBus(unittest.TestCase):
1671
class mock_dbus_python:
1672
"""mock dbus-python modules"""
1675
def get_object(busname, objectpath):
1679
self.bus = dbus_python_adapter.CachingBus(
1680
self.mock_dbus_python)
1682
def test_returns_distinct_objectpaths(self):
1683
obj1 = self.bus.get_object("busname", "objectpath1")
1684
self.assertIsInstance(obj1, Unique)
1685
obj2 = self.bus.get_object("busname", "objectpath2")
1686
self.assertIsInstance(obj2, Unique)
1687
self.assertIsNot(obj1, obj2)
1689
def test_returns_distinct_busnames(self):
1690
obj1 = self.bus.get_object("busname1", "objectpath")
1691
self.assertIsInstance(obj1, Unique)
1692
obj2 = self.bus.get_object("busname2", "objectpath")
1693
self.assertIsInstance(obj2, Unique)
1694
self.assertIsNot(obj1, obj2)
1696
def test_returns_distinct_both(self):
1697
obj1 = self.bus.get_object("busname1", "objectpath")
1698
self.assertIsInstance(obj1, Unique)
1699
obj2 = self.bus.get_object("busname2", "objectpath")
1700
self.assertIsInstance(obj2, Unique)
1701
self.assertIsNot(obj1, obj2)
1703
def test_returns_same(self):
1704
obj1 = self.bus.get_object("busname", "objectpath")
1705
self.assertIsInstance(obj1, Unique)
1706
obj2 = self.bus.get_object("busname", "objectpath")
1707
self.assertIsInstance(obj2, Unique)
1708
self.assertIs(obj1, obj2)
1710
def test_returns_same_old(self):
1711
obj1 = self.bus.get_object("busname1", "objectpath1")
1712
self.assertIsInstance(obj1, Unique)
1713
obj2 = self.bus.get_object("busname2", "objectpath2")
1714
self.assertIsInstance(obj2, Unique)
1715
obj1b = self.bus.get_object("busname1", "objectpath1")
1716
self.assertIsInstance(obj1b, Unique)
1717
self.assertIsNot(obj1, obj2)
1718
self.assertIsNot(obj2, obj1b)
1719
self.assertIs(obj1, obj1b)
1722
class Test_pydbus_adapter_SystemBus(TestCaseWithAssertLogs):
1724
def Stub_pydbus_func(self, func):
1726
"""stub pydbus module"""
1729
def get(busname, objectpath):
1730
DBusObject = collections.namedtuple(
1731
"DBusObject", ("methodname",))
1732
return {"interface":
1733
DBusObject(methodname=func)}
1736
def call_method(self, bus, methodname, busname, objectpath,
1738
with self.assertLogs(log, logging.DEBUG):
1739
return bus.call_method(methodname, busname, objectpath,
1742
def test_call_method_returns(self):
1743
expected_method_return = Unique()
1744
method_args = (Unique(), Unique())
1746
self.assertEqual(len(method_args), len(args))
1747
for marg, arg in zip(method_args, args):
1748
self.assertIs(marg, arg)
1749
return expected_method_return
1750
stub_pydbus = self.Stub_pydbus_func(func)
1751
bus = pydbus_adapter.SystemBus(stub_pydbus)
1752
ret = self.call_method(bus, "methodname", "busname",
1753
"objectpath", "interface",
1755
self.assertIs(ret, expected_method_return)
1757
def test_call_method_handles_exception(self):
1759
raise gi.repository.GLib.Error()
1761
stub_pydbus = self.Stub_pydbus_func(func)
1762
bus = pydbus_adapter.SystemBus(stub_pydbus)
1764
with self.assertRaises(dbus.Error) as e:
1765
self.call_method(bus, "methodname", "busname",
1766
"objectpath", "interface")
1768
self.assertNotIsInstance(e.exception, dbus.ConnectFailed)
1770
def test_get_converts_to_correct_exception(self):
1771
bus = pydbus_adapter.SystemBus(
1772
self.fake_pydbus_raises_exception_on_connect)
1773
with self.assertRaises(dbus.ConnectFailed):
1774
self.call_method(bus, "methodname", "busname",
1775
"objectpath", "interface")
1777
class fake_pydbus_raises_exception_on_connect:
1778
"""fake dbus-python module"""
1781
def get(busname, objectpath):
1782
raise gi.repository.GLib.Error()
1783
Bus = collections.namedtuple("Bus", ["get"])
1786
def test_set_property_uses_setattr(self):
1793
def get(busname, objectpath):
1794
return {"interface": obj}
1795
bus = pydbus_adapter.SystemBus(pydbus_spy)
1797
bus.set_property("busname", "objectpath", "interface", "key",
1799
self.assertIs(value, obj.key)
1801
def test_get_suppresses_xml_deprecation_warning(self):
1802
if sys.version_info.major >= 3:
1804
class stub_pydbus_get:
1807
def get(busname, objectpath):
1808
warnings.warn_explicit(
1809
"deprecated", DeprecationWarning,
1810
"xml.etree.ElementTree", 0)
1811
bus = pydbus_adapter.SystemBus(stub_pydbus_get)
1812
with warnings.catch_warnings(record=True) as w:
1813
warnings.simplefilter("always")
1814
bus.get("busname", "objectpath")
1815
self.assertEqual(0, len(w))
1818
class Test_pydbus_adapter_CachingBus(unittest.TestCase):
1820
"""stub pydbus module"""
1823
def get(busname, objectpath):
1827
self.bus = pydbus_adapter.CachingBus(self.stub_pydbus)
1829
def test_returns_distinct_objectpaths(self):
1830
obj1 = self.bus.get("busname", "objectpath1")
1831
self.assertIsInstance(obj1, Unique)
1832
obj2 = self.bus.get("busname", "objectpath2")
1833
self.assertIsInstance(obj2, Unique)
1834
self.assertIsNot(obj1, obj2)
1836
def test_returns_distinct_busnames(self):
1837
obj1 = self.bus.get("busname1", "objectpath")
1838
self.assertIsInstance(obj1, Unique)
1839
obj2 = self.bus.get("busname2", "objectpath")
1840
self.assertIsInstance(obj2, Unique)
1841
self.assertIsNot(obj1, obj2)
1843
def test_returns_distinct_both(self):
1844
obj1 = self.bus.get("busname1", "objectpath")
1845
self.assertIsInstance(obj1, Unique)
1846
obj2 = self.bus.get("busname2", "objectpath")
1847
self.assertIsInstance(obj2, Unique)
1848
self.assertIsNot(obj1, obj2)
1850
def test_returns_same(self):
1851
obj1 = self.bus.get("busname", "objectpath")
1852
self.assertIsInstance(obj1, Unique)
1853
obj2 = self.bus.get("busname", "objectpath")
1854
self.assertIsInstance(obj2, Unique)
1855
self.assertIs(obj1, obj2)
1857
def test_returns_same_old(self):
1858
obj1 = self.bus.get("busname1", "objectpath1")
1859
self.assertIsInstance(obj1, Unique)
1860
obj2 = self.bus.get("busname2", "objectpath2")
1861
self.assertIsInstance(obj2, Unique)
1862
obj1b = self.bus.get("busname1", "objectpath1")
1863
self.assertIsInstance(obj1b, Unique)
1864
self.assertIsNot(obj1, obj2)
1865
self.assertIsNot(obj2, obj1b)
1866
self.assertIs(obj1, obj1b)
1869
class Test_dbussy_adapter_SystemBus(TestCaseWithAssertLogs):
1873
class ObjectPath(str):
1875
class DBusError(Exception):
1878
def fake_ravel_func(self, func):
1882
class DBusInterfaceProxy:
1884
def methodname(*args):
1885
return [func(*args)]
1888
def get_interface(interface):
1889
if interface == "interface":
1890
return DBusInterfaceProxy()
1891
return {"busname": {"objectpath": DBusObject()}}
1894
def call_method(self, bus, methodname, busname, objectpath,
1896
with self.assertLogs(log, logging.DEBUG):
1897
return bus.call_method(methodname, busname, objectpath,
1900
def test_call_method_returns(self):
1901
expected_method_return = Unique()
1902
method_args = (Unique(), Unique())
1904
self.assertEqual(len(method_args), len(args))
1905
for marg, arg in zip(method_args, args):
1906
self.assertIs(marg, arg)
1907
return expected_method_return
1908
fake_ravel = self.fake_ravel_func(func)
1909
bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
1910
ret = self.call_method(bus, "methodname", "busname",
1911
"objectpath", "interface",
1913
self.assertIs(ret, expected_method_return)
1915
def test_call_method_filters_objectpath(self):
1917
return method_return
1918
fake_ravel = self.fake_ravel_func(func)
1919
bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
1920
method_return = (self.dummy_dbussy.DBUS
1921
.ObjectPath("objectpath"))
1922
ret = self.call_method(bus, "methodname", "busname",
1923
"objectpath", "interface")
1924
self.assertEqual("objectpath", ret)
1925
self.assertNotIsInstance(ret,
1926
self.dummy_dbussy.DBUS.ObjectPath)
1928
def test_call_method_filters_objectpaths_in_dict(self):
1929
ObjectPath = self.dummy_dbussy.DBUS.ObjectPath
1931
return method_return
1932
fake_ravel = self.fake_ravel_func(func)
1933
bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
1935
ObjectPath("objectpath_key_1"):
1936
ObjectPath("objectpath_value_1"),
1937
ObjectPath("objectpath_key_2"):
1938
ObjectPath("objectpath_value_2"),
1940
ret = self.call_method(bus, "methodname", "busname",
1941
"objectpath", "interface")
1942
expected_method_return = {str(key): str(value)
1944
method_return.items()}
1945
for key, value in ret.items():
1946
self.assertNotIsInstance(key, ObjectPath)
1947
self.assertNotIsInstance(value, ObjectPath)
1948
self.assertEqual(expected_method_return, ret)
1949
self.assertIsInstance(ret, dict)
1951
def test_call_method_filters_objectpaths_in_dict_in_dict(self):
1952
ObjectPath = self.dummy_dbussy.DBUS.ObjectPath
1954
return method_return
1955
fake_ravel = self.fake_ravel_func(func)
1956
bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
1958
ObjectPath("key1"): {
1959
ObjectPath("key11"): ObjectPath("value11"),
1960
ObjectPath("key12"): ObjectPath("value12"),
1962
ObjectPath("key2"): {
1963
ObjectPath("key21"): ObjectPath("value21"),
1964
ObjectPath("key22"): ObjectPath("value22"),
1967
ret = self.call_method(bus, "methodname", "busname",
1968
"objectpath", "interface")
1969
expected_method_return = {
1970
"key1": {"key11": "value11",
1971
"key12": "value12"},
1972
"key2": {"key21": "value21",
1973
"key22": "value22"},
1975
self.assertEqual(expected_method_return, ret)
1976
for key, value in ret.items():
1977
self.assertIsInstance(value, dict)
1978
self.assertEqual(expected_method_return[key], value)
1979
self.assertNotIsInstance(key, ObjectPath)
1980
for inner_key, inner_value in value.items():
1981
self.assertIsInstance(value, dict)
1983
expected_method_return[key][inner_key],
1985
self.assertNotIsInstance(key, ObjectPath)
1987
def test_call_method_filters_objectpaths_in_dict_three_deep(self):
1988
ObjectPath = self.dummy_dbussy.DBUS.ObjectPath
1990
return method_return
1991
fake_ravel = self.fake_ravel_func(func)
1992
bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
1994
ObjectPath("key1"): {
1995
ObjectPath("key2"): {
1996
ObjectPath("key3"): ObjectPath("value"),
2000
ret = self.call_method(bus, "methodname", "busname",
2001
"objectpath", "interface")
2002
expected_method_return = {"key1": {"key2": {"key3": "value"}}}
2003
self.assertEqual(expected_method_return, ret)
2004
self.assertIsInstance(ret, dict)
2005
self.assertNotIsInstance(next(iter(ret.keys())), ObjectPath)
2006
self.assertIsInstance(ret["key1"], dict)
2007
self.assertNotIsInstance(next(iter(ret["key1"].keys())),
2009
self.assertIsInstance(ret["key1"]["key2"], dict)
2010
self.assertNotIsInstance(
2011
next(iter(ret["key1"]["key2"].keys())),
2013
self.assertEqual("value", ret["key1"]["key2"]["key3"])
2014
self.assertNotIsInstance(ret["key1"]["key2"]["key3"],
2015
self.dummy_dbussy.DBUS.ObjectPath)
2017
def test_call_method_handles_exception(self):
2019
raise self.dummy_dbussy.DBusError()
2021
fake_ravel = self.fake_ravel_func(func)
2022
bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
2024
with self.assertRaises(dbus.Error) as e:
2025
self.call_method(bus, "methodname", "busname",
2026
"objectpath", "interface")
2028
self.assertNotIsInstance(e.exception, dbus.ConnectFailed)
2030
def test_get_object_converts_to_correct_exception(self):
2031
class fake_ravel_raises_exception_on_connect:
2036
def __getitem__(key):
2037
if key == "objectpath":
2038
raise self.dummy_dbussy.DBusError()
2039
raise Exception(key)
2040
return {"busname": Bus()}
2042
raise self.dummy_dbussy.DBusError()
2043
bus = dbussy_adapter.SystemBus(
2045
fake_ravel_raises_exception_on_connect)
2046
with self.assertRaises(dbus.ConnectFailed):
2047
self.call_method(bus, "methodname", "busname",
2048
"objectpath", "interface")
2051
class Test_commands_from_options(unittest.TestCase):
2054
self.parser = argparse.ArgumentParser()
2055
add_command_line_options(self.parser)
2057
def test_is_enabled(self):
2058
self.assert_command_from_args(["--is-enabled", "client"],
2061
def assert_command_from_args(self, args, command_cls, length=1,
2062
clients=None, **cmd_attrs):
2063
"""Assert that parsing ARGS should result in an instance of
2064
COMMAND_CLS with (optionally) all supplied attributes (CMD_ATTRS)."""
2065
options = self.parser.parse_args(args)
2066
check_option_syntax(self.parser, options)
2067
commands = commands_from_options(options)
2068
self.assertEqual(length, len(commands))
2069
for command in commands:
2070
if isinstance(command, command_cls):
2073
self.assertIsInstance(command, command_cls)
2074
if clients is not None:
2075
self.assertEqual(clients, options.client)
2076
for key, value in cmd_attrs.items():
2077
self.assertEqual(value, getattr(command, key))
2079
def assert_commands_from_args(self, args, commands, clients=None):
2080
for cmd in commands:
2081
self.assert_command_from_args(args, cmd,
2082
length=len(commands),
2085
def test_is_enabled_short(self):
2086
self.assert_command_from_args(["-V", "client"],
2089
def test_approve(self):
2090
self.assert_command_from_args(["--approve", "client"],
2093
def test_approve_short(self):
2094
self.assert_command_from_args(["-A", "client"],
2097
def test_deny(self):
2098
self.assert_command_from_args(["--deny", "client"],
2101
def test_deny_short(self):
2102
self.assert_command_from_args(["-D", "client"], command.Deny)
2104
def test_remove(self):
2105
self.assert_command_from_args(["--remove", "client"],
2108
def test_deny_before_remove(self):
2109
options = self.parser.parse_args(["--deny", "--remove",
2111
check_option_syntax(self.parser, options)
2112
commands = commands_from_options(options)
2113
self.assertEqual(2, len(commands))
2114
self.assertIsInstance(commands[0], command.Deny)
2115
self.assertIsInstance(commands[1], command.Remove)
2117
def test_deny_before_remove_reversed(self):
2118
options = self.parser.parse_args(["--remove", "--deny",
2120
check_option_syntax(self.parser, options)
2121
commands = commands_from_options(options)
2122
self.assertEqual(2, len(commands))
2123
self.assertIsInstance(commands[0], command.Deny)
2124
self.assertIsInstance(commands[1], command.Remove)
2126
def test_remove_short(self):
2127
self.assert_command_from_args(["-r", "client"],
2130
def test_dump_json(self):
2131
self.assert_command_from_args(["--dump-json"],
2134
def test_enable(self):
2135
self.assert_command_from_args(["--enable", "client"],
2138
def test_enable_short(self):
2139
self.assert_command_from_args(["-e", "client"],
2142
def test_disable(self):
2143
self.assert_command_from_args(["--disable", "client"],
2146
def test_disable_short(self):
2147
self.assert_command_from_args(["-d", "client"],
2150
def test_bump_timeout(self):
2151
self.assert_command_from_args(["--bump-timeout", "client"],
2152
command.BumpTimeout)
2154
def test_bump_timeout_short(self):
2155
self.assert_command_from_args(["-b", "client"],
2156
command.BumpTimeout)
2158
def test_start_checker(self):
2159
self.assert_command_from_args(["--start-checker", "client"],
2160
command.StartChecker)
2162
def test_stop_checker(self):
2163
self.assert_command_from_args(["--stop-checker", "client"],
2164
command.StopChecker)
2166
def test_approve_by_default(self):
2167
self.assert_command_from_args(["--approve-by-default",
2169
command.ApproveByDefault)
2171
def test_deny_by_default(self):
2172
self.assert_command_from_args(["--deny-by-default", "client"],
2173
command.DenyByDefault)
2175
def test_checker(self):
2176
self.assert_command_from_args(["--checker", ":", "client"],
2180
def test_checker_empty(self):
2181
self.assert_command_from_args(["--checker", "", "client"],
2185
def test_checker_short(self):
2186
self.assert_command_from_args(["-c", ":", "client"],
2190
def test_host(self):
2191
self.assert_command_from_args(
2192
["--host", "client.example.org", "client"],
2193
command.SetHost, value_to_set="client.example.org")
2195
def test_host_short(self):
2196
self.assert_command_from_args(
2197
["-H", "client.example.org", "client"], command.SetHost,
2198
value_to_set="client.example.org")
2200
def test_secret_devnull(self):
2201
self.assert_command_from_args(["--secret", os.path.devnull,
2202
"client"], command.SetSecret,
2205
def test_secret_tempfile(self):
2206
with tempfile.NamedTemporaryFile(mode="r+b") as f:
2207
value = b"secret\0xyzzy\nbar"
2210
self.assert_command_from_args(["--secret", f.name,
2215
def test_secret_devnull_short(self):
2216
self.assert_command_from_args(["-s", os.path.devnull,
2217
"client"], command.SetSecret,
2220
def test_secret_tempfile_short(self):
2221
with tempfile.NamedTemporaryFile(mode="r+b") as f:
2222
value = b"secret\0xyzzy\nbar"
2225
self.assert_command_from_args(["-s", f.name, "client"],
2229
def test_timeout(self):
2230
self.assert_command_from_args(["--timeout", "PT5M", "client"],
2232
value_to_set=300000)
2234
def test_timeout_short(self):
2235
self.assert_command_from_args(["-t", "PT5M", "client"],
2237
value_to_set=300000)
2239
def test_extended_timeout(self):
2240
self.assert_command_from_args(["--extended-timeout", "PT15M",
2242
command.SetExtendedTimeout,
2243
value_to_set=900000)
2245
def test_interval(self):
2246
self.assert_command_from_args(["--interval", "PT2M",
2247
"client"], command.SetInterval,
2248
value_to_set=120000)
2250
def test_interval_short(self):
2251
self.assert_command_from_args(["-i", "PT2M", "client"],
2252
command.SetInterval,
2253
value_to_set=120000)
2255
def test_approval_delay(self):
2256
self.assert_command_from_args(["--approval-delay", "PT30S",
2258
command.SetApprovalDelay,
2261
def test_approval_duration(self):
2262
self.assert_command_from_args(["--approval-duration", "PT1S",
2264
command.SetApprovalDuration,
2267
def test_print_table(self):
2268
self.assert_command_from_args([], command.PrintTable,
2271
def test_print_table_verbose(self):
2272
self.assert_command_from_args(["--verbose"],
2276
def test_print_table_verbose_short(self):
2277
self.assert_command_from_args(["-v"], command.PrintTable,
2281
def test_manual_page_example_1(self):
2282
self.assert_command_from_args("",
2287
def test_manual_page_example_2(self):
2288
self.assert_command_from_args(
2289
"--verbose foo1.example.org foo2.example.org".split(),
2290
command.PrintTable, clients=["foo1.example.org",
2291
"foo2.example.org"],
2294
def test_manual_page_example_3(self):
2295
self.assert_command_from_args("--enable --all".split(),
2299
def test_manual_page_example_4(self):
2300
self.assert_commands_from_args(
2301
("--timeout=PT5M --interval=PT1M foo1.example.org"
2302
" foo2.example.org").split(),
2303
[command.SetTimeout, command.SetInterval],
2304
clients=["foo1.example.org", "foo2.example.org"])
2306
def test_manual_page_example_5(self):
2307
self.assert_command_from_args("--approve --all".split(),
2312
class TestCommand(unittest.TestCase):
790
log.removeFilter(warning_filter)
791
self.assertTrue(getattr(warning_filter, "found", False))
792
self.assertEqual(value, datetime.timedelta(0, 7200))
795
class TestCmd(unittest.TestCase):
2313
796
"""Abstract class for tests of command classes"""
2315
class FakeMandosBus(dbus.MandosBus):
2316
def __init__(self, testcase):
2317
self.client_properties = {
799
class MockClient(object):
800
def __init__(self, name, **attributes):
801
self.__dbus_object_path__ = "objpath_{}".format(name)
802
self.attributes = attributes
803
self.attributes["Name"] = name
805
def Set(self, interface, property, value, dbus_interface):
806
testcase.assertEqual(interface, client_interface)
807
testcase.assertEqual(dbus_interface,
808
dbus.PROPERTIES_IFACE)
809
self.attributes[property] = value
810
def Get(self, interface, property, dbus_interface):
811
testcase.assertEqual(interface, client_interface)
812
testcase.assertEqual(dbus_interface,
813
dbus.PROPERTIES_IFACE)
814
return self.attributes[property]
815
def Approve(self, approve, dbus_interface):
816
testcase.assertEqual(dbus_interface, client_interface)
817
self.calls.append(("Approve", (approve,
819
self.client = MockClient(
821
KeyID=("92ed150794387c03ce684574b1139a65"
822
"94a34f895daaaf09fd8ea90a27cddb12"),
824
Host="foo.example.org",
825
Enabled=dbus.Boolean(True),
827
LastCheckedOK="2019-02-03T00:00:00",
828
Created="2019-01-02T00:00:00",
830
Fingerprint=("778827225BA7DE539C5A"
831
"7CFA59CFF7CDBD9A5920"),
832
CheckerRunning=dbus.Boolean(False),
833
LastEnabled="2019-01-03T00:00:00",
834
ApprovalPending=dbus.Boolean(False),
835
ApprovedByDefault=dbus.Boolean(True),
836
LastApprovalRequest="",
838
ApprovalDuration=1000,
839
Checker="fping -q -- %(host)s",
840
ExtendedTimeout=900000,
841
Expires="2019-02-04T00:00:00",
843
self.other_client = MockClient(
845
KeyID=("0558568eedd67d622f5c83b35a115f79"
846
"6ab612cff5ad227247e46c2b020f441c"),
849
Enabled=dbus.Boolean(True),
851
LastCheckedOK="2019-02-04T00:00:00",
852
Created="2019-01-03T00:00:00",
854
Fingerprint=("3E393AEAEFB84C7E89E2"
855
"F547B3A107558FCA3A27"),
856
CheckerRunning=dbus.Boolean(True),
857
LastEnabled="2019-01-04T00:00:00",
858
ApprovalPending=dbus.Boolean(False),
859
ApprovedByDefault=dbus.Boolean(False),
860
LastApprovalRequest="2019-01-03T00:00:00",
862
ApprovalDuration=1000,
864
ExtendedTimeout=900000,
865
Expires="2019-02-05T00:00:00",
866
LastCheckerStatus=-2)
867
self.clients = collections.OrderedDict(
869
(self.client, self.client.attributes),
870
(self.other_client, self.other_client.attributes),
872
self.one_client = {self.client: self.client.attributes}
874
class TestPrintTableCmd(TestCmd):
875
def test_normal(self):
876
output = PrintTableCmd().output(self.clients)
877
expected_output = """
878
Name Enabled Timeout Last Successful Check
879
foo Yes 00:05:00 2019-02-03T00:00:00
880
barbar Yes 00:05:00 2019-02-04T00:00:00
882
self.assertEqual(output, expected_output)
883
def test_verbose(self):
884
output = PrintTableCmd(verbose=True).output(self.clients)
885
expected_output = """
886
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
887
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
888
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
890
self.assertEqual(output, expected_output)
891
def test_one_client(self):
892
output = PrintTableCmd().output(self.one_client)
893
expected_output = """
894
Name Enabled Timeout Last Successful Check
895
foo Yes 00:05:00 2019-02-03T00:00:00
897
self.assertEqual(output, expected_output)
899
class TestDumpJSONCmd(TestCmd):
901
self.expected_json = {
2319
904
"KeyID": ("92ed150794387c03ce684574b1139a65"
2320
905
"94a34f895daaaf09fd8ea90a27cddb12"),
2321
"Secret": b"secret",
2322
906
"Host": "foo.example.org",
2323
907
"Enabled": True,
2324
908
"Timeout": 300000,
2358
941
"ApprovedByDefault": False,
2359
942
"LastApprovalRequest": "2019-01-03T00:00:00",
2360
943
"ApprovalDelay": 30000,
2361
"ApprovalDuration": 93785000,
944
"ApprovalDuration": 1000,
2363
946
"ExtendedTimeout": 900000,
2364
947
"Expires": "2019-02-05T00:00:00",
2365
948
"LastCheckerStatus": -2,
2367
self.clients = collections.OrderedDict(
2369
("client_objectpath", self.client_properties),
2370
("other_client_objectpath",
2371
self.other_client_properties),
2373
self.one_client = {"client_objectpath":
2374
self.client_properties}
2375
self.testcase = testcase
2378
def call_method(self, methodname, busname, objectpath,
2380
self.testcase.assertEqual("se.recompile.Mandos", busname)
2381
self.calls.append((methodname, busname, objectpath,
2383
if interface == "org.freedesktop.DBus.Properties":
2384
if methodname == "Set":
2385
self.testcase.assertEqual(3, len(args))
2386
interface, key, value = args
2387
self.testcase.assertEqual(
2388
"se.recompile.Mandos.Client", interface)
2389
self.clients[objectpath][key] = value
2391
elif interface == "se.recompile.Mandos":
2392
self.testcase.assertEqual("RemoveClient", methodname)
2393
self.testcase.assertEqual(1, len(args))
2394
clientpath = args[0]
2395
del self.clients[clientpath]
2397
elif interface == "se.recompile.Mandos.Client":
2398
if methodname == "Approve":
2399
self.testcase.assertEqual(1, len(args))
2404
self.bus = self.FakeMandosBus(self)
2407
class TestBaseCommands(TestCommand):
2409
def test_IsEnabled_exits_successfully(self):
951
return super(TestDumpJSONCmd, self).setUp()
952
def test_normal(self):
953
json_data = json.loads(DumpJSONCmd().output(self.clients))
954
self.assertDictEqual(json_data, self.expected_json)
955
def test_one_client(self):
956
clients = self.one_client
957
json_data = json.loads(DumpJSONCmd().output(clients))
958
expected_json = {"foo": self.expected_json["foo"]}
959
self.assertDictEqual(json_data, expected_json)
961
class TestIsEnabledCmd(TestCmd):
962
def test_is_enabled(self):
963
self.assertTrue(all(IsEnabledCmd().is_enabled(client, properties)
964
for client, properties in self.clients.items()))
965
def test_is_enabled_run_exits_successfully(self):
2410
966
with self.assertRaises(SystemExit) as e:
2411
command.IsEnabled().run(self.bus.one_client)
967
IsEnabledCmd().run(None, self.one_client)
2412
968
if e.exception.code is not None:
2413
self.assertEqual(0, e.exception.code)
969
self.assertEqual(e.exception.code, 0)
2415
971
self.assertIsNone(e.exception.code)
2417
def test_IsEnabled_exits_with_failure(self):
2418
self.bus.client_properties["Enabled"] = False
972
def test_is_enabled_run_exits_with_failure(self):
973
self.client.attributes["Enabled"] = dbus.Boolean(False)
2419
974
with self.assertRaises(SystemExit) as e:
2420
command.IsEnabled().run(self.bus.one_client)
975
IsEnabledCmd().run(None, self.one_client)
2421
976
if isinstance(e.exception.code, int):
2422
self.assertNotEqual(0, e.exception.code)
977
self.assertNotEqual(e.exception.code, 0)
2424
979
self.assertIsNotNone(e.exception.code)
2426
def test_Approve(self):
2427
busname = "se.recompile.Mandos"
2428
client_interface = "se.recompile.Mandos.Client"
2429
command.Approve().run(self.bus.clients, self.bus)
2430
self.assertTrue(self.bus.clients)
2431
for clientpath in self.bus.clients:
2432
self.assertIn(("Approve", busname, clientpath,
2433
client_interface, (True,)), self.bus.calls)
2435
def test_Deny(self):
2436
busname = "se.recompile.Mandos"
2437
client_interface = "se.recompile.Mandos.Client"
2438
command.Deny().run(self.bus.clients, self.bus)
2439
self.assertTrue(self.bus.clients)
2440
for clientpath in self.bus.clients:
2441
self.assertIn(("Approve", busname, clientpath,
2442
client_interface, (False,)),
2445
def test_Remove(self):
2446
busname = "se.recompile.Mandos"
2448
server_interface = "se.recompile.Mandos"
2449
orig_clients = self.bus.clients.copy()
2450
command.Remove().run(self.bus.clients, self.bus)
2451
self.assertFalse(self.bus.clients)
2452
for clientpath in orig_clients:
2453
self.assertIn(("RemoveClient", busname,
2454
server_path, server_interface,
2455
(clientpath,)), self.bus.calls)
2460
"KeyID": ("92ed150794387c03ce684574b1139a65"
2461
"94a34f895daaaf09fd8ea90a27cddb12"),
2462
"Host": "foo.example.org",
2465
"LastCheckedOK": "2019-02-03T00:00:00",
2466
"Created": "2019-01-02T00:00:00",
2468
"Fingerprint": ("778827225BA7DE539C5A"
2469
"7CFA59CFF7CDBD9A5920"),
2470
"CheckerRunning": False,
2471
"LastEnabled": "2019-01-03T00:00:00",
2472
"ApprovalPending": False,
2473
"ApprovedByDefault": True,
2474
"LastApprovalRequest": "",
2476
"ApprovalDuration": 1000,
2477
"Checker": "fping -q -- %(host)s",
2478
"ExtendedTimeout": 900000,
2479
"Expires": "2019-02-04T00:00:00",
2480
"LastCheckerStatus": 0,
2484
"KeyID": ("0558568eedd67d622f5c83b35a115f79"
2485
"6ab612cff5ad227247e46c2b020f441c"),
2486
"Host": "192.0.2.3",
2489
"LastCheckedOK": "2019-02-04T00:00:00",
2490
"Created": "2019-01-03T00:00:00",
2492
"Fingerprint": ("3E393AEAEFB84C7E89E2"
2493
"F547B3A107558FCA3A27"),
2494
"CheckerRunning": True,
2495
"LastEnabled": "2019-01-04T00:00:00",
2496
"ApprovalPending": False,
2497
"ApprovedByDefault": False,
2498
"LastApprovalRequest": "2019-01-03T00:00:00",
2499
"ApprovalDelay": 30000,
2500
"ApprovalDuration": 93785000,
2502
"ExtendedTimeout": 900000,
2503
"Expires": "2019-02-05T00:00:00",
2504
"LastCheckerStatus": -2,
2508
def test_DumpJSON_normal(self):
2509
with self.capture_stdout_to_buffer() as buffer:
2510
command.DumpJSON().run(self.bus.clients)
2511
json_data = json.loads(buffer.getvalue())
2512
self.assertDictEqual(self.expected_json, json_data)
2515
@contextlib.contextmanager
2516
def capture_stdout_to_buffer():
2517
capture_buffer = io.StringIO()
2518
old_stdout = sys.stdout
2519
sys.stdout = capture_buffer
2521
yield capture_buffer
2523
sys.stdout = old_stdout
2525
def test_DumpJSON_one_client(self):
2526
with self.capture_stdout_to_buffer() as buffer:
2527
command.DumpJSON().run(self.bus.one_client)
2528
json_data = json.loads(buffer.getvalue())
2529
expected_json = {"foo": self.expected_json["foo"]}
2530
self.assertDictEqual(expected_json, json_data)
2532
def test_PrintTable_normal(self):
2533
with self.capture_stdout_to_buffer() as buffer:
2534
command.PrintTable().run(self.bus.clients)
2535
expected_output = "\n".join((
2536
"Name Enabled Timeout Last Successful Check",
2537
"foo Yes 00:05:00 2019-02-03T00:00:00 ",
2538
"barbar Yes 00:05:00 2019-02-04T00:00:00 ",
2540
self.assertEqual(expected_output, buffer.getvalue())
2542
def test_PrintTable_verbose(self):
2543
with self.capture_stdout_to_buffer() as buffer:
2544
command.PrintTable(verbose=True).run(self.bus.clients)
2559
"Last Successful Check ",
2560
"2019-02-03T00:00:00 ",
2561
"2019-02-04T00:00:00 ",
2564
"2019-01-02T00:00:00 ",
2565
"2019-01-03T00:00:00 ",
2577
("92ed150794387c03ce684574b1139a6594a34f895daaaf09fd8"
2579
("0558568eedd67d622f5c83b35a115f796ab612cff5ad227247e"
2583
"778827225BA7DE539C5A7CFA59CFF7CDBD9A5920 ",
2584
"3E393AEAEFB84C7E89E2F547B3A107558FCA3A27 ",
2586
"Check Is Running ",
2591
"2019-01-03T00:00:00 ",
2592
"2019-01-04T00:00:00 ",
2594
"Approval Is Pending ",
2598
"Approved By Default ",
2602
"Last Approval Request ",
2604
"2019-01-03T00:00:00 ",
2610
"Approval Duration ",
2615
"fping -q -- %(host)s ",
2618
"Extended Timeout ",
2623
"2019-02-04T00:00:00 ",
2624
"2019-02-05T00:00:00 ",
2626
"Last Checker Status",
2631
num_lines = max(len(rows) for rows in columns)
2632
expected_output = ("\n".join("".join(rows[line]
2633
for rows in columns)
2634
for line in range(num_lines))
2636
self.assertEqual(expected_output, buffer.getvalue())
2638
def test_PrintTable_one_client(self):
2639
with self.capture_stdout_to_buffer() as buffer:
2640
command.PrintTable().run(self.bus.one_client)
2641
expected_output = "\n".join((
2642
"Name Enabled Timeout Last Successful Check",
2643
"foo Yes 00:05:00 2019-02-03T00:00:00 ",
2645
self.assertEqual(expected_output, buffer.getvalue())
2648
class TestPropertySetterCmd(TestCommand):
2649
"""Abstract class for tests of command.PropertySetter classes"""
981
class TestRemoveCmd(TestCmd):
982
def test_remove(self):
983
class MockMandos(object):
986
def RemoveClient(self, dbus_path):
987
self.calls.append(("RemoveClient", (dbus_path,)))
988
mandos = MockMandos()
989
super(TestRemoveCmd, self).setUp()
990
RemoveCmd().run(mandos, self.clients)
991
self.assertEqual(len(mandos.calls), 2)
992
for client in self.clients:
993
self.assertIn(("RemoveClient",
994
(client.__dbus_object_path__,)),
997
class TestApproveCmd(TestCmd):
998
def test_approve(self):
999
ApproveCmd().run(None, self.clients)
1000
for client in self.clients:
1001
self.assertIn(("Approve", (True, client_interface)),
1004
class TestDenyCmd(TestCmd):
1005
def test_deny(self):
1006
DenyCmd().run(None, self.clients)
1007
for client in self.clients:
1008
self.assertIn(("Approve", (False, client_interface)),
1011
class TestEnableCmd(TestCmd):
1012
def test_enable(self):
1013
for client in self.clients:
1014
client.attributes["Enabled"] = False
1016
EnableCmd().run(None, self.clients)
1018
for client in self.clients:
1019
self.assertTrue(client.attributes["Enabled"])
1021
class TestDisableCmd(TestCmd):
1022
def test_disable(self):
1023
DisableCmd().run(None, self.clients)
1025
for client in self.clients:
1026
self.assertFalse(client.attributes["Enabled"])
1028
class Unique(object):
1029
"""Class for objects which exist only to be unique objects, since
1030
unittest.mock.sentinel only exists in Python 3.3"""
1032
class TestPropertyCmd(TestCmd):
1033
"""Abstract class for tests of PropertyCmd classes"""
2651
1034
def runTest(self):
2652
1035
if not hasattr(self, "command"):
2653
return # Abstract TestCase class
2655
if hasattr(self, "values_to_set"):
2656
cmd_args = [(value,) for value in self.values_to_set]
2657
values_to_get = getattr(self, "values_to_get",
2660
cmd_args = [() for x in range(len(self.values_to_get))]
2661
values_to_get = self.values_to_get
2662
self.assertTrue(values_to_get)
2663
for value_to_get, cmd_arg in zip(values_to_get, cmd_args):
2664
for clientpath in self.bus.clients:
2665
self.bus.clients[clientpath][self.propname] = (
2667
self.command(*cmd_arg).run(self.bus.clients, self.bus)
2668
self.assertTrue(self.bus.clients)
2669
for clientpath in self.bus.clients:
2670
value = (self.bus.clients[clientpath]
1037
values_to_get = getattr(self, "values_to_get",
1039
for value_to_set, value_to_get in zip(self.values_to_set,
1041
for client in self.clients:
1042
old_value = client.attributes[self.property]
1043
self.assertNotIsInstance(old_value, Unique)
1044
client.attributes[self.property] = Unique()
1045
self.run_command(value_to_set, self.clients)
1046
for client in self.clients:
1047
value = client.attributes[self.property]
2672
1048
self.assertNotIsInstance(value, Unique)
2673
self.assertEqual(value_to_get, value)
2676
class TestEnableCmd(TestPropertySetterCmd):
2677
command = command.Enable
2678
propname = "Enabled"
2679
values_to_get = [True]
2682
class TestDisableCmd(TestPropertySetterCmd):
2683
command = command.Disable
2684
propname = "Enabled"
2685
values_to_get = [False]
2688
class TestBumpTimeoutCmd(TestPropertySetterCmd):
2689
command = command.BumpTimeout
2690
propname = "LastCheckedOK"
2691
values_to_get = [""]
2694
class TestStartCheckerCmd(TestPropertySetterCmd):
2695
command = command.StartChecker
2696
propname = "CheckerRunning"
2697
values_to_get = [True]
2700
class TestStopCheckerCmd(TestPropertySetterCmd):
2701
command = command.StopChecker
2702
propname = "CheckerRunning"
2703
values_to_get = [False]
2706
class TestApproveByDefaultCmd(TestPropertySetterCmd):
2707
command = command.ApproveByDefault
2708
propname = "ApprovedByDefault"
2709
values_to_get = [True]
2712
class TestDenyByDefaultCmd(TestPropertySetterCmd):
2713
command = command.DenyByDefault
2714
propname = "ApprovedByDefault"
2715
values_to_get = [False]
2718
class TestSetCheckerCmd(TestPropertySetterCmd):
2719
command = command.SetChecker
2720
propname = "Checker"
1049
self.assertEqual(value, value_to_get)
1050
def run_command(self, value, clients):
1051
self.command().run(None, clients)
1053
class TestBumpTimeoutCmd(TestPropertyCmd):
1054
command = BumpTimeoutCmd
1055
property = "LastCheckedOK"
1056
values_to_set = [""]
1058
class TestStartCheckerCmd(TestPropertyCmd):
1059
command = StartCheckerCmd
1060
property = "CheckerRunning"
1061
values_to_set = [dbus.Boolean(True)]
1063
class TestStopCheckerCmd(TestPropertyCmd):
1064
command = StopCheckerCmd
1065
property = "CheckerRunning"
1066
values_to_set = [dbus.Boolean(False)]
1068
class TestApproveByDefaultCmd(TestPropertyCmd):
1069
command = ApproveByDefaultCmd
1070
property = "ApprovedByDefault"
1071
values_to_set = [dbus.Boolean(True)]
1073
class TestDenyByDefaultCmd(TestPropertyCmd):
1074
command = DenyByDefaultCmd
1075
property = "ApprovedByDefault"
1076
values_to_set = [dbus.Boolean(False)]
1078
class TestValueArgumentPropertyCmd(TestPropertyCmd):
1079
"""Abstract class for tests of PropertyCmd classes using the
1080
ValueArgumentMixIn"""
1082
if type(self) is TestValueArgumentPropertyCmd:
1084
return super(TestValueArgumentPropertyCmd, self).runTest()
1085
def run_command(self, value, clients):
1086
self.command(value).run(None, clients)
1088
class TestSetCheckerCmd(TestValueArgumentPropertyCmd):
1089
command = SetCheckerCmd
1090
property = "Checker"
2721
1091
values_to_set = ["", ":", "fping -q -- %s"]
2724
class TestSetHostCmd(TestPropertySetterCmd):
2725
command = command.SetHost
2727
values_to_set = ["192.0.2.3", "client.example.org"]
2730
class TestSetSecretCmd(TestPropertySetterCmd):
2731
command = command.SetSecret
2733
def __init__(self, *args, **kwargs):
2734
self.values_to_set = [io.BytesIO(b""),
2735
io.BytesIO(b"secret\0xyzzy\nbar")]
2736
self.values_to_get = [f.getvalue() for f in
2738
super(TestSetSecretCmd, self).__init__(*args, **kwargs)
2741
class TestSetTimeoutCmd(TestPropertySetterCmd):
2742
command = command.SetTimeout
2743
propname = "Timeout"
2744
values_to_set = [datetime.timedelta(),
2745
datetime.timedelta(minutes=5),
2746
datetime.timedelta(seconds=1),
2747
datetime.timedelta(weeks=1),
2748
datetime.timedelta(weeks=52)]
2749
values_to_get = [dt.total_seconds()*1000 for dt in values_to_set]
2752
class TestSetExtendedTimeoutCmd(TestPropertySetterCmd):
2753
command = command.SetExtendedTimeout
2754
propname = "ExtendedTimeout"
2755
values_to_set = [datetime.timedelta(),
2756
datetime.timedelta(minutes=5),
2757
datetime.timedelta(seconds=1),
2758
datetime.timedelta(weeks=1),
2759
datetime.timedelta(weeks=52)]
2760
values_to_get = [dt.total_seconds()*1000 for dt in values_to_set]
2763
class TestSetIntervalCmd(TestPropertySetterCmd):
2764
command = command.SetInterval
2765
propname = "Interval"
2766
values_to_set = [datetime.timedelta(),
2767
datetime.timedelta(minutes=5),
2768
datetime.timedelta(seconds=1),
2769
datetime.timedelta(weeks=1),
2770
datetime.timedelta(weeks=52)]
2771
values_to_get = [dt.total_seconds()*1000 for dt in values_to_set]
2774
class TestSetApprovalDelayCmd(TestPropertySetterCmd):
2775
command = command.SetApprovalDelay
2776
propname = "ApprovalDelay"
2777
values_to_set = [datetime.timedelta(),
2778
datetime.timedelta(minutes=5),
2779
datetime.timedelta(seconds=1),
2780
datetime.timedelta(weeks=1),
2781
datetime.timedelta(weeks=52)]
2782
values_to_get = [dt.total_seconds()*1000 for dt in values_to_set]
2785
class TestSetApprovalDurationCmd(TestPropertySetterCmd):
2786
command = command.SetApprovalDuration
2787
propname = "ApprovalDuration"
2788
values_to_set = [datetime.timedelta(),
2789
datetime.timedelta(minutes=5),
2790
datetime.timedelta(seconds=1),
2791
datetime.timedelta(weeks=1),
2792
datetime.timedelta(weeks=52)]
2793
values_to_get = [dt.total_seconds()*1000 for dt in values_to_set]
1093
class TestSetHostCmd(TestValueArgumentPropertyCmd):
1094
command = SetHostCmd
1096
values_to_set = ["192.0.2.3", "foo.example.org"]
1098
class TestSetSecretCmd(TestValueArgumentPropertyCmd):
1099
command = SetSecretCmd
1101
values_to_set = [open("/dev/null", "rb"),
1102
io.BytesIO(b"secret\0xyzzy\nbar")]
1103
values_to_get = [b"", b"secret\0xyzzy\nbar"]
1105
class TestSetTimeoutCmd(TestValueArgumentPropertyCmd):
1106
command = SetTimeoutCmd
1107
property = "Timeout"
1108
values_to_set = [datetime.timedelta(),
1109
datetime.timedelta(minutes=5),
1110
datetime.timedelta(seconds=1),
1111
datetime.timedelta(weeks=1),
1112
datetime.timedelta(weeks=52)]
1113
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1115
class TestSetExtendedTimeoutCmd(TestValueArgumentPropertyCmd):
1116
command = SetExtendedTimeoutCmd
1117
property = "ExtendedTimeout"
1118
values_to_set = [datetime.timedelta(),
1119
datetime.timedelta(minutes=5),
1120
datetime.timedelta(seconds=1),
1121
datetime.timedelta(weeks=1),
1122
datetime.timedelta(weeks=52)]
1123
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1125
class TestSetIntervalCmd(TestValueArgumentPropertyCmd):
1126
command = SetIntervalCmd
1127
property = "Interval"
1128
values_to_set = [datetime.timedelta(),
1129
datetime.timedelta(minutes=5),
1130
datetime.timedelta(seconds=1),
1131
datetime.timedelta(weeks=1),
1132
datetime.timedelta(weeks=52)]
1133
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1135
class TestSetApprovalDelayCmd(TestValueArgumentPropertyCmd):
1136
command = SetApprovalDelayCmd
1137
property = "ApprovalDelay"
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]
1145
class TestSetApprovalDurationCmd(TestValueArgumentPropertyCmd):
1146
command = SetApprovalDurationCmd
1147
property = "ApprovalDuration"
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]
1155
class Test_command_from_options(unittest.TestCase):
1157
self.parser = argparse.ArgumentParser()
1158
add_command_line_options(self.parser)
1159
def assert_command_from_args(self, args, command_cls, **cmd_attrs):
1160
"""Assert that parsing ARGS should result in an instance of
1161
COMMAND_CLS with (optionally) all supplied attributes (CMD_ATTRS)."""
1162
options = self.parser.parse_args(args)
1163
check_option_syntax(self.parser, options)
1164
commands = commands_from_options(options)
1165
self.assertEqual(len(commands), 1)
1166
command = commands[0]
1167
self.assertIsInstance(command, command_cls)
1168
for key, value in cmd_attrs.items():
1169
self.assertEqual(getattr(command, key), value)
1170
def test_print_table(self):
1171
self.assert_command_from_args([], PrintTableCmd,
1174
def test_print_table_verbose(self):
1175
self.assert_command_from_args(["--verbose"], PrintTableCmd,
1178
def test_print_table_verbose_short(self):
1179
self.assert_command_from_args(["-v"], PrintTableCmd,
1182
def test_enable(self):
1183
self.assert_command_from_args(["--enable", "foo"], EnableCmd)
1185
def test_enable_short(self):
1186
self.assert_command_from_args(["-e", "foo"], EnableCmd)
1188
def test_disable(self):
1189
self.assert_command_from_args(["--disable", "foo"],
1192
def test_disable_short(self):
1193
self.assert_command_from_args(["-d", "foo"], DisableCmd)
1195
def test_bump_timeout(self):
1196
self.assert_command_from_args(["--bump-timeout", "foo"],
1199
def test_bump_timeout_short(self):
1200
self.assert_command_from_args(["-b", "foo"], BumpTimeoutCmd)
1202
def test_start_checker(self):
1203
self.assert_command_from_args(["--start-checker", "foo"],
1206
def test_stop_checker(self):
1207
self.assert_command_from_args(["--stop-checker", "foo"],
1210
def test_remove(self):
1211
self.assert_command_from_args(["--remove", "foo"],
1214
def test_remove_short(self):
1215
self.assert_command_from_args(["-r", "foo"], RemoveCmd)
1217
def test_checker(self):
1218
self.assert_command_from_args(["--checker", ":", "foo"],
1219
SetCheckerCmd, value_to_set=":")
1221
def test_checker_empty(self):
1222
self.assert_command_from_args(["--checker", "", "foo"],
1223
SetCheckerCmd, value_to_set="")
1225
def test_checker_short(self):
1226
self.assert_command_from_args(["-c", ":", "foo"],
1227
SetCheckerCmd, value_to_set=":")
1229
def test_timeout(self):
1230
self.assert_command_from_args(["--timeout", "PT5M", "foo"],
1232
value_to_set=300000)
1234
def test_timeout_short(self):
1235
self.assert_command_from_args(["-t", "PT5M", "foo"],
1237
value_to_set=300000)
1239
def test_extended_timeout(self):
1240
self.assert_command_from_args(["--extended-timeout", "PT15M",
1242
SetExtendedTimeoutCmd,
1243
value_to_set=900000)
1245
def test_interval(self):
1246
self.assert_command_from_args(["--interval", "PT2M", "foo"],
1248
value_to_set=120000)
1250
def test_interval_short(self):
1251
self.assert_command_from_args(["-i", "PT2M", "foo"],
1253
value_to_set=120000)
1255
def test_approve_by_default(self):
1256
self.assert_command_from_args(["--approve-by-default", "foo"],
1257
ApproveByDefaultCmd)
1259
def test_deny_by_default(self):
1260
self.assert_command_from_args(["--deny-by-default", "foo"],
1263
def test_approval_delay(self):
1264
self.assert_command_from_args(["--approval-delay", "PT30S",
1265
"foo"], SetApprovalDelayCmd,
1268
def test_approval_duration(self):
1269
self.assert_command_from_args(["--approval-duration", "PT1S",
1270
"foo"], SetApprovalDurationCmd,
1273
def test_host(self):
1274
self.assert_command_from_args(["--host", "foo.example.org",
1276
value_to_set="foo.example.org")
1278
def test_host_short(self):
1279
self.assert_command_from_args(["-H", "foo.example.org",
1281
value_to_set="foo.example.org")
1283
def test_secret_devnull(self):
1284
self.assert_command_from_args(["--secret", os.path.devnull,
1285
"foo"], SetSecretCmd,
1288
def test_secret_tempfile(self):
1289
with tempfile.NamedTemporaryFile(mode="r+b") as f:
1290
value = b"secret\0xyzzy\nbar"
1293
self.assert_command_from_args(["--secret", f.name,
1294
"foo"], SetSecretCmd,
1297
def test_secret_devnull_short(self):
1298
self.assert_command_from_args(["-s", os.path.devnull, "foo"],
1299
SetSecretCmd, value_to_set=b"")
1301
def test_secret_tempfile_short(self):
1302
with tempfile.NamedTemporaryFile(mode="r+b") as f:
1303
value = b"secret\0xyzzy\nbar"
1306
self.assert_command_from_args(["-s", f.name, "foo"],
1310
def test_approve(self):
1311
self.assert_command_from_args(["--approve", "foo"],
1314
def test_approve_short(self):
1315
self.assert_command_from_args(["-A", "foo"], ApproveCmd)
1317
def test_deny(self):
1318
self.assert_command_from_args(["--deny", "foo"], DenyCmd)
1320
def test_deny_short(self):
1321
self.assert_command_from_args(["-D", "foo"], DenyCmd)
1323
def test_dump_json(self):
1324
self.assert_command_from_args(["--dump-json"], DumpJSONCmd)
1326
def test_is_enabled(self):
1327
self.assert_command_from_args(["--is-enabled", "foo"],
1330
def test_is_enabled_short(self):
1331
self.assert_command_from_args(["-V", "foo"], IsEnabledCmd)
2797
def parse_test_args():
2798
# type: () -> argparse.Namespace
1335
def should_only_run_tests():
2799
1336
parser = argparse.ArgumentParser(add_help=False)
2800
parser.add_argument("--check", action="store_true")
2801
parser.add_argument("--prefix", )
1337
parser.add_argument("--check", action='store_true')
2802
1338
args, unknown_args = parser.parse_known_args()
2804
# Remove test options from sys.argv
1339
run_tests = args.check
1341
# Remove --check argument from sys.argv
2805
1342
sys.argv[1:] = unknown_args
2808
1345
# Add all tests from doctest strings
2809
1346
def load_tests(loader, tests, none):