276
277
# Abstract classes first
277
278
class Command(object):
278
279
"""Abstract class for commands"""
279
def run(self, mandos, clients):
280
def run(self, clients, bus=None, mandos=None):
280
281
"""Normal commands should implement run_on_one_client(), but
281
282
commands which want to operate on all clients at the same time
282
283
can override this run() method instead."""
283
284
self.mandos = mandos
284
for client, properties in clients.items():
285
for clientpath, properties in clients.items():
286
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
287
dbus_busname, str(clientpath))
288
client = bus.get_object(dbus_busname, clientpath)
285
289
self.run_on_one_client(client, properties)
287
291
class PrintCmd(Command):
293
297
"LastApprovalRequest", "ApprovalDelay",
294
298
"ApprovalDuration", "Checker", "ExtendedTimeout",
295
299
"Expires", "LastCheckerStatus")
296
def run(self, mandos, clients):
297
print(self.output(clients))
300
def run(self, clients, bus=None, mandos=None):
301
print(self.output(clients.values()))
302
def output(self, clients):
303
raise NotImplementedError()
299
305
class PropertyCmd(Command):
300
306
"""Abstract class for Actions for setting one client property"""
301
307
def run_on_one_client(self, client, properties):
302
308
"""Set the Client's D-Bus property"""
303
log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", busname,
309
log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", dbus_busname,
304
310
client.__dbus_object_path__,
305
dbus.PROPERTIES_IFACE, client_interface,
306
self.property, self.value_to_set
311
dbus.PROPERTIES_IFACE, client_dbus_interface,
312
self.propname, self.value_to_set
307
313
if not isinstance(self.value_to_set, dbus.Boolean)
308
314
else bool(self.value_to_set))
309
client.Set(client_interface, self.property, self.value_to_set,
315
client.Set(client_dbus_interface, self.propname,
310
317
dbus_interface=dbus.PROPERTIES_IFACE)
320
raise NotImplementedError()
312
class ValueArgumentMixIn(object):
313
"""Mixin class for commands taking a value as argument"""
322
class PropertyValueCmd(PropertyCmd):
323
"""Abstract class for PropertyCmd recieving a value as argument"""
314
324
def __init__(self, value):
315
325
self.value_to_set = value
317
class MillisecondsValueArgumentMixIn(ValueArgumentMixIn):
318
"""Mixin class for commands taking a value argument as
327
class MillisecondsPropertyValueArgumentCmd(PropertyValueCmd):
328
"""Abstract class for PropertyValueCmd taking a value argument as
329
a datetime.timedelta() but should store it as milliseconds."""
321
331
def value_to_set(self):
323
333
@value_to_set.setter
324
334
def value_to_set(self, value):
325
"""When setting, convert value to a datetime.timedelta"""
335
"""When setting, convert value from a datetime.timedelta"""
326
336
self._vts = int(round(value.total_seconds() * 1000))
328
338
# Actual (non-abstract) command classes
430
441
class IsEnabledCmd(Command):
431
def run_on_one_client(self, client, properties):
442
def run(self, clients, bus=None, mandos=None):
443
client, properties = next(iter(clients.items()))
432
444
if self.is_enabled(client, properties):
435
447
def is_enabled(self, client, properties):
436
return bool(properties["Enabled"])
448
return properties["Enabled"]
438
450
class RemoveCmd(Command):
439
451
def run_on_one_client(self, client, properties):
440
log.debug("D-Bus: %s:%s:%s.RemoveClient(%r)", busname,
441
server_path, server_interface,
452
log.debug("D-Bus: %s:%s:%s.RemoveClient(%r)", dbus_busname,
453
server_dbus_path, server_dbus_interface,
442
454
str(client.__dbus_object_path__))
443
455
self.mandos.RemoveClient(client.__dbus_object_path__)
445
457
class ApproveCmd(Command):
446
458
def run_on_one_client(self, client, properties):
447
log.debug("D-Bus: %s:%s.Approve(True)",
448
client.__dbus_object_path__, client_interface)
459
log.debug("D-Bus: %s:%s:%s.Approve(True)", dbus_busname,
460
client.__dbus_object_path__, client_dbus_interface)
449
461
client.Approve(dbus.Boolean(True),
450
dbus_interface=client_interface)
462
dbus_interface=client_dbus_interface)
452
464
class DenyCmd(Command):
453
465
def run_on_one_client(self, client, properties):
454
log.debug("D-Bus: %s:%s.Approve(False)",
455
client.__dbus_object_path__, client_interface)
466
log.debug("D-Bus: %s:%s:%s.Approve(False)", dbus_busname,
467
client.__dbus_object_path__, client_dbus_interface)
456
468
client.Approve(dbus.Boolean(False),
457
dbus_interface=client_interface)
469
dbus_interface=client_dbus_interface)
459
471
class EnableCmd(PropertyCmd):
461
473
value_to_set = dbus.Boolean(True)
463
475
class DisableCmd(PropertyCmd):
465
477
value_to_set = dbus.Boolean(False)
467
479
class BumpTimeoutCmd(PropertyCmd):
468
property = "LastCheckedOK"
480
propname = "LastCheckedOK"
469
481
value_to_set = ""
471
483
class StartCheckerCmd(PropertyCmd):
472
property = "CheckerRunning"
484
propname = "CheckerRunning"
473
485
value_to_set = dbus.Boolean(True)
475
487
class StopCheckerCmd(PropertyCmd):
476
property = "CheckerRunning"
488
propname = "CheckerRunning"
477
489
value_to_set = dbus.Boolean(False)
479
491
class ApproveByDefaultCmd(PropertyCmd):
480
property = "ApprovedByDefault"
492
propname = "ApprovedByDefault"
481
493
value_to_set = dbus.Boolean(True)
483
495
class DenyByDefaultCmd(PropertyCmd):
484
property = "ApprovedByDefault"
496
propname = "ApprovedByDefault"
485
497
value_to_set = dbus.Boolean(False)
487
class SetCheckerCmd(PropertyCmd, ValueArgumentMixIn):
490
class SetHostCmd(PropertyCmd, ValueArgumentMixIn):
493
class SetSecretCmd(PropertyCmd, ValueArgumentMixIn):
499
class SetCheckerCmd(PropertyValueCmd):
502
class SetHostCmd(PropertyValueCmd):
505
class SetSecretCmd(PropertyValueCmd):
495
508
def value_to_set(self):
499
512
"""When setting, read data from supplied file object"""
500
513
self._vts = value.read()
504
class SetTimeoutCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
507
class SetExtendedTimeoutCmd(PropertyCmd,
508
MillisecondsValueArgumentMixIn):
509
property = "ExtendedTimeout"
511
class SetIntervalCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
512
property = "Interval"
514
class SetApprovalDelayCmd(PropertyCmd,
515
MillisecondsValueArgumentMixIn):
516
property = "ApprovalDelay"
518
class SetApprovalDurationCmd(PropertyCmd,
519
MillisecondsValueArgumentMixIn):
520
property = "ApprovalDuration"
516
class SetTimeoutCmd(MillisecondsPropertyValueArgumentCmd):
519
class SetExtendedTimeoutCmd(MillisecondsPropertyValueArgumentCmd):
520
propname = "ExtendedTimeout"
522
class SetIntervalCmd(MillisecondsPropertyValueArgumentCmd):
523
propname = "Interval"
525
class SetApprovalDelayCmd(MillisecondsPropertyValueArgumentCmd):
526
propname = "ApprovalDelay"
528
class SetApprovalDurationCmd(MillisecondsPropertyValueArgumentCmd):
529
propname = "ApprovalDuration"
522
531
def add_command_line_options(parser):
523
532
parser.add_argument("--version", action="version",
715
729
bus = dbus.SystemBus()
716
log.debug("D-Bus: Connect to: (name=%r, path=%r)", busname,
718
mandos_dbus_objc = bus.get_object(busname, server_path)
730
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
731
dbus_busname, server_dbus_path)
732
mandos_dbus_objc = bus.get_object(dbus_busname,
719
734
except dbus.exceptions.DBusException:
720
735
log.critical("Could not connect to Mandos server")
723
738
mandos_serv = dbus.Interface(mandos_dbus_objc,
724
dbus_interface=server_interface)
739
dbus_interface=server_dbus_interface)
725
740
mandos_serv_object_manager = dbus.Interface(
726
741
mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
733
748
dbus_filter = NullFilter()
735
750
dbus_logger.addFilter(dbus_filter)
736
log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", busname,
737
server_path, dbus.OBJECT_MANAGER_IFACE)
738
mandos_clients = {path: ifs_and_props[client_interface]
751
log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", dbus_busname,
752
server_dbus_path, dbus.OBJECT_MANAGER_IFACE)
753
mandos_clients = {path: ifs_and_props[client_dbus_interface]
739
754
for path, ifs_and_props in
740
755
mandos_serv_object_manager
741
756
.GetManagedObjects().items()
742
if client_interface in ifs_and_props}
757
if client_dbus_interface in ifs_and_props}
743
758
except dbus.exceptions.DBusException as e:
744
759
log.critical("Failed to access Mandos server through D-Bus:"
754
769
if not clientnames:
755
clients = {bus.get_object(busname, path): properties
756
for path, properties in mandos_clients.items()}
770
clients = {objpath: properties
771
for objpath, properties in mandos_clients.items()}
758
773
for name in clientnames:
759
for path, client in mandos_clients.items():
760
if client["Name"] == name:
761
client_objc = bus.get_object(busname, path)
762
clients[client_objc] = client
774
for objpath, properties in mandos_clients.items():
775
if properties["Name"] == name:
776
clients[objpath] = properties
765
779
log.critical("Client not found on server: %r", name)
824
838
class MockClient(object):
825
839
def __init__(self, name, **attributes):
826
self.__dbus_object_path__ = "objpath_{}".format(name)
840
self.__dbus_object_path__ = "/clients/{}".format(name)
827
841
self.attributes = attributes
828
842
self.attributes["Name"] = name
830
def Set(self, interface, property, value, dbus_interface):
831
testcase.assertEqual(interface, client_interface)
832
testcase.assertEqual(dbus_interface,
833
dbus.PROPERTIES_IFACE)
834
self.attributes[property] = value
835
def Get(self, interface, property, dbus_interface):
836
testcase.assertEqual(interface, client_interface)
837
testcase.assertEqual(dbus_interface,
838
dbus.PROPERTIES_IFACE)
839
return self.attributes[property]
844
def Set(self, interface, propname, value, dbus_interface):
845
testcase.assertEqual(interface, client_dbus_interface)
846
testcase.assertEqual(dbus_interface,
847
dbus.PROPERTIES_IFACE)
848
self.attributes[propname] = value
849
def Get(self, interface, propname, dbus_interface):
850
testcase.assertEqual(interface, client_dbus_interface)
851
testcase.assertEqual(dbus_interface,
852
dbus.PROPERTIES_IFACE)
853
return self.attributes[propname]
840
854
def Approve(self, approve, dbus_interface):
841
testcase.assertEqual(dbus_interface, client_interface)
855
testcase.assertEqual(dbus_interface,
856
client_dbus_interface)
842
857
self.calls.append(("Approve", (approve,
843
858
dbus_interface)))
844
859
self.client = MockClient(
891
906
LastCheckerStatus=-2)
892
907
self.clients = collections.OrderedDict(
894
(self.client, self.client.attributes),
895
(self.other_client, self.other_client.attributes),
909
("/clients/foo", self.client.attributes),
910
("/clients/barbar", self.other_client.attributes),
897
self.one_client = {self.client: self.client.attributes}
912
self.one_client = {"/clients/foo": self.client.attributes}
917
def get_object(client_bus_name, path):
918
self.assertEqual(client_bus_name, dbus_busname)
920
"/clients/foo": self.client,
921
"/clients/barbar": self.other_client,
899
925
class TestPrintTableCmd(TestCmd):
900
926
def test_normal(self):
901
output = PrintTableCmd().output(self.clients)
902
expected_output = """
903
Name Enabled Timeout Last Successful Check
904
foo Yes 00:05:00 2019-02-03T00:00:00
905
barbar Yes 00:05:00 2019-02-04T00:00:00
927
output = PrintTableCmd().output(self.clients.values())
928
expected_output = "\n".join((
929
"Name Enabled Timeout Last Successful Check",
930
"foo Yes 00:05:00 2019-02-03T00:00:00 ",
931
"barbar Yes 00:05:00 2019-02-04T00:00:00 ",
907
933
self.assertEqual(output, expected_output)
908
934
def test_verbose(self):
909
output = PrintTableCmd(verbose=True).output(self.clients)
910
expected_output = """
911
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
912
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
913
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
935
output = PrintTableCmd(verbose=True).output(
936
self.clients.values())
937
expected_output = "\n".join((
938
# First line (headers)
939
"Name Enabled Timeout Last Successful Check Created "
940
" Interval Host Key ID "
942
" Check Is Running Last Enabl"
943
"ed Approval Is Pending Approved By Default Last A"
944
"pproval Request Approval Delay Approval Duration Checker"
945
" Extended Timeout Expires Last "
947
# Second line (client "foo")
948
"foo Yes 00:05:00 2019-02-03T00:00:00 2019-01-02"
949
"T00:00:00 00:02:00 foo.example.org 92ed150794387c03ce684"
950
"574b1139a6594a34f895daaaf09fd8ea90a27cddb12 778827225BA7"
951
"DE539C5A7CFA59CFF7CDBD9A5920 No 2019-01-03"
953
" 00:00:00 00:00:01 fping -"
954
"q -- %(host)s 00:15:00 2019-02-04T00:00:00 0 "
956
# Third line (client "barbar")
957
"barbar Yes 00:05:00 2019-02-04T00:00:00 2019-01-03"
958
"T00:00:00 00:02:00 192.0.2.3 0558568eedd67d622f5c8"
959
"3b35a115f796ab612cff5ad227247e46c2b020f441c 3E393AEAEFB8"
960
"4C7E89E2F547B3A107558FCA3A27 Yes 2019-01-04"
961
"T00:00:00 No No 2019-0"
962
"1-03T00:00:00 00:00:30 00:00:01 : "
963
" 00:15:00 2019-02-05T00:00:00 -2 "
915
966
self.assertEqual(output, expected_output)
916
967
def test_one_client(self):
917
output = PrintTableCmd().output(self.one_client)
968
output = PrintTableCmd().output(self.one_client.values())
918
969
expected_output = """
919
970
Name Enabled Timeout Last Successful Check
920
971
foo Yes 00:05:00 2019-02-03T00:00:00
986
1037
class TestIsEnabledCmd(TestCmd):
987
1038
def test_is_enabled(self):
988
self.assertTrue(all(IsEnabledCmd().is_enabled(client, properties)
989
for client, properties in self.clients.items()))
1039
self.assertTrue(all(IsEnabledCmd().is_enabled(client,
1041
for client, properties
1042
in self.clients.items()))
990
1043
def test_is_enabled_run_exits_successfully(self):
991
1044
with self.assertRaises(SystemExit) as e:
992
IsEnabledCmd().run(None, self.one_client)
1045
IsEnabledCmd().run(self.one_client)
993
1046
if e.exception.code is not None:
994
1047
self.assertEqual(e.exception.code, 0)
1012
1065
self.calls.append(("RemoveClient", (dbus_path,)))
1013
1066
mandos = MockMandos()
1014
1067
super(TestRemoveCmd, self).setUp()
1015
RemoveCmd().run(mandos, self.clients)
1068
RemoveCmd().run(self.clients, self.bus, mandos)
1016
1069
self.assertEqual(len(mandos.calls), 2)
1017
for client in self.clients:
1018
self.assertIn(("RemoveClient",
1019
(client.__dbus_object_path__,)),
1070
for clientpath in self.clients:
1071
self.assertIn(("RemoveClient", (clientpath,)),
1022
1074
class TestApproveCmd(TestCmd):
1023
1075
def test_approve(self):
1024
ApproveCmd().run(None, self.clients)
1025
for client in self.clients:
1026
self.assertIn(("Approve", (True, client_interface)),
1076
ApproveCmd().run(self.clients, self.bus)
1077
for clientpath in self.clients:
1078
client = self.bus.get_object(dbus_busname, clientpath)
1079
self.assertIn(("Approve", (True, client_dbus_interface)),
1029
1082
class TestDenyCmd(TestCmd):
1030
1083
def test_deny(self):
1031
DenyCmd().run(None, self.clients)
1032
for client in self.clients:
1033
self.assertIn(("Approve", (False, client_interface)),
1084
DenyCmd().run(self.clients, self.bus)
1085
for clientpath in self.clients:
1086
client = self.bus.get_object(dbus_busname, clientpath)
1087
self.assertIn(("Approve", (False, client_dbus_interface)),
1036
1090
class TestEnableCmd(TestCmd):
1037
1091
def test_enable(self):
1038
for client in self.clients:
1092
for clientpath in self.clients:
1093
client = self.bus.get_object(dbus_busname, clientpath)
1039
1094
client.attributes["Enabled"] = False
1041
EnableCmd().run(None, self.clients)
1096
EnableCmd().run(self.clients, self.bus)
1043
for client in self.clients:
1098
for clientpath in self.clients:
1099
client = self.bus.get_object(dbus_busname, clientpath)
1044
1100
self.assertTrue(client.attributes["Enabled"])
1046
1102
class TestDisableCmd(TestCmd):
1047
1103
def test_disable(self):
1048
DisableCmd().run(None, self.clients)
1050
for client in self.clients:
1104
DisableCmd().run(self.clients, self.bus)
1105
for clientpath in self.clients:
1106
client = self.bus.get_object(dbus_busname, clientpath)
1051
1107
self.assertFalse(client.attributes["Enabled"])
1053
1109
class Unique(object):
1063
1119
self.values_to_set)
1064
1120
for value_to_set, value_to_get in zip(self.values_to_set,
1065
1121
values_to_get):
1066
for client in self.clients:
1067
old_value = client.attributes[self.property]
1122
for clientpath in self.clients:
1123
client = self.bus.get_object(dbus_busname, clientpath)
1124
old_value = client.attributes[self.propname]
1068
1125
self.assertNotIsInstance(old_value, Unique)
1069
client.attributes[self.property] = Unique()
1126
client.attributes[self.propname] = Unique()
1070
1127
self.run_command(value_to_set, self.clients)
1071
for client in self.clients:
1072
value = client.attributes[self.property]
1128
for clientpath in self.clients:
1129
client = self.bus.get_object(dbus_busname, clientpath)
1130
value = client.attributes[self.propname]
1073
1131
self.assertNotIsInstance(value, Unique)
1074
1132
self.assertEqual(value, value_to_get)
1075
1133
def run_command(self, value, clients):
1076
self.command().run(None, clients)
1134
self.command().run(clients, self.bus)
1078
1136
class TestBumpTimeoutCmd(TestPropertyCmd):
1079
1137
command = BumpTimeoutCmd
1080
property = "LastCheckedOK"
1138
propname = "LastCheckedOK"
1081
1139
values_to_set = [""]
1083
1141
class TestStartCheckerCmd(TestPropertyCmd):
1084
1142
command = StartCheckerCmd
1085
property = "CheckerRunning"
1143
propname = "CheckerRunning"
1086
1144
values_to_set = [dbus.Boolean(True)]
1088
1146
class TestStopCheckerCmd(TestPropertyCmd):
1089
1147
command = StopCheckerCmd
1090
property = "CheckerRunning"
1148
propname = "CheckerRunning"
1091
1149
values_to_set = [dbus.Boolean(False)]
1093
1151
class TestApproveByDefaultCmd(TestPropertyCmd):
1094
1152
command = ApproveByDefaultCmd
1095
property = "ApprovedByDefault"
1153
propname = "ApprovedByDefault"
1096
1154
values_to_set = [dbus.Boolean(True)]
1098
1156
class TestDenyByDefaultCmd(TestPropertyCmd):
1099
1157
command = DenyByDefaultCmd
1100
property = "ApprovedByDefault"
1158
propname = "ApprovedByDefault"
1101
1159
values_to_set = [dbus.Boolean(False)]
1103
class TestValueArgumentPropertyCmd(TestPropertyCmd):
1104
"""Abstract class for tests of PropertyCmd classes using the
1105
ValueArgumentMixIn"""
1161
class TestPropertyValueCmd(TestPropertyCmd):
1162
"""Abstract class for tests of PropertyValueCmd classes"""
1106
1163
def runTest(self):
1107
if type(self) is TestValueArgumentPropertyCmd:
1164
if type(self) is TestPropertyValueCmd:
1109
return super(TestValueArgumentPropertyCmd, self).runTest()
1166
return super(TestPropertyValueCmd, self).runTest()
1110
1167
def run_command(self, value, clients):
1111
self.command(value).run(None, clients)
1168
self.command(value).run(clients, self.bus)
1113
class TestSetCheckerCmd(TestValueArgumentPropertyCmd):
1170
class TestSetCheckerCmd(TestPropertyValueCmd):
1114
1171
command = SetCheckerCmd
1115
property = "Checker"
1172
propname = "Checker"
1116
1173
values_to_set = ["", ":", "fping -q -- %s"]
1118
class TestSetHostCmd(TestValueArgumentPropertyCmd):
1175
class TestSetHostCmd(TestPropertyValueCmd):
1119
1176
command = SetHostCmd
1121
1178
values_to_set = ["192.0.2.3", "foo.example.org"]
1123
class TestSetSecretCmd(TestValueArgumentPropertyCmd):
1180
class TestSetSecretCmd(TestPropertyValueCmd):
1124
1181
command = SetSecretCmd
1126
1183
values_to_set = [io.BytesIO(b""),
1127
1184
io.BytesIO(b"secret\0xyzzy\nbar")]
1128
1185
values_to_get = [b"", b"secret\0xyzzy\nbar"]
1130
class TestSetTimeoutCmd(TestValueArgumentPropertyCmd):
1187
class TestSetTimeoutCmd(TestPropertyValueCmd):
1131
1188
command = SetTimeoutCmd
1132
property = "Timeout"
1189
propname = "Timeout"
1133
1190
values_to_set = [datetime.timedelta(),
1134
1191
datetime.timedelta(minutes=5),
1135
1192
datetime.timedelta(seconds=1),
1137
1194
datetime.timedelta(weeks=52)]
1138
1195
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1140
class TestSetExtendedTimeoutCmd(TestValueArgumentPropertyCmd):
1197
class TestSetExtendedTimeoutCmd(TestPropertyValueCmd):
1141
1198
command = SetExtendedTimeoutCmd
1142
property = "ExtendedTimeout"
1199
propname = "ExtendedTimeout"
1143
1200
values_to_set = [datetime.timedelta(),
1144
1201
datetime.timedelta(minutes=5),
1145
1202
datetime.timedelta(seconds=1),
1157
1214
datetime.timedelta(weeks=52)]
1158
1215
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1160
class TestSetApprovalDelayCmd(TestValueArgumentPropertyCmd):
1217
class TestSetApprovalDelayCmd(TestPropertyValueCmd):
1161
1218
command = SetApprovalDelayCmd
1162
property = "ApprovalDelay"
1219
propname = "ApprovalDelay"
1163
1220
values_to_set = [datetime.timedelta(),
1164
1221
datetime.timedelta(minutes=5),
1165
1222
datetime.timedelta(seconds=1),
1167
1224
datetime.timedelta(weeks=52)]
1168
1225
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1170
class TestSetApprovalDurationCmd(TestValueArgumentPropertyCmd):
1227
class TestSetApprovalDurationCmd(TestPropertyValueCmd):
1171
1228
command = SetApprovalDurationCmd
1172
property = "ApprovalDuration"
1229
propname = "ApprovalDuration"
1173
1230
values_to_set = [datetime.timedelta(),
1174
1231
datetime.timedelta(minutes=5),
1175
1232
datetime.timedelta(seconds=1),
1355
1413
def test_is_enabled_short(self):
1356
1414
self.assert_command_from_args(["-V", "foo"], IsEnabledCmd)
1416
def test_deny_before_remove(self):
1417
options = self.parser.parse_args(["--deny", "--remove",
1419
check_option_syntax(self.parser, options)
1420
commands = commands_from_options(options)
1421
self.assertEqual(len(commands), 2)
1422
self.assertIsInstance(commands[0], DenyCmd)
1423
self.assertIsInstance(commands[1], RemoveCmd)
1425
def test_deny_before_remove_reversed(self):
1426
options = self.parser.parse_args(["--remove", "--deny",
1428
check_option_syntax(self.parser, options)
1429
commands = commands_from_options(options)
1430
self.assertEqual(len(commands), 2)
1431
self.assertIsInstance(commands[0], DenyCmd)
1432
self.assertIsInstance(commands[1], RemoveCmd)
1359
1435
class Test_check_option_syntax(unittest.TestCase):
1360
1436
# This mostly corresponds to the definition from has_actions() in