102
102
mandos_serv_object_manager = dbus.Interface(
103
103
mandos_dbus_object, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
105
managed_objects = get_managed_objects(mandos_serv_object_manager)
108
for path, ifs_and_props in managed_objects.items():
110
all_clients[path] = ifs_and_props[client_dbus_interface]
114
# Compile dict of (clientpath: properties) to process
106
log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", dbus_busname,
107
server_dbus_path, dbus.OBJECT_MANAGER_IFACE)
108
with SilenceLogger("dbus.proxies"):
109
all_clients = {path: ifs_and_props[client_dbus_interface]
110
for path, ifs_and_props in
111
mandos_serv_object_manager
112
.GetManagedObjects().items()
113
if client_dbus_interface in ifs_and_props}
114
except dbus.exceptions.DBusException as e:
115
log.critical("Failed to access Mandos server through D-Bus:"
119
# Compile dict of (clients: properties) to process
115
122
if not clientnames:
116
123
clients = all_clients
119
125
for name in clientnames:
120
126
for objpath, properties in all_clients.items():
121
127
if properties["Name"] == name:
232
238
>>> rfc3339_duration_to_delta("")
233
239
Traceback (most recent call last):
235
ValueError: Invalid RFC 3339 duration: ""
241
ValueError: Invalid RFC 3339 duration: u''
236
242
>>> # Must start with "P":
237
243
>>> rfc3339_duration_to_delta("1D")
238
244
Traceback (most recent call last):
240
ValueError: Invalid RFC 3339 duration: "1D"
246
ValueError: Invalid RFC 3339 duration: u'1D'
241
247
>>> # Must use correct order
242
248
>>> rfc3339_duration_to_delta("PT1S2M")
243
249
Traceback (most recent call last):
245
ValueError: Invalid RFC 3339 duration: "PT1S2M"
251
ValueError: Invalid RFC 3339 duration: u'PT1S2M'
246
252
>>> # Time needs time marker
247
253
>>> rfc3339_duration_to_delta("P1H2S")
248
254
Traceback (most recent call last):
250
ValueError: Invalid RFC 3339 duration: "P1H2S"
256
ValueError: Invalid RFC 3339 duration: u'P1H2S'
251
257
>>> # Weeks can not be combined with anything else
252
258
>>> rfc3339_duration_to_delta("P1D2W")
253
259
Traceback (most recent call last):
255
ValueError: Invalid RFC 3339 duration: "P1D2W"
261
ValueError: Invalid RFC 3339 duration: u'P1D2W'
256
262
>>> rfc3339_duration_to_delta("P2W2H")
257
263
Traceback (most recent call last):
259
ValueError: Invalid RFC 3339 duration: "P2W2H"
265
ValueError: Invalid RFC 3339 duration: u'P2W2H'
262
268
# Parsing an RFC 3339 duration with regular expressions is not
427
433
def get_mandos_dbus_object(bus):
428
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
429
dbus_busname, server_dbus_path)
430
with if_dbus_exception_log_with_exception_and_exit(
431
"Could not connect to Mandos server: %s"):
435
log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
436
dbus_busname, server_dbus_path)
432
437
mandos_dbus_object = bus.get_object(dbus_busname,
433
438
server_dbus_path)
439
except dbus.exceptions.DBusException:
440
log.critical("Could not connect to Mandos server")
434
443
return mandos_dbus_object
437
@contextlib.contextmanager
438
def if_dbus_exception_log_with_exception_and_exit(*args, **kwargs):
441
except dbus.exceptions.DBusException as e:
442
log.critical(*(args + (e,)), **kwargs)
446
def get_managed_objects(object_manager):
447
log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", dbus_busname,
448
server_dbus_path, dbus.OBJECT_MANAGER_IFACE)
449
with if_dbus_exception_log_with_exception_and_exit(
450
"Failed to access Mandos server through D-Bus:\n%s"):
451
with SilenceLogger("dbus.proxies"):
452
managed_objects = object_manager.GetManagedObjects()
453
return managed_objects
456
446
class SilenceLogger(object):
457
447
"Simple context manager to silence a particular logger"
458
448
def __init__(self, loggername):
842
class TestCaseWithAssertLogs(unittest.TestCase):
843
"""unittest.TestCase.assertLogs only exists in Python 3.4"""
845
if not hasattr(unittest.TestCase, "assertLogs"):
846
@contextlib.contextmanager
847
def assertLogs(self, logger, level=logging.INFO):
848
capturing_handler = self.CapturingLevelHandler(level)
849
old_level = logger.level
850
old_propagate = logger.propagate
851
logger.addHandler(capturing_handler)
852
logger.setLevel(level)
853
logger.propagate = False
855
yield capturing_handler.watcher
857
logger.propagate = old_propagate
858
logger.removeHandler(capturing_handler)
859
logger.setLevel(old_level)
860
self.assertGreater(len(capturing_handler.watcher.records),
863
class CapturingLevelHandler(logging.Handler):
864
def __init__(self, level, *args, **kwargs):
865
logging.Handler.__init__(self, *args, **kwargs)
866
self.watcher = self.LoggingWatcher([], [])
867
def emit(self, record):
868
self.watcher.records.append(record)
869
self.watcher.output.append(self.format(record))
871
LoggingWatcher = collections.namedtuple("LoggingWatcher",
876
class Test_string_to_delta(TestCaseWithAssertLogs):
832
class Test_string_to_delta(unittest.TestCase):
877
833
def test_handles_basic_rfc3339(self):
878
834
self.assertEqual(string_to_delta("PT0S"),
879
835
datetime.timedelta())
885
841
datetime.timedelta(0, 7200))
887
843
def test_falls_back_to_pre_1_6_1_with_warning(self):
888
with self.assertLogs(log, logging.WARNING):
889
value = string_to_delta("2h")
844
# assertLogs only exists in Python 3.4
845
if hasattr(self, "assertLogs"):
846
with self.assertLogs(log, logging.WARNING):
847
value = string_to_delta("2h")
849
class WarningFilter(logging.Filter):
850
"""Don't show, but record the presence of, warnings"""
851
def filter(self, record):
852
is_warning = record.levelno >= logging.WARNING
853
self.found = is_warning or getattr(self, "found",
855
return not is_warning
856
warning_filter = WarningFilter()
857
log.addFilter(warning_filter)
859
value = string_to_delta("2h")
861
log.removeFilter(warning_filter)
862
self.assertTrue(getattr(warning_filter, "found", False))
890
863
self.assertEqual(value, datetime.timedelta(0, 7200))
955
928
def check_option_syntax(self, options):
956
929
check_option_syntax(self.parser, options)
958
def test_actions_all_conflicts_with_verbose(self):
959
for action, value in self.actions.items():
960
options = self.parser.parse_args()
961
setattr(options, action, value)
963
options.verbose = True
964
with self.assertParseError():
965
self.check_option_syntax(options)
967
def test_actions_with_client_conflicts_with_verbose(self):
968
for action, value in self.actions.items():
969
options = self.parser.parse_args()
970
setattr(options, action, value)
971
options.verbose = True
972
options.client = ["foo"]
931
def test_actions_conflicts_with_verbose(self):
932
for action, value in self.actions.items():
933
options = self.parser.parse_args()
934
setattr(options, action, value)
935
options.verbose = True
973
936
with self.assertParseError():
974
937
self.check_option_syntax(options)
1001
964
options.all = True
1002
965
self.check_option_syntax(options)
1004
def test_any_action_is_ok_with_one_client(self):
1005
for action, value in self.actions.items():
1006
options = self.parser.parse_args()
1007
setattr(options, action, value)
1008
options.client = ["foo"]
967
def test_is_enabled_fails_without_client(self):
968
options = self.parser.parse_args()
969
options.is_enabled = True
970
with self.assertParseError():
1009
971
self.check_option_syntax(options)
1011
def test_one_client_with_all_actions_except_is_enabled(self):
973
def test_is_enabled_works_with_one_client(self):
1012
974
options = self.parser.parse_args()
1013
for action, value in self.actions.items():
1014
if action == "is_enabled":
1016
setattr(options, action, value)
975
options.is_enabled = True
1017
976
options.client = ["foo"]
1018
977
self.check_option_syntax(options)
1020
def test_two_clients_with_all_actions_except_is_enabled(self):
1021
options = self.parser.parse_args()
1022
for action, value in self.actions.items():
1023
if action == "is_enabled":
1025
setattr(options, action, value)
1026
options.client = ["foo", "barbar"]
1027
self.check_option_syntax(options)
1029
def test_two_clients_are_ok_with_actions_except_is_enabled(self):
1030
for action, value in self.actions.items():
1031
if action == "is_enabled":
1033
options = self.parser.parse_args()
1034
setattr(options, action, value)
1035
options.client = ["foo", "barbar"]
1036
self.check_option_syntax(options)
1038
def test_is_enabled_fails_without_client(self):
1039
options = self.parser.parse_args()
1040
options.is_enabled = True
1041
with self.assertParseError():
1042
self.check_option_syntax(options)
1044
979
def test_is_enabled_fails_with_two_clients(self):
1045
980
options = self.parser.parse_args()
1046
981
options.is_enabled = True
1081
1016
def get_object(self, busname, dbus_path):
1082
1017
raise dbus.exceptions.DBusException("Test")
1084
with self.assertLogs(log, logging.CRITICAL):
1085
with self.assertRaises(SystemExit) as e:
1086
bus = get_mandos_dbus_object(bus=MockBusFailing())
1019
# assertLogs only exists in Python 3.4
1020
if hasattr(self, "assertLogs"):
1021
with self.assertLogs(log, logging.CRITICAL):
1022
with self.assertRaises(SystemExit) as e:
1023
bus = get_mandos_dbus_object(bus=MockBus())
1025
critical_filter = self.CriticalFilter()
1026
log.addFilter(critical_filter)
1028
with self.assertRaises(SystemExit) as e:
1029
get_mandos_dbus_object(bus=MockBusFailing())
1031
log.removeFilter(critical_filter)
1032
self.assertTrue(critical_filter.found)
1088
1033
if isinstance(e.exception.code, int):
1089
1034
self.assertNotEqual(e.exception.code, 0)
1091
1036
self.assertIsNotNone(e.exception.code)
1094
class Test_get_managed_objects(TestCaseWithAssertLogs):
1095
def test_calls_and_returns_GetManagedObjects(self):
1096
managed_objects = {"/clients/foo": { "Name": "foo"}}
1097
class MockObjectManager(object):
1098
def GetManagedObjects(self):
1099
return managed_objects
1100
retval = get_managed_objects(MockObjectManager())
1101
self.assertDictEqual(managed_objects, retval)
1103
def test_logs_and_exits_on_dbus_error(self):
1104
dbus_logger = logging.getLogger("dbus.proxies")
1106
class MockObjectManagerFailing(object):
1107
def GetManagedObjects(self):
1108
dbus_logger.error("Test")
1109
raise dbus.exceptions.DBusException("Test")
1111
class CountingHandler(logging.Handler):
1113
def emit(self, record):
1116
counting_handler = CountingHandler()
1118
dbus_logger.addHandler(counting_handler)
1038
class CriticalFilter(logging.Filter):
1039
"""Don't show, but register, critical messages"""
1041
def filter(self, record):
1042
is_critical = record.levelno >= logging.CRITICAL
1043
self.found = is_critical or self.found
1044
return not is_critical
1047
class Test_SilenceLogger(unittest.TestCase):
1048
loggername = "mandos-ctl.Test_SilenceLogger"
1049
log = logging.getLogger(loggername)
1050
log.propagate = False
1051
log.addHandler(logging.NullHandler())
1054
self.counting_filter = self.CountingFilter()
1056
class CountingFilter(logging.Filter):
1057
"Count number of records"
1059
def filter(self, record):
1063
def test_should_filter_records_only_when_active(self):
1121
with self.assertLogs(log, logging.CRITICAL) as watcher:
1122
with self.assertRaises(SystemExit) as e:
1123
get_managed_objects(MockObjectManagerFailing())
1065
with SilenceLogger(self.loggername):
1066
self.log.addFilter(self.counting_filter)
1067
self.log.info("Filtered log message 1")
1068
self.log.info("Non-filtered message 2")
1069
self.log.info("Non-filtered message 3")
1125
dbus_logger.removeFilter(counting_handler)
1127
# Make sure the dbus logger was suppressed
1128
self.assertEqual(counting_handler.count, 0)
1130
# Test that the dbus_logger still works
1131
with self.assertLogs(dbus_logger, logging.ERROR):
1132
dbus_logger.error("Test")
1134
if isinstance(e.exception.code, int):
1135
self.assertNotEqual(e.exception.code, 0)
1137
self.assertIsNotNone(e.exception.code)
1071
self.log.removeFilter(self.counting_filter)
1072
self.assertEqual(self.counting_filter.count, 2)
1140
1075
class Test_commands_from_options(unittest.TestCase):