277
276
# Abstract classes first
278
277
class Command(object):
279
278
"""Abstract class for commands"""
280
def run(self, clients, bus=None, mandos=None):
279
def run(self, mandos, clients):
281
280
"""Normal commands should implement run_on_one_client(), but
282
281
commands which want to operate on all clients at the same time
283
282
can override this run() method instead."""
284
283
self.mandos = mandos
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)
284
for client, properties in clients.items():
289
285
self.run_on_one_client(client, properties)
291
287
class PrintCmd(Command):
297
293
"LastApprovalRequest", "ApprovalDelay",
298
294
"ApprovalDuration", "Checker", "ExtendedTimeout",
299
295
"Expires", "LastCheckerStatus")
300
def run(self, clients, bus=None, mandos=None):
296
def run(self, mandos, clients):
301
297
print(self.output(clients.values()))
302
298
def output(self, clients):
303
299
raise NotImplementedError()
306
302
"""Abstract class for Actions for setting one client property"""
307
303
def run_on_one_client(self, client, properties):
308
304
"""Set the Client's D-Bus property"""
309
log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", dbus_busname,
305
log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", busname,
310
306
client.__dbus_object_path__,
311
dbus.PROPERTIES_IFACE, client_dbus_interface,
307
dbus.PROPERTIES_IFACE, client_interface,
312
308
self.propname, self.value_to_set
313
309
if not isinstance(self.value_to_set, dbus.Boolean)
314
310
else bool(self.value_to_set))
315
client.Set(client_dbus_interface, self.propname,
311
client.Set(client_interface, self.propname, self.value_to_set,
317
312
dbus_interface=dbus.PROPERTIES_IFACE)
319
314
def propname(self):
320
315
raise NotImplementedError()
322
class PropertyValueCmd(PropertyCmd):
323
"""Abstract class for PropertyCmd recieving a value as argument"""
317
class ValueArgumentMixIn(object):
318
"""Mixin class for commands taking a value as argument"""
324
319
def __init__(self, value):
325
320
self.value_to_set = value
327
class MillisecondsPropertyValueArgumentCmd(PropertyValueCmd):
328
"""Abstract class for PropertyValueCmd taking a value argument as
329
a datetime.timedelta() but should store it as milliseconds."""
322
class MillisecondsValueArgumentMixIn(ValueArgumentMixIn):
323
"""Mixin class for commands taking a value argument as
331
326
def value_to_set(self):
333
328
@value_to_set.setter
334
329
def value_to_set(self, value):
335
"""When setting, convert value from a datetime.timedelta"""
330
"""When setting, convert value to a datetime.timedelta"""
336
331
self._vts = int(round(value.total_seconds() * 1000))
338
333
# Actual (non-abstract) command classes
449
443
class RemoveCmd(Command):
450
444
def run_on_one_client(self, client, properties):
451
log.debug("D-Bus: %s:%s:%s.RemoveClient(%r)", dbus_busname,
452
server_dbus_path, server_dbus_interface,
445
log.debug("D-Bus: %s:%s:%s.RemoveClient(%r)", busname,
446
server_path, server_interface,
453
447
str(client.__dbus_object_path__))
454
448
self.mandos.RemoveClient(client.__dbus_object_path__)
456
450
class ApproveCmd(Command):
457
451
def run_on_one_client(self, client, properties):
458
log.debug("D-Bus: %s:%s:%s.Approve(True)", dbus_busname,
459
client.__dbus_object_path__, client_dbus_interface)
452
log.debug("D-Bus: %s:%s.Approve(True)",
453
client.__dbus_object_path__, client_interface)
460
454
client.Approve(dbus.Boolean(True),
461
dbus_interface=client_dbus_interface)
455
dbus_interface=client_interface)
463
457
class DenyCmd(Command):
464
458
def run_on_one_client(self, client, properties):
465
log.debug("D-Bus: %s:%s:%s.Approve(False)", dbus_busname,
466
client.__dbus_object_path__, client_dbus_interface)
459
log.debug("D-Bus: %s:%s.Approve(False)",
460
client.__dbus_object_path__, client_interface)
467
461
client.Approve(dbus.Boolean(False),
468
dbus_interface=client_dbus_interface)
462
dbus_interface=client_interface)
470
464
class EnableCmd(PropertyCmd):
471
465
propname = "Enabled"
495
489
propname = "ApprovedByDefault"
496
490
value_to_set = dbus.Boolean(False)
498
class SetCheckerCmd(PropertyValueCmd):
492
class SetCheckerCmd(PropertyCmd, ValueArgumentMixIn):
499
493
propname = "Checker"
501
class SetHostCmd(PropertyValueCmd):
495
class SetHostCmd(PropertyCmd, ValueArgumentMixIn):
502
496
propname = "Host"
504
class SetSecretCmd(PropertyValueCmd):
498
class SetSecretCmd(PropertyCmd, ValueArgumentMixIn):
505
499
propname = "Secret"
507
501
def value_to_set(self):
512
506
self._vts = value.read()
515
class SetTimeoutCmd(MillisecondsPropertyValueArgumentCmd):
509
class SetTimeoutCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
516
510
propname = "Timeout"
518
class SetExtendedTimeoutCmd(MillisecondsPropertyValueArgumentCmd):
512
class SetExtendedTimeoutCmd(PropertyCmd,
513
MillisecondsValueArgumentMixIn):
519
514
propname = "ExtendedTimeout"
521
class SetIntervalCmd(MillisecondsPropertyValueArgumentCmd):
516
class SetIntervalCmd(PropertyCmd, MillisecondsValueArgumentMixIn):
522
517
propname = "Interval"
524
class SetApprovalDelayCmd(MillisecondsPropertyValueArgumentCmd):
519
class SetApprovalDelayCmd(PropertyCmd,
520
MillisecondsValueArgumentMixIn):
525
521
propname = "ApprovalDelay"
527
class SetApprovalDurationCmd(MillisecondsPropertyValueArgumentCmd):
523
class SetApprovalDurationCmd(PropertyCmd,
524
MillisecondsValueArgumentMixIn):
528
525
propname = "ApprovalDuration"
530
527
def add_command_line_options(parser):
728
725
bus = dbus.SystemBus()
729
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
730
dbus_busname, server_dbus_path)
731
mandos_dbus_objc = bus.get_object(dbus_busname,
726
log.debug("D-Bus: Connect to: (name=%r, path=%r)", busname,
728
mandos_dbus_objc = bus.get_object(busname, server_path)
733
729
except dbus.exceptions.DBusException:
734
730
log.critical("Could not connect to Mandos server")
737
733
mandos_serv = dbus.Interface(mandos_dbus_objc,
738
dbus_interface=server_dbus_interface)
734
dbus_interface=server_interface)
739
735
mandos_serv_object_manager = dbus.Interface(
740
736
mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
747
743
dbus_filter = NullFilter()
749
745
dbus_logger.addFilter(dbus_filter)
750
log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", dbus_busname,
751
server_dbus_path, dbus.OBJECT_MANAGER_IFACE)
752
mandos_clients = {path: ifs_and_props[client_dbus_interface]
746
log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", busname,
747
server_path, dbus.OBJECT_MANAGER_IFACE)
748
mandos_clients = {path: ifs_and_props[client_interface]
753
749
for path, ifs_and_props in
754
750
mandos_serv_object_manager
755
751
.GetManagedObjects().items()
756
if client_dbus_interface in ifs_and_props}
752
if client_interface in ifs_and_props}
757
753
except dbus.exceptions.DBusException as e:
758
754
log.critical("Failed to access Mandos server through D-Bus:"
768
764
if not clientnames:
769
clients = {objpath: properties
770
for objpath, properties in mandos_clients.items()}
765
clients = {(log.debug("D-Bus: Connect to: (name=%r, path=%r)",
766
busname, str(path)) and False) or
767
bus.get_object(busname, path): properties
768
for path, properties in mandos_clients.items()}
772
770
for name in clientnames:
773
for objpath, properties in mandos_clients.items():
774
if properties["Name"] == name:
775
clients[objpath] = properties
771
for path, client in mandos_clients.items():
772
if client["Name"] == name:
773
log.debug("D-Bus: Connect to: (name=%r, path=%r)",
775
client_objc = bus.get_object(busname, path)
776
clients[client_objc] = client
778
779
log.critical("Client not found on server: %r", name)
837
838
class MockClient(object):
838
839
def __init__(self, name, **attributes):
839
self.__dbus_object_path__ = "/clients/{}".format(name)
840
self.__dbus_object_path__ = "objpath_{}".format(name)
840
841
self.attributes = attributes
841
842
self.attributes["Name"] = name
843
844
def Set(self, interface, propname, value, dbus_interface):
844
testcase.assertEqual(interface, client_dbus_interface)
845
testcase.assertEqual(interface, client_interface)
845
846
testcase.assertEqual(dbus_interface,
846
847
dbus.PROPERTIES_IFACE)
847
848
self.attributes[propname] = value
848
849
def Get(self, interface, propname, dbus_interface):
849
testcase.assertEqual(interface, client_dbus_interface)
850
testcase.assertEqual(interface, client_interface)
850
851
testcase.assertEqual(dbus_interface,
851
852
dbus.PROPERTIES_IFACE)
852
853
return self.attributes[propname]
853
854
def Approve(self, approve, dbus_interface):
854
testcase.assertEqual(dbus_interface,
855
client_dbus_interface)
855
testcase.assertEqual(dbus_interface, client_interface)
856
856
self.calls.append(("Approve", (approve,
857
857
dbus_interface)))
858
858
self.client = MockClient(
905
905
LastCheckerStatus=-2)
906
906
self.clients = collections.OrderedDict(
908
("/clients/foo", self.client.attributes),
909
("/clients/barbar", self.other_client.attributes),
908
(self.client, self.client.attributes),
909
(self.other_client, self.other_client.attributes),
911
self.one_client = {"/clients/foo": self.client.attributes}
916
def get_object(client_bus_name, path):
917
self.assertEqual(client_bus_name, dbus_busname)
919
"/clients/foo": self.client,
920
"/clients/barbar": self.other_client,
911
self.one_client = {self.client: self.client.attributes}
924
913
class TestPrintTableCmd(TestCmd):
925
914
def test_normal(self):
1038
1027
self.calls.append(("RemoveClient", (dbus_path,)))
1039
1028
mandos = MockMandos()
1040
1029
super(TestRemoveCmd, self).setUp()
1041
RemoveCmd().run(self.clients, self.bus, mandos)
1030
RemoveCmd().run(mandos, self.clients)
1042
1031
self.assertEqual(len(mandos.calls), 2)
1043
for clientpath in self.clients:
1044
self.assertIn(("RemoveClient", (clientpath,)),
1032
for client in self.clients:
1033
self.assertIn(("RemoveClient",
1034
(client.__dbus_object_path__,)),
1047
1037
class TestApproveCmd(TestCmd):
1048
1038
def test_approve(self):
1049
ApproveCmd().run(self.clients, self.bus)
1050
for clientpath in self.clients:
1051
client = self.bus.get_object(dbus_busname, clientpath)
1052
self.assertIn(("Approve", (True, client_dbus_interface)),
1039
ApproveCmd().run(None, self.clients)
1040
for client in self.clients:
1041
self.assertIn(("Approve", (True, client_interface)),
1055
1044
class TestDenyCmd(TestCmd):
1056
1045
def test_deny(self):
1057
DenyCmd().run(self.clients, self.bus)
1058
for clientpath in self.clients:
1059
client = self.bus.get_object(dbus_busname, clientpath)
1060
self.assertIn(("Approve", (False, client_dbus_interface)),
1046
DenyCmd().run(None, self.clients)
1047
for client in self.clients:
1048
self.assertIn(("Approve", (False, client_interface)),
1063
1051
class TestEnableCmd(TestCmd):
1064
1052
def test_enable(self):
1065
for clientpath in self.clients:
1066
client = self.bus.get_object(dbus_busname, clientpath)
1053
for client in self.clients:
1067
1054
client.attributes["Enabled"] = False
1069
EnableCmd().run(self.clients, self.bus)
1056
EnableCmd().run(None, self.clients)
1071
for clientpath in self.clients:
1072
client = self.bus.get_object(dbus_busname, clientpath)
1058
for client in self.clients:
1073
1059
self.assertTrue(client.attributes["Enabled"])
1075
1061
class TestDisableCmd(TestCmd):
1076
1062
def test_disable(self):
1077
DisableCmd().run(self.clients, self.bus)
1078
for clientpath in self.clients:
1079
client = self.bus.get_object(dbus_busname, clientpath)
1063
DisableCmd().run(None, self.clients)
1065
for client in self.clients:
1080
1066
self.assertFalse(client.attributes["Enabled"])
1082
1068
class Unique(object):
1092
1078
self.values_to_set)
1093
1079
for value_to_set, value_to_get in zip(self.values_to_set,
1094
1080
values_to_get):
1095
for clientpath in self.clients:
1096
client = self.bus.get_object(dbus_busname, clientpath)
1081
for client in self.clients:
1097
1082
old_value = client.attributes[self.propname]
1098
1083
self.assertNotIsInstance(old_value, Unique)
1099
1084
client.attributes[self.propname] = Unique()
1100
1085
self.run_command(value_to_set, self.clients)
1101
for clientpath in self.clients:
1102
client = self.bus.get_object(dbus_busname, clientpath)
1086
for client in self.clients:
1103
1087
value = client.attributes[self.propname]
1104
1088
self.assertNotIsInstance(value, Unique)
1105
1089
self.assertEqual(value, value_to_get)
1106
1090
def run_command(self, value, clients):
1107
self.command().run(clients, self.bus)
1091
self.command().run(None, clients)
1109
1093
class TestBumpTimeoutCmd(TestPropertyCmd):
1110
1094
command = BumpTimeoutCmd
1131
1115
propname = "ApprovedByDefault"
1132
1116
values_to_set = [dbus.Boolean(False)]
1134
class TestPropertyValueCmd(TestPropertyCmd):
1135
"""Abstract class for tests of PropertyValueCmd classes"""
1118
class TestValueArgumentPropertyCmd(TestPropertyCmd):
1119
"""Abstract class for tests of PropertyCmd classes using the
1120
ValueArgumentMixIn"""
1136
1121
def runTest(self):
1137
if type(self) is TestPropertyValueCmd:
1122
if type(self) is TestValueArgumentPropertyCmd:
1139
return super(TestPropertyValueCmd, self).runTest()
1124
return super(TestValueArgumentPropertyCmd, self).runTest()
1140
1125
def run_command(self, value, clients):
1141
self.command(value).run(clients, self.bus)
1126
self.command(value).run(None, clients)
1143
class TestSetCheckerCmd(TestPropertyValueCmd):
1128
class TestSetCheckerCmd(TestValueArgumentPropertyCmd):
1144
1129
command = SetCheckerCmd
1145
1130
propname = "Checker"
1146
1131
values_to_set = ["", ":", "fping -q -- %s"]
1148
class TestSetHostCmd(TestPropertyValueCmd):
1133
class TestSetHostCmd(TestValueArgumentPropertyCmd):
1149
1134
command = SetHostCmd
1150
1135
propname = "Host"
1151
1136
values_to_set = ["192.0.2.3", "foo.example.org"]
1153
class TestSetSecretCmd(TestPropertyValueCmd):
1138
class TestSetSecretCmd(TestValueArgumentPropertyCmd):
1154
1139
command = SetSecretCmd
1155
1140
propname = "Secret"
1156
1141
values_to_set = [io.BytesIO(b""),
1157
1142
io.BytesIO(b"secret\0xyzzy\nbar")]
1158
1143
values_to_get = [b"", b"secret\0xyzzy\nbar"]
1160
class TestSetTimeoutCmd(TestPropertyValueCmd):
1145
class TestSetTimeoutCmd(TestValueArgumentPropertyCmd):
1161
1146
command = SetTimeoutCmd
1162
1147
propname = "Timeout"
1163
1148
values_to_set = [datetime.timedelta(),
1167
1152
datetime.timedelta(weeks=52)]
1168
1153
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1170
class TestSetExtendedTimeoutCmd(TestPropertyValueCmd):
1155
class TestSetExtendedTimeoutCmd(TestValueArgumentPropertyCmd):
1171
1156
command = SetExtendedTimeoutCmd
1172
1157
propname = "ExtendedTimeout"
1173
1158
values_to_set = [datetime.timedelta(),
1187
1172
datetime.timedelta(weeks=52)]
1188
1173
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1190
class TestSetApprovalDelayCmd(TestPropertyValueCmd):
1175
class TestSetApprovalDelayCmd(TestValueArgumentPropertyCmd):
1191
1176
command = SetApprovalDelayCmd
1192
1177
propname = "ApprovalDelay"
1193
1178
values_to_set = [datetime.timedelta(),
1197
1182
datetime.timedelta(weeks=52)]
1198
1183
values_to_get = [0, 300000, 1000, 604800000, 31449600000]
1200
class TestSetApprovalDurationCmd(TestPropertyValueCmd):
1185
class TestSetApprovalDurationCmd(TestValueArgumentPropertyCmd):
1201
1186
command = SetApprovalDurationCmd
1202
1187
propname = "ApprovalDuration"
1203
1188
values_to_set = [datetime.timedelta(),