4
4
# Mandos Monitor - Control and monitor the Mandos server
6
# Copyright © 2009-2012 Teddy Hogeborn
7
# Copyright © 2009-2012 Björn Påhlsson
6
# Copyright © 2009-2013 Teddy Hogeborn
7
# Copyright © 2009-2013 Björn Påhlsson
9
9
# This program is free software: you can redistribute it and/or modify
10
10
# it under the terms of the GNU General Public License as published by
53
60
domain = 'se.recompile'
54
61
server_interface = domain + '.Mandos'
55
62
client_interface = domain + '.Mandos.Client'
58
# Always run in monochrome mode
59
urwid.curses_display.curses.has_colors = lambda : False
61
# Urwid doesn't support blinking, but we want it. Since we have no
62
# use for underline on its own, we make underline also always blink.
63
urwid.curses_display.curses.A_UNDERLINE |= (
64
urwid.curses_display.curses.A_BLINK)
66
65
def isoformat_to_datetime(iso):
67
66
"Parse an ISO 8601 date string to a datetime.datetime()"
84
83
properties and calls a hook function when any of them are
87
def __init__(self, proxy_object=None, *args, **kwargs):
86
def __init__(self, proxy_object=None, properties=None, **kwargs):
88
87
self.proxy = proxy_object # Mandos Client proxy object
90
self.properties = dict()
88
self.properties = dict() if properties is None else properties
91
89
self.property_changed_match = (
92
90
self.proxy.connect_to_signal("PropertyChanged",
93
self.property_changed,
91
self._property_changed,
97
self.properties.update(
98
self.proxy.GetAll(client_interface,
99
dbus_interface = dbus.PROPERTIES_IFACE))
101
#XXX This breaks good super behaviour
102
# super(MandosClientPropertyCache, self).__init__(
95
if properties is None:
96
self.properties.update(
97
self.proxy.GetAll(client_interface,
99
= dbus.PROPERTIES_IFACE))
101
super(MandosClientPropertyCache, self).__init__(**kwargs)
103
def _property_changed(self, property, value):
104
"""Helper which takes positional arguments"""
105
return self.property_changed(property=property, value=value)
105
107
def property_changed(self, property=None, value=None):
106
108
"""This is called whenever we get a PropertyChanged signal
109
111
# Update properties dict with new value
110
112
self.properties[property] = value
112
def delete(self, *args, **kwargs):
113
115
self.property_changed_match.remove()
114
super(MandosClientPropertyCache, self).__init__(
118
118
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
122
122
def __init__(self, server_proxy_object=None, update_hook=None,
123
delete_hook=None, logger=None, *args, **kwargs):
123
delete_hook=None, logger=None, **kwargs):
124
124
# Called on update
125
125
self.update_hook = update_hook
126
126
# Called on delete
131
131
self.logger = logger
133
133
self._update_timer_callback_tag = None
134
self._update_timer_callback_lock = 0
136
135
# The widget shown normally
137
136
self._text_widget = urwid.Text("")
138
137
# The widget shown when we have focus
139
138
self._focus_text_widget = urwid.Text("")
140
super(MandosClientWidget, self).__init__(
141
update_hook=update_hook, delete_hook=delete_hook,
139
super(MandosClientWidget, self).__init__(**kwargs)
144
141
self.opened = False
146
last_checked_ok = isoformat_to_datetime(self.properties
149
if self.properties ["LastCheckerStatus"] != 0:
150
self.using_timer(True)
152
if self.need_approval:
153
self.using_timer(True)
155
143
self.match_objects = (
156
144
self.proxy.connect_to_signal("CheckerCompleted",
157
145
self.checker_completed,
176
164
#self.logger('Created client {0}'
177
165
# .format(self.properties["Name"]))
179
def property_changed(self, property=None, value=None):
180
super(self, MandosClientWidget).property_changed(property,
182
if property == "ApprovalPending":
183
using_timer(bool(value))
184
if property == "LastCheckerStatus":
185
using_timer(value != 0)
186
#self.logger('Checker for client {0} (command "{1}") was '
187
# ' successful'.format(self.properties["Name"],
190
167
def using_timer(self, flag):
191
168
"""Call this method with True or False when timer should be
192
169
activated or deactivated.
194
old = self._update_timer_callback_lock
196
self._update_timer_callback_lock += 1
198
self._update_timer_callback_lock -= 1
199
if old == 0 and self._update_timer_callback_lock:
171
if flag and self._update_timer_callback_tag is None:
200
172
# Will update the shown timer value every second
201
173
self._update_timer_callback_tag = (gobject.timeout_add
203
175
self.update_timer))
204
elif old and self._update_timer_callback_lock == 0:
176
elif not (flag or self._update_timer_callback_tag is None):
205
177
gobject.source_remove(self._update_timer_callback_tag)
206
178
self._update_timer_callback_tag = None
281
252
"bold-underline-blink":
282
253
"bold-underline-blink-standout",
285
256
# Rebuild focus and non-focus widgets using current properties
287
258
# Base part of a client. Name!
288
259
base = '{name}: '.format(name=self.properties["Name"])
289
260
if not self.properties["Enabled"]:
290
261
message = "DISABLED"
262
self.using_timer(False)
291
263
elif self.properties["ApprovalPending"]:
292
264
timeout = datetime.timedelta(milliseconds
293
265
= self.properties
295
267
last_approval_request = isoformat_to_datetime(
296
268
self.properties["LastApprovalRequest"])
297
269
if last_approval_request is not None:
298
timer = timeout - (datetime.datetime.utcnow()
299
- last_approval_request)
270
timer = max(timeout - (datetime.datetime.utcnow()
271
- last_approval_request),
272
datetime.timedelta())
301
274
timer = datetime.timedelta()
302
275
if self.properties["ApprovedByDefault"]:
303
276
message = "Approval in {0}. (d)eny?"
305
278
message = "Denial in {0}. (a)pprove?"
306
message = message.format(unicode(timer).rsplit(".", 1)[0])
279
message = message.format(str(timer).rsplit(".", 1)[0])
280
self.using_timer(True)
307
281
elif self.properties["LastCheckerStatus"] != 0:
308
282
# When checker has failed, show timer until client expires
309
283
expires = self.properties["Expires"]
313
287
expires = (datetime.datetime.strptime
314
288
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
315
timer = expires - datetime.datetime.utcnow()
289
timer = max(expires - datetime.datetime.utcnow(),
290
datetime.timedelta())
316
291
message = ('A checker has failed! Time until client'
317
292
' gets disabled: {0}'
318
.format(unicode(timer).rsplit(".", 1)[0]))
293
.format(str(timer).rsplit(".", 1)[0]))
294
self.using_timer(True)
320
296
message = "enabled"
297
self.using_timer(False)
321
298
self._text = "{0}{1}".format(base, message)
323
300
if not urwid.supports_unicode():
324
301
self._text = self._text.encode("ascii", "replace")
325
302
textlist = [("normal", self._text)]
343
320
return True # Keep calling this
345
def delete(self, *args, **kwargs):
322
def delete(self, **kwargs):
346
323
if self._update_timer_callback_tag is not None:
347
324
gobject.source_remove(self._update_timer_callback_tag)
348
325
self._update_timer_callback_tag = None
351
328
self.match_objects = ()
352
329
if self.delete_hook is not None:
353
330
self.delete_hook(self)
354
return super(MandosClientWidget, self).delete(*args, **kwargs)
331
return super(MandosClientWidget, self).delete(**kwargs)
356
333
def render(self, maxcolrow, focus=False):
357
334
"""Render differently if we have focus.
402
def property_changed(self, property=None, value=None,
379
def property_changed(self, property=None, **kwargs):
404
380
"""Call self.update() if old value is not new value.
405
381
This overrides the method from MandosClientPropertyCache"""
406
property_name = unicode(property)
382
property_name = str(property)
407
383
old_value = self.properties.get(property_name)
408
384
super(MandosClientWidget, self).property_changed(
409
property=property, value=value, *args, **kwargs)
385
property=property, **kwargs)
410
386
if self.properties.get(property_name) != old_value:
416
392
"down" key presses, thus not allowing any containing widgets to
417
393
use them as an excuse to shift focus away from this widget.
419
def keypress(self, maxcolrow, key):
420
ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
395
def keypress(self, *args, **kwargs):
396
ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
421
397
if ret in ("up", "down"):
437
413
"default", "default", None),
439
"default", "default", "bold"),
415
"bold", "default", "bold"),
440
416
("underline-blink",
441
"default", "default", "underline"),
417
"underline,blink", "default", "underline,blink"),
443
"default", "default", "standout"),
419
"standout", "default", "standout"),
444
420
("bold-underline-blink",
445
"default", "default", ("bold", "underline")),
421
"bold,underline,blink", "default", "bold,underline,blink"),
446
422
("bold-standout",
447
"default", "default", ("bold", "standout")),
423
"bold,standout", "default", "bold,standout"),
448
424
("underline-blink-standout",
449
"default", "default", ("underline", "standout")),
425
"underline,blink,standout", "default",
426
"underline,blink,standout"),
450
427
("bold-underline-blink-standout",
451
"default", "default", ("bold", "underline",
428
"bold,underline,blink,standout", "default",
429
"bold,underline,blink,standout"),
455
432
if urwid.supports_unicode():
606
579
mandos_clients = (self.mandos_serv
607
580
.GetAllClientsWithProperties())
581
if not mandos_clients:
582
self.log_message_raw(("bold", "Note: Server has no clients."))
608
583
except dbus.exceptions.DBusException:
584
self.log_message_raw(("bold", "Note: No Mandos server running."))
609
585
mandos_clients = dbus.Dictionary()
611
587
(self.mandos_serv
623
599
self.client_not_found,
624
600
dbus_interface=server_interface,
625
601
byte_arrays=True))
626
for path, client in mandos_clients.iteritems():
602
for path, client in mandos_clients.items():
627
603
client_proxy_object = self.bus.get_object(self.busname,
629
605
self.add_client(MandosClientWidget(server_proxy_object