75
79
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
78
def milliseconds_to_string(ms):
79
td = datetime.timedelta(0, 0, 0, ms)
80
return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
81
.format(days="{}T".format(td.days) if td.days else "",
82
hours=td.seconds // 3600,
83
minutes=(td.seconds % 3600) // 60,
84
seconds=td.seconds % 60))
83
parser = argparse.ArgumentParser()
85
add_command_line_options(parser)
87
options = parser.parse_args()
89
check_option_syntax(parser, options)
91
clientnames = options.client
94
log.setLevel(logging.DEBUG)
97
bus = dbus.SystemBus()
98
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
99
dbus_busname, server_dbus_path)
100
mandos_dbus_objc = bus.get_object(dbus_busname,
102
except dbus.exceptions.DBusException:
103
log.critical("Could not connect to Mandos server")
106
mandos_serv = dbus.Interface(mandos_dbus_objc,
107
dbus_interface=server_dbus_interface)
108
mandos_serv_object_manager = dbus.Interface(
109
mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
111
# Filter out log message from dbus module
112
dbus_logger = logging.getLogger("dbus.proxies")
113
class NullFilter(logging.Filter):
114
def filter(self, record):
116
dbus_filter = NullFilter()
118
dbus_logger.addFilter(dbus_filter)
119
log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", dbus_busname,
120
server_dbus_path, dbus.OBJECT_MANAGER_IFACE)
121
mandos_clients = {path: ifs_and_props[client_dbus_interface]
122
for path, ifs_and_props in
123
mandos_serv_object_manager
124
.GetManagedObjects().items()
125
if client_dbus_interface in ifs_and_props}
126
except dbus.exceptions.DBusException as e:
127
log.critical("Failed to access Mandos server through D-Bus:"
131
# restore dbus logger
132
dbus_logger.removeFilter(dbus_filter)
134
# Compile dict of (clients: properties) to process
138
clients = {objpath: properties
139
for objpath, properties in mandos_clients.items()}
141
for name in clientnames:
142
for objpath, properties in mandos_clients.items():
143
if properties["Name"] == name:
144
clients[objpath] = properties
147
log.critical("Client not found on server: %r", name)
150
# Run all commands on clients
151
commands = commands_from_options(options)
152
for command in commands:
153
command.run(clients, bus, mandos_serv)
156
def add_command_line_options(parser):
157
parser.add_argument("--version", action="version",
158
version="%(prog)s {}".format(version),
159
help="show version number and exit")
160
parser.add_argument("-a", "--all", action="store_true",
161
help="Select all clients")
162
parser.add_argument("-v", "--verbose", action="store_true",
163
help="Print all fields")
164
parser.add_argument("-j", "--dump-json", action="store_true",
165
help="Dump client data in JSON format")
166
enable_disable = parser.add_mutually_exclusive_group()
167
enable_disable.add_argument("-e", "--enable", action="store_true",
168
help="Enable client")
169
enable_disable.add_argument("-d", "--disable",
171
help="disable client")
172
parser.add_argument("-b", "--bump-timeout", action="store_true",
173
help="Bump timeout for client")
174
start_stop_checker = parser.add_mutually_exclusive_group()
175
start_stop_checker.add_argument("--start-checker",
177
help="Start checker for client")
178
start_stop_checker.add_argument("--stop-checker",
180
help="Stop checker for client")
181
parser.add_argument("-V", "--is-enabled", action="store_true",
182
help="Check if client is enabled")
183
parser.add_argument("-r", "--remove", action="store_true",
184
help="Remove client")
185
parser.add_argument("-c", "--checker",
186
help="Set checker command for client")
187
parser.add_argument("-t", "--timeout", type=string_to_delta,
188
help="Set timeout for client")
189
parser.add_argument("--extended-timeout", type=string_to_delta,
190
help="Set extended timeout for client")
191
parser.add_argument("-i", "--interval", type=string_to_delta,
192
help="Set checker interval for client")
193
approve_deny_default = parser.add_mutually_exclusive_group()
194
approve_deny_default.add_argument(
195
"--approve-by-default", action="store_true",
196
default=None, dest="approved_by_default",
197
help="Set client to be approved by default")
198
approve_deny_default.add_argument(
199
"--deny-by-default", action="store_false",
200
dest="approved_by_default",
201
help="Set client to be denied by default")
202
parser.add_argument("--approval-delay", type=string_to_delta,
203
help="Set delay before client approve/deny")
204
parser.add_argument("--approval-duration", type=string_to_delta,
205
help="Set duration of one client approval")
206
parser.add_argument("-H", "--host", help="Set host for client")
207
parser.add_argument("-s", "--secret",
208
type=argparse.FileType(mode="rb"),
209
help="Set password blob (file) for client")
210
approve_deny = parser.add_mutually_exclusive_group()
211
approve_deny.add_argument(
212
"-A", "--approve", action="store_true",
213
help="Approve any current client request")
214
approve_deny.add_argument("-D", "--deny", action="store_true",
215
help="Deny any current client request")
216
parser.add_argument("--debug", action="store_true",
217
help="Debug mode (show D-Bus commands)")
218
parser.add_argument("--check", action="store_true",
219
help="Run self-test")
220
parser.add_argument("client", nargs="*", help="Client name")
223
def string_to_delta(interval):
224
"""Parse a string and return a datetime.timedelta"""
227
return rfc3339_duration_to_delta(interval)
228
except ValueError as e:
229
log.warning("%s - Parsing as pre-1.6.1 interval instead",
231
return parse_pre_1_6_1_interval(interval)
87
234
def rfc3339_duration_to_delta(duration):
271
## Classes for commands.
273
# Abstract classes first
407
def check_option_syntax(parser, options):
408
"""Apply additional restrictions on options, not expressible in
411
def has_actions(options):
412
return any((options.enable,
414
options.bump_timeout,
415
options.start_checker,
416
options.stop_checker,
419
options.checker is not None,
420
options.timeout is not None,
421
options.extended_timeout is not None,
422
options.interval is not None,
423
options.approved_by_default is not None,
424
options.approval_delay is not None,
425
options.approval_duration is not None,
426
options.host is not None,
427
options.secret is not None,
431
if has_actions(options) and not (options.client or options.all):
432
parser.error("Options require clients names or --all.")
433
if options.verbose and has_actions(options):
434
parser.error("--verbose can only be used alone.")
435
if options.dump_json and (options.verbose
436
or has_actions(options)):
437
parser.error("--dump-json can only be used alone.")
438
if options.all and not has_actions(options):
439
parser.error("--all requires an action.")
440
if options.is_enabled and len(options.client) > 1:
441
parser.error("--is-enabled requires exactly one client")
443
options.remove = False
444
if has_actions(options) and not options.deny:
445
parser.error("--remove can only be combined with --deny")
446
options.remove = True
449
def commands_from_options(options):
453
if options.is_enabled:
454
commands.append(IsEnabledCmd())
457
commands.append(ApproveCmd())
460
commands.append(DenyCmd())
463
commands.append(RemoveCmd())
465
if options.dump_json:
466
commands.append(DumpJSONCmd())
469
commands.append(EnableCmd())
472
commands.append(DisableCmd())
474
if options.bump_timeout:
475
commands.append(BumpTimeoutCmd())
477
if options.start_checker:
478
commands.append(StartCheckerCmd())
480
if options.stop_checker:
481
commands.append(StopCheckerCmd())
483
if options.approved_by_default is not None:
484
if options.approved_by_default:
485
commands.append(ApproveByDefaultCmd())
487
commands.append(DenyByDefaultCmd())
489
if options.checker is not None:
490
commands.append(SetCheckerCmd(options.checker))
492
if options.host is not None:
493
commands.append(SetHostCmd(options.host))
495
if options.secret is not None:
496
commands.append(SetSecretCmd(options.secret))
498
if options.timeout is not None:
499
commands.append(SetTimeoutCmd(options.timeout))
501
if options.extended_timeout:
503
SetExtendedTimeoutCmd(options.extended_timeout))
505
if options.interval is not None:
506
commands.append(SetIntervalCmd(options.interval))
508
if options.approval_delay is not None:
509
commands.append(SetApprovalDelayCmd(options.approval_delay))
511
if options.approval_duration is not None:
513
SetApprovalDurationCmd(options.approval_duration))
515
# If no command option has been given, show table of clients,
516
# optionally verbosely
518
commands.append(PrintTableCmd(verbose=options.verbose))
274
523
class Command(object):
275
524
"""Abstract class for commands"""
276
def run(self, mandos, clients):
525
def run(self, clients, bus=None, mandos=None):
277
526
"""Normal commands should implement run_on_one_client(), but
278
527
commands which want to operate on all clients at the same time
279
528
can override this run() method instead."""
280
529
self.mandos = mandos
281
for client in clients:
282
self.run_on_one_client(client)
284
class PrintCmd(Command):
285
"""Abstract class for commands printing client details"""
530
for clientpath, properties in clients.items():
531
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
532
dbus_busname, str(clientpath))
533
client = bus.get_object(dbus_busname, clientpath)
534
self.run_on_one_client(client, properties)
537
class IsEnabledCmd(Command):
538
def run(self, clients, bus=None, mandos=None):
539
client, properties = next(iter(clients.items()))
540
if self.is_enabled(client, properties):
543
def is_enabled(self, client, properties):
544
return properties["Enabled"]
547
class ApproveCmd(Command):
548
def run_on_one_client(self, client, properties):
549
log.debug("D-Bus: %s:%s:%s.Approve(True)", dbus_busname,
550
client.__dbus_object_path__, client_dbus_interface)
551
client.Approve(dbus.Boolean(True),
552
dbus_interface=client_dbus_interface)
555
class DenyCmd(Command):
556
def run_on_one_client(self, client, properties):
557
log.debug("D-Bus: %s:%s:%s.Approve(False)", dbus_busname,
558
client.__dbus_object_path__, client_dbus_interface)
559
client.Approve(dbus.Boolean(False),
560
dbus_interface=client_dbus_interface)
563
class RemoveCmd(Command):
564
def run_on_one_client(self, client, properties):
565
log.debug("D-Bus: %s:%s:%s.RemoveClient(%r)", dbus_busname,
566
server_dbus_path, server_dbus_interface,
567
str(client.__dbus_object_path__))
568
self.mandos.RemoveClient(client.__dbus_object_path__)
571
class OutputCmd(Command):
572
"""Abstract class for commands outputting client details"""
286
573
all_keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
287
574
"Created", "Interval", "Host", "KeyID",
288
575
"Fingerprint", "CheckerRunning", "LastEnabled",
403
683
**{key: self.string_from_client(client, key)
404
684
for key in self.keywords})
408
class DumpJSONCmd(PrintCmd):
409
def output(self, clients):
410
data = {client["Name"]:
411
{key: self.dbus_boolean_to_bool(client[key])
412
for key in self.all_keywords}
413
for client in clients.values()}
414
return json.dumps(data, indent=4, separators=(',', ': '))
416
def dbus_boolean_to_bool(value):
417
if isinstance(value, dbus.Boolean):
421
class IsEnabledCmd(Command):
422
def run_on_one_client(self, client):
423
if self.is_enabled(client):
426
def is_enabled(self, client):
427
return client.Get(client_interface, "Enabled",
428
dbus_interface=dbus.PROPERTIES_IFACE)
430
class RemoveCmd(Command):
431
def run_on_one_client(self, client):
432
self.mandos.RemoveClient(client.__dbus_object_path__)
434
class ApproveCmd(Command):
435
def run_on_one_client(self, client):
436
client.Approve(dbus.Boolean(True),
437
dbus_interface=client_interface)
439
class DenyCmd(Command):
440
def run_on_one_client(self, client):
441
client.Approve(dbus.Boolean(False),
442
dbus_interface=client_interface)
687
def milliseconds_to_string(ms):
688
td = datetime.timedelta(0, 0, 0, ms)
689
return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
690
.format(days="{}T".format(td.days)
692
hours=td.seconds // 3600,
693
minutes=(td.seconds % 3600) // 60,
694
seconds=td.seconds % 60))
697
class PropertyCmd(Command):
698
"""Abstract class for Actions for setting one client property"""
699
def run_on_one_client(self, client, properties):
700
"""Set the Client's D-Bus property"""
701
log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", dbus_busname,
702
client.__dbus_object_path__,
703
dbus.PROPERTIES_IFACE, client_dbus_interface,
704
self.propname, self.value_to_set
705
if not isinstance(self.value_to_set, dbus.Boolean)
706
else bool(self.value_to_set))
707
client.Set(client_dbus_interface, self.propname,
709
dbus_interface=dbus.PROPERTIES_IFACE)
712
raise NotImplementedError()
444
715
class EnableCmd(PropertyCmd):
446
717
value_to_set = dbus.Boolean(True)
448
720
class DisableCmd(PropertyCmd):
450
722
value_to_set = dbus.Boolean(False)
452
725
class BumpTimeoutCmd(PropertyCmd):
453
property = "LastCheckedOK"
726
propname = "LastCheckedOK"
454
727
value_to_set = ""
456
730
class StartCheckerCmd(PropertyCmd):
457
property = "CheckerRunning"
731
propname = "CheckerRunning"
458
732
value_to_set = dbus.Boolean(True)
460
735
class StopCheckerCmd(PropertyCmd):
461
property = "CheckerRunning"
736
propname = "CheckerRunning"
462
737
value_to_set = dbus.Boolean(False)
464
740
class ApproveByDefaultCmd(PropertyCmd):
465
property = "ApprovedByDefault"
741
propname = "ApprovedByDefault"
466
742
value_to_set = dbus.Boolean(True)
468
745
class DenyByDefaultCmd(PropertyCmd):
469
property = "ApprovedByDefault"
746
propname = "ApprovedByDefault"
470
747
value_to_set = dbus.Boolean(False)
472
class SetCheckerCmd(PropertyCmd, ValueArgumentMixIn):
475
class SetHostCmd(PropertyCmd, ValueArgumentMixIn):
478
class SetSecretCmd(PropertyCmd, ValueArgumentMixIn):
481
class SetTimeoutCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
484
class SetExtendedTimeoutCmd(PropertyCmd,
485
MillisecondsValueArgumentMixIn):
486
property = "ExtendedTimeout"
488
class SetIntervalCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
489
property = "Interval"
491
class SetApprovalDelayCmd(PropertyCmd,
492
MillisecondsValueArgumentMixIn):
493
property = "ApprovalDelay"
495
class SetApprovalDurationCmd(PropertyCmd,
496
MillisecondsValueArgumentMixIn):
497
property = "ApprovalDuration"
499
def has_actions(options):
500
return any((options.enable,
502
options.bump_timeout,
503
options.start_checker,
504
options.stop_checker,
507
options.checker is not None,
508
options.timeout is not None,
509
options.extended_timeout is not None,
510
options.interval is not None,
511
options.approved_by_default is not None,
512
options.approval_delay is not None,
513
options.approval_duration is not None,
514
options.host is not None,
515
options.secret is not None,
519
def add_command_line_options(parser):
520
parser.add_argument("--version", action="version",
521
version="%(prog)s {}".format(version),
522
help="show version number and exit")
523
parser.add_argument("-a", "--all", action="store_true",
524
help="Select all clients")
525
parser.add_argument("-v", "--verbose", action="store_true",
526
help="Print all fields")
527
parser.add_argument("-j", "--dump-json", action="store_true",
528
help="Dump client data in JSON format")
529
enable_disable = parser.add_mutually_exclusive_group()
530
enable_disable.add_argument("-e", "--enable", action="store_true",
531
help="Enable client")
532
enable_disable.add_argument("-d", "--disable",
534
help="disable client")
535
parser.add_argument("-b", "--bump-timeout", action="store_true",
536
help="Bump timeout for client")
537
start_stop_checker = parser.add_mutually_exclusive_group()
538
start_stop_checker.add_argument("--start-checker",
540
help="Start checker for client")
541
start_stop_checker.add_argument("--stop-checker",
543
help="Stop checker for client")
544
parser.add_argument("-V", "--is-enabled", action="store_true",
545
help="Check if client is enabled")
546
parser.add_argument("-r", "--remove", action="store_true",
547
help="Remove client")
548
parser.add_argument("-c", "--checker",
549
help="Set checker command for client")
550
parser.add_argument("-t", "--timeout",
551
help="Set timeout for client")
552
parser.add_argument("--extended-timeout",
553
help="Set extended timeout for client")
554
parser.add_argument("-i", "--interval",
555
help="Set checker interval for client")
556
approve_deny_default = parser.add_mutually_exclusive_group()
557
approve_deny_default.add_argument(
558
"--approve-by-default", action="store_true",
559
default=None, dest="approved_by_default",
560
help="Set client to be approved by default")
561
approve_deny_default.add_argument(
562
"--deny-by-default", action="store_false",
563
dest="approved_by_default",
564
help="Set client to be denied by default")
565
parser.add_argument("--approval-delay",
566
help="Set delay before client approve/deny")
567
parser.add_argument("--approval-duration",
568
help="Set duration of one client approval")
569
parser.add_argument("-H", "--host", help="Set host for client")
570
parser.add_argument("-s", "--secret",
571
type=argparse.FileType(mode="rb"),
572
help="Set password blob (file) for client")
573
approve_deny = parser.add_mutually_exclusive_group()
574
approve_deny.add_argument(
575
"-A", "--approve", action="store_true",
576
help="Approve any current client request")
577
approve_deny.add_argument("-D", "--deny", action="store_true",
578
help="Deny any current client request")
579
parser.add_argument("--check", action="store_true",
580
help="Run self-test")
581
parser.add_argument("client", nargs="*", help="Client name")
584
def commands_and_clients_from_options(options):
588
if options.dump_json:
589
commands.append(DumpJSONCmd())
592
commands.append(EnableCmd())
595
commands.append(DisableCmd())
597
if options.bump_timeout:
598
commands.append(BumpTimeoutCmd(options.bump_timeout))
600
if options.start_checker:
601
commands.append(StartCheckerCmd())
603
if options.stop_checker:
604
commands.append(StopCheckerCmd())
606
if options.is_enabled:
607
commands.append(IsEnabledCmd())
610
commands.append(RemoveCmd())
612
if options.checker is not None:
613
commands.append(SetCheckerCmd())
615
if options.timeout is not None:
616
commands.append(SetTimeoutCmd(options.timeout))
618
if options.extended_timeout:
620
SetExtendedTimeoutCmd(options.extended_timeout))
622
if options.interval is not None:
623
command.append(SetIntervalCmd(options.interval))
625
if options.approved_by_default is not None:
626
if options.approved_by_default:
627
command.append(ApproveByDefaultCmd())
629
command.append(DenyByDefaultCmd())
631
if options.approval_delay is not None:
632
command.append(SetApprovalDelayCmd(options.approval_delay))
634
if options.approval_duration is not None:
636
SetApprovalDurationCmd(options.approval_duration))
638
if options.host is not None:
639
command.append(SetHostCmd(options.host))
641
if options.secret is not None:
642
command.append(SetSecretCmd(options.secret))
645
commands.append(ApproveCmd())
648
commands.append(DenyCmd())
650
# If no command option has been given, show table of clients,
651
# optionally verbosely
653
commands.append(PrintTableCmd(verbose=options.verbose))
655
return commands, options.client
659
parser = argparse.ArgumentParser()
661
add_command_line_options(parser)
663
options = parser.parse_args()
665
if has_actions(options) and not (options.client or options.all):
666
parser.error("Options require clients names or --all.")
667
if options.verbose and has_actions(options):
668
parser.error("--verbose can only be used alone.")
669
if options.dump_json and (options.verbose
670
or has_actions(options)):
671
parser.error("--dump-json can only be used alone.")
672
if options.all and not has_actions(options):
673
parser.error("--all requires an action.")
674
if options.is_enabled and len(options.client) > 1:
675
parser.error("--is-enabled requires exactly one client")
677
commands, clientnames = commands_and_clients_from_options(options)
680
bus = dbus.SystemBus()
681
mandos_dbus_objc = bus.get_object(busname, server_path)
682
except dbus.exceptions.DBusException:
683
log.critical("Could not connect to Mandos server")
686
mandos_serv = dbus.Interface(mandos_dbus_objc,
687
dbus_interface=server_interface)
688
mandos_serv_object_manager = dbus.Interface(
689
mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
691
# Filter out log message from dbus module
692
dbus_logger = logging.getLogger("dbus.proxies")
693
class NullFilter(logging.Filter):
694
def filter(self, record):
696
dbus_filter = NullFilter()
698
dbus_logger.addFilter(dbus_filter)
699
mandos_clients = {path: ifs_and_props[client_interface]
700
for path, ifs_and_props in
701
mandos_serv_object_manager
702
.GetManagedObjects().items()
703
if client_interface in ifs_and_props}
704
except dbus.exceptions.DBusException as e:
705
log.critical("Failed to access Mandos server through D-Bus:"
709
# restore dbus logger
710
dbus_logger.removeFilter(dbus_filter)
712
# Compile dict of (clients: properties) to process
716
clients = {bus.get_object(busname, path): properties
717
for path, properties in mandos_clients.items()}
719
for name in clientnames:
720
for path, client in mandos_clients.items():
721
if client["Name"] == name:
722
client_objc = bus.get_object(busname, path)
723
clients[client_objc] = client
726
log.critical("Client not found on server: %r", name)
729
# Run all commands on clients
730
for command in commands:
731
command.run(mandos_serv, clients)
750
class PropertyValueCmd(PropertyCmd):
751
"""Abstract class for PropertyCmd recieving a value as argument"""
752
def __init__(self, value):
753
self.value_to_set = value
756
class SetCheckerCmd(PropertyValueCmd):
760
class SetHostCmd(PropertyValueCmd):
764
class SetSecretCmd(PropertyValueCmd):
767
def value_to_set(self):
770
def value_to_set(self, value):
771
"""When setting, read data from supplied file object"""
772
self._vts = value.read()
776
class MillisecondsPropertyValueArgumentCmd(PropertyValueCmd):
777
"""Abstract class for PropertyValueCmd taking a value argument as
778
a datetime.timedelta() but should store it as milliseconds."""
780
def value_to_set(self):
783
def value_to_set(self, value):
784
"""When setting, convert value from a datetime.timedelta"""
785
self._vts = int(round(value.total_seconds() * 1000))
788
class SetTimeoutCmd(MillisecondsPropertyValueArgumentCmd):
792
class SetExtendedTimeoutCmd(MillisecondsPropertyValueArgumentCmd):
793
propname = "ExtendedTimeout"
796
class SetIntervalCmd(MillisecondsPropertyValueArgumentCmd):
797
propname = "Interval"
800
class SetApprovalDelayCmd(MillisecondsPropertyValueArgumentCmd):
801
propname = "ApprovalDelay"
804
class SetApprovalDurationCmd(MillisecondsPropertyValueArgumentCmd):
805
propname = "ApprovalDuration"
734
class Test_milliseconds_to_string(unittest.TestCase):
736
self.assertEqual(milliseconds_to_string(93785000),
738
def test_no_days(self):
739
self.assertEqual(milliseconds_to_string(7385000), "02:03:05")
740
def test_all_zero(self):
741
self.assertEqual(milliseconds_to_string(0), "00:00:00")
742
def test_no_fractional_seconds(self):
743
self.assertEqual(milliseconds_to_string(400), "00:00:00")
744
self.assertEqual(milliseconds_to_string(900), "00:00:00")
745
self.assertEqual(milliseconds_to_string(1900), "00:00:01")
747
809
class Test_string_to_delta(unittest.TestCase):
748
810
def test_handles_basic_rfc3339(self):
811
self.assertEqual(string_to_delta("PT0S"),
812
datetime.timedelta())
813
self.assertEqual(string_to_delta("P0D"),
814
datetime.timedelta())
815
self.assertEqual(string_to_delta("PT1S"),
816
datetime.timedelta(0, 1))
749
817
self.assertEqual(string_to_delta("PT2H"),
750
818
datetime.timedelta(0, 7200))
751
819
def test_falls_back_to_pre_1_6_1_with_warning(self):
771
839
self.assertEqual(value, datetime.timedelta(0, 7200))
842
class Test_check_option_syntax(unittest.TestCase):
843
# This mostly corresponds to the definition from has_actions() in
844
# check_option_syntax()
846
# The actual values set here are not that important, but we do
847
# at least stick to the correct types, even though they are
851
"bump_timeout": True,
852
"start_checker": True,
853
"stop_checker": True,
857
"timeout": datetime.timedelta(),
858
"extended_timeout": datetime.timedelta(),
859
"interval": datetime.timedelta(),
860
"approved_by_default": True,
861
"approval_delay": datetime.timedelta(),
862
"approval_duration": datetime.timedelta(),
864
"secret": io.BytesIO(b"x"),
870
self.parser = argparse.ArgumentParser()
871
add_command_line_options(self.parser)
873
@contextlib.contextmanager
874
def assertParseError(self):
875
with self.assertRaises(SystemExit) as e:
876
with self.temporarily_suppress_stderr():
878
# Exit code from argparse is guaranteed to be "2". Reference:
879
# https://docs.python.org/3/library
880
# /argparse.html#exiting-methods
881
self.assertEqual(e.exception.code, 2)
884
@contextlib.contextmanager
885
def temporarily_suppress_stderr():
886
null = os.open(os.path.devnull, os.O_RDWR)
887
stderrcopy = os.dup(sys.stderr.fileno())
888
os.dup2(null, sys.stderr.fileno())
894
os.dup2(stderrcopy, sys.stderr.fileno())
897
def check_option_syntax(self, options):
898
check_option_syntax(self.parser, options)
900
def test_actions_requires_client_or_all(self):
901
for action, value in self.actions.items():
902
options = self.parser.parse_args()
903
setattr(options, action, value)
904
with self.assertParseError():
905
self.check_option_syntax(options)
907
def test_actions_conflicts_with_verbose(self):
908
for action, value in self.actions.items():
909
options = self.parser.parse_args()
910
setattr(options, action, value)
911
options.verbose = True
912
with self.assertParseError():
913
self.check_option_syntax(options)
915
def test_dump_json_conflicts_with_verbose(self):
916
options = self.parser.parse_args()
917
options.dump_json = True
918
options.verbose = True
919
with self.assertParseError():
920
self.check_option_syntax(options)
922
def test_dump_json_conflicts_with_action(self):
923
for action, value in self.actions.items():
924
options = self.parser.parse_args()
925
setattr(options, action, value)
926
options.dump_json = True
927
with self.assertParseError():
928
self.check_option_syntax(options)
930
def test_all_can_not_be_alone(self):
931
options = self.parser.parse_args()
933
with self.assertParseError():
934
self.check_option_syntax(options)
936
def test_all_is_ok_with_any_action(self):
937
for action, value in self.actions.items():
938
options = self.parser.parse_args()
939
setattr(options, action, value)
941
self.check_option_syntax(options)
943
def test_is_enabled_fails_without_client(self):
944
options = self.parser.parse_args()
945
options.is_enabled = True
946
with self.assertParseError():
947
self.check_option_syntax(options)
949
def test_is_enabled_works_with_one_client(self):
950
options = self.parser.parse_args()
951
options.is_enabled = True
952
options.client = ["foo"]
953
self.check_option_syntax(options)
955
def test_is_enabled_fails_with_two_clients(self):
956
options = self.parser.parse_args()
957
options.is_enabled = True
958
options.client = ["foo", "barbar"]
959
with self.assertParseError():
960
self.check_option_syntax(options)
962
def test_remove_can_only_be_combined_with_action_deny(self):
963
for action, value in self.actions.items():
964
if action in {"remove", "deny"}:
966
options = self.parser.parse_args()
967
setattr(options, action, value)
969
options.remove = True
970
with self.assertParseError():
971
self.check_option_syntax(options)
974
class Test_command_from_options(unittest.TestCase):
976
self.parser = argparse.ArgumentParser()
977
add_command_line_options(self.parser)
978
def assert_command_from_args(self, args, command_cls,
980
"""Assert that parsing ARGS should result in an instance of
981
COMMAND_CLS with (optionally) all supplied attributes (CMD_ATTRS)."""
982
options = self.parser.parse_args(args)
983
check_option_syntax(self.parser, options)
984
commands = commands_from_options(options)
985
self.assertEqual(len(commands), 1)
986
command = commands[0]
987
self.assertIsInstance(command, command_cls)
988
for key, value in cmd_attrs.items():
989
self.assertEqual(getattr(command, key), value)
990
def test_print_table(self):
991
self.assert_command_from_args([], PrintTableCmd,
994
def test_print_table_verbose(self):
995
self.assert_command_from_args(["--verbose"], PrintTableCmd,
998
def test_print_table_verbose_short(self):
999
self.assert_command_from_args(["-v"], PrintTableCmd,
1002
def test_enable(self):
1003
self.assert_command_from_args(["--enable", "foo"], EnableCmd)
1005
def test_enable_short(self):
1006
self.assert_command_from_args(["-e", "foo"], EnableCmd)
1008
def test_disable(self):
1009
self.assert_command_from_args(["--disable", "foo"],
1012
def test_disable_short(self):
1013
self.assert_command_from_args(["-d", "foo"], DisableCmd)
1015
def test_bump_timeout(self):
1016
self.assert_command_from_args(["--bump-timeout", "foo"],
1019
def test_bump_timeout_short(self):
1020
self.assert_command_from_args(["-b", "foo"], BumpTimeoutCmd)
1022
def test_start_checker(self):
1023
self.assert_command_from_args(["--start-checker", "foo"],
1026
def test_stop_checker(self):
1027
self.assert_command_from_args(["--stop-checker", "foo"],
1030
def test_remove(self):
1031
self.assert_command_from_args(["--remove", "foo"],
1034
def test_remove_short(self):
1035
self.assert_command_from_args(["-r", "foo"], RemoveCmd)
1037
def test_checker(self):
1038
self.assert_command_from_args(["--checker", ":", "foo"],
1039
SetCheckerCmd, value_to_set=":")
1041
def test_checker_empty(self):
1042
self.assert_command_from_args(["--checker", "", "foo"],
1043
SetCheckerCmd, value_to_set="")
1045
def test_checker_short(self):
1046
self.assert_command_from_args(["-c", ":", "foo"],
1047
SetCheckerCmd, value_to_set=":")
1049
def test_timeout(self):
1050
self.assert_command_from_args(["--timeout", "PT5M", "foo"],
1052
value_to_set=300000)
1054
def test_timeout_short(self):
1055
self.assert_command_from_args(["-t", "PT5M", "foo"],
1057
value_to_set=300000)
1059
def test_extended_timeout(self):
1060
self.assert_command_from_args(["--extended-timeout", "PT15M",
1062
SetExtendedTimeoutCmd,
1063
value_to_set=900000)
1065
def test_interval(self):
1066
self.assert_command_from_args(["--interval", "PT2M", "foo"],
1068
value_to_set=120000)
1070
def test_interval_short(self):
1071
self.assert_command_from_args(["-i", "PT2M", "foo"],
1073
value_to_set=120000)
1075
def test_approve_by_default(self):
1076
self.assert_command_from_args(["--approve-by-default", "foo"],
1077
ApproveByDefaultCmd)
1079
def test_deny_by_default(self):
1080
self.assert_command_from_args(["--deny-by-default", "foo"],
1083
def test_approval_delay(self):
1084
self.assert_command_from_args(["--approval-delay", "PT30S",
1085
"foo"], SetApprovalDelayCmd,
1088
def test_approval_duration(self):
1089
self.assert_command_from_args(["--approval-duration", "PT1S",
1090
"foo"], SetApprovalDurationCmd,
1093
def test_host(self):
1094
self.assert_command_from_args(["--host", "foo.example.org",
1096
value_to_set="foo.example.org")
1098
def test_host_short(self):
1099
self.assert_command_from_args(["-H", "foo.example.org",
1101
value_to_set="foo.example.org")
1103
def test_secret_devnull(self):
1104
self.assert_command_from_args(["--secret", os.path.devnull,
1105
"foo"], SetSecretCmd,
1108
def test_secret_tempfile(self):
1109
with tempfile.NamedTemporaryFile(mode="r+b") as f:
1110
value = b"secret\0xyzzy\nbar"
1113
self.assert_command_from_args(["--secret", f.name,
1114
"foo"], SetSecretCmd,
1117
def test_secret_devnull_short(self):
1118
self.assert_command_from_args(["-s", os.path.devnull, "foo"],
1119
SetSecretCmd, value_to_set=b"")
1121
def test_secret_tempfile_short(self):
1122
with tempfile.NamedTemporaryFile(mode="r+b") as f:
1123
value = b"secret\0xyzzy\nbar"
1126
self.assert_command_from_args(["-s", f.name, "foo"],
1130
def test_approve(self):
1131
self.assert_command_from_args(["--approve", "foo"],
1134
def test_approve_short(self):
1135
self.assert_command_from_args(["-A", "foo"], ApproveCmd)
1137
def test_deny(self):
1138
self.assert_command_from_args(["--deny", "foo"], DenyCmd)
1140
def test_deny_short(self):
1141
self.assert_command_from_args(["-D", "foo"], DenyCmd)
1143
def test_dump_json(self):
1144
self.assert_command_from_args(["--dump-json"], DumpJSONCmd)
1146
def test_is_enabled(self):
1147
self.assert_command_from_args(["--is-enabled", "foo"],
1150
def test_is_enabled_short(self):
1151
self.assert_command_from_args(["-V", "foo"], IsEnabledCmd)
1153
def test_deny_before_remove(self):
1154
options = self.parser.parse_args(["--deny", "--remove",
1156
check_option_syntax(self.parser, options)
1157
commands = commands_from_options(options)
1158
self.assertEqual(len(commands), 2)
1159
self.assertIsInstance(commands[0], DenyCmd)
1160
self.assertIsInstance(commands[1], RemoveCmd)
1162
def test_deny_before_remove_reversed(self):
1163
options = self.parser.parse_args(["--remove", "--deny",
1165
check_option_syntax(self.parser, options)
1166
commands = commands_from_options(options)
1167
self.assertEqual(len(commands), 2)
1168
self.assertIsInstance(commands[0], DenyCmd)
1169
self.assertIsInstance(commands[1], RemoveCmd)
774
1172
class TestCmd(unittest.TestCase):
775
1173
"""Abstract class for tests of command classes"""
776
1174
def setUp(self):
778
1176
class MockClient(object):
779
1177
def __init__(self, name, **attributes):
780
self.__dbus_object_path__ = "objpath_{}".format(name)
1178
self.__dbus_object_path__ = "/clients/{}".format(name)
781
1179
self.attributes = attributes
782
1180
self.attributes["Name"] = name
784
def Set(self, interface, property, value, dbus_interface):
785
testcase.assertEqual(interface, client_interface)
786
testcase.assertEqual(dbus_interface,
787
dbus.PROPERTIES_IFACE)
788
self.attributes[property] = value
789
self.calls.append(("Set", (interface, property, value,
791
def Get(self, interface, property, dbus_interface):
792
testcase.assertEqual(interface, client_interface)
793
testcase.assertEqual(dbus_interface,
794
dbus.PROPERTIES_IFACE)
795
self.calls.append(("Get", (interface, property,
797
return self.attributes[property]
798
def __getitem__(self, key):
799
return self.attributes[key]
800
def __setitem__(self, key, value):
801
self.attributes[key] = value
802
self.clients = collections.OrderedDict([
806
KeyID=("92ed150794387c03ce684574b1139a65"
807
"94a34f895daaaf09fd8ea90a27cddb12"),
809
Host="foo.example.org",
810
Enabled=dbus.Boolean(True),
812
LastCheckedOK="2019-02-03T00:00:00",
813
Created="2019-01-02T00:00:00",
815
Fingerprint=("778827225BA7DE539C5A"
816
"7CFA59CFF7CDBD9A5920"),
817
CheckerRunning=dbus.Boolean(False),
818
LastEnabled="2019-01-03T00:00:00",
819
ApprovalPending=dbus.Boolean(False),
820
ApprovedByDefault=dbus.Boolean(True),
821
LastApprovalRequest="",
823
ApprovalDuration=1000,
824
Checker="fping -q -- %(host)s",
825
ExtendedTimeout=900000,
826
Expires="2019-02-04T00:00:00",
827
LastCheckerStatus=0)),
831
KeyID=("0558568eedd67d622f5c83b35a115f79"
832
"6ab612cff5ad227247e46c2b020f441c"),
835
Enabled=dbus.Boolean(True),
837
LastCheckedOK="2019-02-04T00:00:00",
838
Created="2019-01-03T00:00:00",
840
Fingerprint=("3E393AEAEFB84C7E89E2"
841
"F547B3A107558FCA3A27"),
842
CheckerRunning=dbus.Boolean(True),
843
LastEnabled="2019-01-04T00:00:00",
844
ApprovalPending=dbus.Boolean(False),
845
ApprovedByDefault=dbus.Boolean(False),
846
LastApprovalRequest="2019-01-03T00:00:00",
848
ApprovalDuration=1000,
850
ExtendedTimeout=900000,
851
Expires="2019-02-05T00:00:00",
852
LastCheckerStatus=-2)),
1182
def Set(self, interface, propname, value, dbus_interface):
1183
testcase.assertEqual(interface, client_dbus_interface)
1184
testcase.assertEqual(dbus_interface,
1185
dbus.PROPERTIES_IFACE)
1186
self.attributes[propname] = value
1187
def Get(self, interface, propname, dbus_interface):
1188
testcase.assertEqual(interface, client_dbus_interface)
1189
testcase.assertEqual(dbus_interface,
1190
dbus.PROPERTIES_IFACE)
1191
return self.attributes[propname]
1192
def Approve(self, approve, dbus_interface):
1193
testcase.assertEqual(dbus_interface,
1194
client_dbus_interface)
1195
self.calls.append(("Approve", (approve,
1197
self.client = MockClient(
1199
KeyID=("92ed150794387c03ce684574b1139a65"
1200
"94a34f895daaaf09fd8ea90a27cddb12"),
1202
Host="foo.example.org",
1203
Enabled=dbus.Boolean(True),
1205
LastCheckedOK="2019-02-03T00:00:00",
1206
Created="2019-01-02T00:00:00",
1208
Fingerprint=("778827225BA7DE539C5A"
1209
"7CFA59CFF7CDBD9A5920"),
1210
CheckerRunning=dbus.Boolean(False),
1211
LastEnabled="2019-01-03T00:00:00",
1212
ApprovalPending=dbus.Boolean(False),
1213
ApprovedByDefault=dbus.Boolean(True),
1214
LastApprovalRequest="",
1216
ApprovalDuration=1000,
1217
Checker="fping -q -- %(host)s",
1218
ExtendedTimeout=900000,
1219
Expires="2019-02-04T00:00:00",
1220
LastCheckerStatus=0)
1221
self.other_client = MockClient(
1223
KeyID=("0558568eedd67d622f5c83b35a115f79"
1224
"6ab612cff5ad227247e46c2b020f441c"),
1225
Secret=b"secretbar",
1227
Enabled=dbus.Boolean(True),
1229
LastCheckedOK="2019-02-04T00:00:00",
1230
Created="2019-01-03T00:00:00",
1232
Fingerprint=("3E393AEAEFB84C7E89E2"
1233
"F547B3A107558FCA3A27"),
1234
CheckerRunning=dbus.Boolean(True),
1235
LastEnabled="2019-01-04T00:00:00",
1236
ApprovalPending=dbus.Boolean(False),
1237
ApprovedByDefault=dbus.Boolean(False),
1238
LastApprovalRequest="2019-01-03T00:00:00",
1239
ApprovalDelay=30000,
1240
ApprovalDuration=93785000,
1242
ExtendedTimeout=900000,
1243
Expires="2019-02-05T00:00:00",
1244
LastCheckerStatus=-2)
1245
self.clients = collections.OrderedDict(
1247
("/clients/foo", self.client.attributes),
1248
("/clients/barbar", self.other_client.attributes),
855
class TestPrintTableCmd(TestCmd):
856
def test_normal(self):
857
output = PrintTableCmd().output(self.clients)
858
expected_output = """
859
Name Enabled Timeout Last Successful Check
860
foo Yes 00:05:00 2019-02-03T00:00:00
861
barbar Yes 00:05:00 2019-02-04T00:00:00
863
self.assertEqual(output, expected_output)
864
def test_verbose(self):
865
output = PrintTableCmd(verbose=True).output(self.clients)
866
expected_output = """
867
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
868
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
869
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
871
self.assertEqual(output, expected_output)
872
def test_one_client(self):
873
output = PrintTableCmd().output({"foo": self.clients["foo"]})
874
expected_output = """
875
Name Enabled Timeout Last Successful Check
876
foo Yes 00:05:00 2019-02-03T00:00:00
878
self.assertEqual(output, expected_output)
1250
self.one_client = {"/clients/foo": self.client.attributes}
1255
def get_object(client_bus_name, path):
1256
self.assertEqual(client_bus_name, dbus_busname)
1258
"/clients/foo": self.client,
1259
"/clients/barbar": self.other_client,
1264
class TestIsEnabledCmd(TestCmd):
1265
def test_is_enabled(self):
1266
self.assertTrue(all(IsEnabledCmd().is_enabled(client,
1268
for client, properties
1269
in self.clients.items()))
1270
def test_is_enabled_run_exits_successfully(self):
1271
with self.assertRaises(SystemExit) as e:
1272
IsEnabledCmd().run(self.one_client)
1273
if e.exception.code is not None:
1274
self.assertEqual(e.exception.code, 0)
1276
self.assertIsNone(e.exception.code)
1277
def test_is_enabled_run_exits_with_failure(self):
1278
self.client.attributes["Enabled"] = dbus.Boolean(False)
1279
with self.assertRaises(SystemExit) as e:
1280
IsEnabledCmd().run(self.one_client)
1281
if isinstance(e.exception.code, int):
1282
self.assertNotEqual(e.exception.code, 0)
1284
self.assertIsNotNone(e.exception.code)
1287
class TestApproveCmd(TestCmd):
1288
def test_approve(self):
1289
ApproveCmd().run(self.clients, self.bus)
1290
for clientpath in self.clients:
1291
client = self.bus.get_object(dbus_busname, clientpath)
1292
self.assertIn(("Approve", (True, client_dbus_interface)),
1296
class TestDenyCmd(TestCmd):
1297
def test_deny(self):
1298
DenyCmd().run(self.clients, self.bus)
1299
for clientpath in self.clients:
1300
client = self.bus.get_object(dbus_busname, clientpath)
1301
self.assertIn(("Approve", (False, client_dbus_interface)),
1304
class TestRemoveCmd(TestCmd):
1305
def test_remove(self):
1306
class MockMandos(object):
1309
def RemoveClient(self, dbus_path):
1310
self.calls.append(("RemoveClient", (dbus_path,)))
1311
mandos = MockMandos()
1312
super(TestRemoveCmd, self).setUp()
1313
RemoveCmd().run(self.clients, self.bus, mandos)
1314
self.assertEqual(len(mandos.calls), 2)
1315
for clientpath in self.clients:
1316
self.assertIn(("RemoveClient", (clientpath,)),
880
1320
class TestDumpJSONCmd(TestCmd):
881
1321
def setUp(self):
934
1374
json_data = json.loads(DumpJSONCmd().output(self.clients))
935
1375
self.assertDictEqual(json_data, self.expected_json)
936
1376
def test_one_client(self):
937
clients = {"foo": self.clients["foo"]}
1377
clients = self.one_client
938
1378
json_data = json.loads(DumpJSONCmd().output(clients))
939
1379
expected_json = {"foo": self.expected_json["foo"]}
940
1380
self.assertDictEqual(json_data, expected_json)
942
class TestIsEnabledCmd(TestCmd):
943
def test_is_enabled(self):
944
self.assertTrue(all(IsEnabledCmd().is_enabled(client)
945
for client in self.clients.values()))
946
def test_is_enabled_does_get_attribute(self):
947
client = self.clients["foo"]
948
self.assertTrue(IsEnabledCmd().is_enabled(client))
949
self.assertListEqual(client.calls,
951
("se.recompile.Mandos.Client",
953
"org.freedesktop.DBus.Properties"))])
954
def test_is_enabled_run_exits_successfully(self):
955
client = self.clients["foo"]
956
with self.assertRaises(SystemExit) as e:
957
IsEnabledCmd().run(None, [client])
958
if e.exception.code is not None:
959
self.assertEqual(e.exception.code, 0)
961
self.assertIsNone(e.exception.code)
962
def test_is_enabled_run_exits_with_failure(self):
963
client = self.clients["foo"]
964
client["Enabled"] = dbus.Boolean(False)
965
with self.assertRaises(SystemExit) as e:
966
IsEnabledCmd().run(None, [client])
967
if isinstance(e.exception.code, int):
968
self.assertNotEqual(e.exception.code, 0)
970
self.assertIsNotNone(e.exception.code)
1383
class TestPrintTableCmd(TestCmd):
1384
def test_normal(self):
1385
output = PrintTableCmd().output(self.clients.values())
1386
expected_output = "\n".join((
1387
"Name Enabled Timeout Last Successful Check",
1388
"foo Yes 00:05:00 2019-02-03T00:00:00 ",
1389
"barbar Yes 00:05:00 2019-02-04T00:00:00 ",
1391
self.assertEqual(output, expected_output)
1392
def test_verbose(self):
1393
output = PrintTableCmd(verbose=True).output(
1394
self.clients.values())
1409
"Last Successful Check ",
1410
"2019-02-03T00:00:00 ",
1411
"2019-02-04T00:00:00 ",
1414
"2019-01-02T00:00:00 ",
1415
"2019-01-03T00:00:00 ",
1427
("92ed150794387c03ce684574b1139a6594a34f895daaaf09fd8"
1429
("0558568eedd67d622f5c83b35a115f796ab612cff5ad227247e"
1433
"778827225BA7DE539C5A7CFA59CFF7CDBD9A5920 ",
1434
"3E393AEAEFB84C7E89E2F547B3A107558FCA3A27 ",
1436
"Check Is Running ",
1441
"2019-01-03T00:00:00 ",
1442
"2019-01-04T00:00:00 ",
1444
"Approval Is Pending ",
1448
"Approved By Default ",
1452
"Last Approval Request ",
1454
"2019-01-03T00:00:00 ",
1460
"Approval Duration ",
1465
"fping -q -- %(host)s ",
1468
"Extended Timeout ",
1473
"2019-02-04T00:00:00 ",
1474
"2019-02-05T00:00:00 ",
1476
"Last Checker Status",
1481
num_lines = max(len(rows) for rows in columns)
1482
expected_output = "\n".join("".join(rows[line]
1483
for rows in columns)
1484
for line in range(num_lines))
1485
self.assertEqual(output, expected_output)
1486
def test_one_client(self):
1487
output = PrintTableCmd().output(self.one_client.values())
1488
expected_output = "\n".join((
1489
"Name Enabled Timeout Last Successful Check",
1490
"foo Yes 00:05:00 2019-02-03T00:00:00 ",
1492
self.assertEqual(output, expected_output)
1495
class Unique(object):
1496
"""Class for objects which exist only to be unique objects, since
1497
unittest.mock.sentinel only exists in Python 3.3"""
1500
class TestPropertyCmd(TestCmd):
1501
"""Abstract class for tests of PropertyCmd classes"""
1503
if not hasattr(self, "command"):
1505
values_to_get = getattr(self, "values_to_get",
1507
for value_to_set, value_to_get in zip(self.values_to_set,
1509
for clientpath in self.clients:
1510
client = self.bus.get_object(dbus_busname, clientpath)
1511
old_value = client.attributes[self.propname]
1512
self.assertNotIsInstance(old_value, Unique)
1513
client.attributes[self.propname] = Unique()
1514
self.run_command(value_to_set, self.clients)
1515
for clientpath in self.clients:
1516
client = self.bus.get_object(dbus_busname, clientpath)
1517
value = client.attributes[self.propname]
1518
self.assertNotIsInstance(value, Unique)
1519
self.assertEqual(value, value_to_get)
1520
def run_command(self, value, clients):
1521
self.command().run(clients, self.bus)
1524
class TestEnableCmd(TestPropertyCmd):
1526
propname = "Enabled"
1527
values_to_set = [dbus.Boolean(True)]
1530
class TestDisableCmd(TestPropertyCmd):
1531
command = DisableCmd
1532
propname = "Enabled"
1533
values_to_set = [dbus.Boolean(False)]
1536
class TestBumpTimeoutCmd(TestPropertyCmd):
1537
command = BumpTimeoutCmd
1538
propname = "LastCheckedOK"
1539
values_to_set = [""]
1542
class TestStartCheckerCmd(TestPropertyCmd):
1543
command = StartCheckerCmd
1544
propname = "CheckerRunning"
1545
values_to_set = [dbus.Boolean(True)]
1548
class TestStopCheckerCmd(TestPropertyCmd):
1549
command = StopCheckerCmd
1550
propname = "CheckerRunning"
1551
values_to_set = [dbus.Boolean(False)]
1554
class TestApproveByDefaultCmd(TestPropertyCmd):
1555
command = ApproveByDefaultCmd
1556
propname = "ApprovedByDefault"
1557
values_to_set = [dbus.Boolean(True)]
1560
class TestDenyByDefaultCmd(TestPropertyCmd):
1561
command = DenyByDefaultCmd
1562
propname = "ApprovedByDefault"
1563
values_to_set = [dbus.Boolean(False)]
1566
class TestPropertyValueCmd(TestPropertyCmd):
1567
"""Abstract class for tests of PropertyValueCmd classes"""
1569
if type(self) is TestPropertyValueCmd:
1571
return super(TestPropertyValueCmd, self).runTest()
1572
def run_command(self, value, clients):
1573
self.command(value).run(clients, self.bus)
1576
class TestSetCheckerCmd(TestPropertyValueCmd):
1577
command = SetCheckerCmd
1578
propname = "Checker"
1579
values_to_set = ["", ":", "fping -q -- %s"]
1582
class TestSetHostCmd(TestPropertyValueCmd):
1583
command = SetHostCmd
1585
values_to_set = ["192.0.2.3", "foo.example.org"]
1588
class TestSetSecretCmd(TestPropertyValueCmd):
1589
command = SetSecretCmd
1591
values_to_set = [io.BytesIO(b""),
1592
io.BytesIO(b"secret\0xyzzy\nbar")]
1593
values_to_get = [b"", b"secret\0xyzzy\nbar"]
1596
class TestSetTimeoutCmd(TestPropertyValueCmd):
1597
command = SetTimeoutCmd
1598
propname = "Timeout"
1599
values_to_set = [datetime.timedelta(),
1600
datetime.timedelta(minutes=5),
1601
datetime.timedelta(seconds=1),
1602
datetime.timedelta(weeks=1),
1603
datetime.timedelta(weeks=52)]
1604
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1607
class TestSetExtendedTimeoutCmd(TestPropertyValueCmd):
1608
command = SetExtendedTimeoutCmd
1609
propname = "ExtendedTimeout"
1610
values_to_set = [datetime.timedelta(),
1611
datetime.timedelta(minutes=5),
1612
datetime.timedelta(seconds=1),
1613
datetime.timedelta(weeks=1),
1614
datetime.timedelta(weeks=52)]
1615
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1618
class TestSetIntervalCmd(TestPropertyValueCmd):
1619
command = SetIntervalCmd
1620
propname = "Interval"
1621
values_to_set = [datetime.timedelta(),
1622
datetime.timedelta(minutes=5),
1623
datetime.timedelta(seconds=1),
1624
datetime.timedelta(weeks=1),
1625
datetime.timedelta(weeks=52)]
1626
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1629
class TestSetApprovalDelayCmd(TestPropertyValueCmd):
1630
command = SetApprovalDelayCmd
1631
propname = "ApprovalDelay"
1632
values_to_set = [datetime.timedelta(),
1633
datetime.timedelta(minutes=5),
1634
datetime.timedelta(seconds=1),
1635
datetime.timedelta(weeks=1),
1636
datetime.timedelta(weeks=52)]
1637
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1640
class TestSetApprovalDurationCmd(TestPropertyValueCmd):
1641
command = SetApprovalDurationCmd
1642
propname = "ApprovalDuration"
1643
values_to_set = [datetime.timedelta(),
1644
datetime.timedelta(minutes=5),
1645
datetime.timedelta(seconds=1),
1646
datetime.timedelta(weeks=1),
1647
datetime.timedelta(weeks=52)]
1648
values_to_get = [0, 300000, 1000, 604800000, 31449600000]