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
61
domain = 'se.recompile'
54
62
server_interface = domain + '.Mandos'
55
63
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
66
def isoformat_to_datetime(iso):
67
67
"Parse an ISO 8601 date string to a datetime.datetime()"
84
84
properties and calls a hook function when any of them are
87
def __init__(self, proxy_object=None, *args, **kwargs):
87
def __init__(self, proxy_object=None, properties=None, **kwargs):
88
88
self.proxy = proxy_object # Mandos Client proxy object
90
self.properties = dict()
89
self.properties = dict() if properties is None else properties
91
90
self.property_changed_match = (
92
91
self.proxy.connect_to_signal("PropertyChanged",
93
self.property_changed,
92
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__(
96
if properties is None:
97
self.properties.update(
98
self.proxy.GetAll(client_interface,
100
= dbus.PROPERTIES_IFACE))
102
super(MandosClientPropertyCache, self).__init__(**kwargs)
104
def _property_changed(self, property, value):
105
"""Helper which takes positional arguments"""
106
return self.property_changed(property=property, value=value)
105
108
def property_changed(self, property=None, value=None):
106
109
"""This is called whenever we get a PropertyChanged signal
109
112
# Update properties dict with new value
110
113
self.properties[property] = value
112
def delete(self, *args, **kwargs):
113
116
self.property_changed_match.remove()
114
super(MandosClientPropertyCache, self).__init__(
118
119
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
122
123
def __init__(self, server_proxy_object=None, update_hook=None,
123
delete_hook=None, logger=None, *args, **kwargs):
124
delete_hook=None, logger=None, **kwargs):
124
125
# Called on update
125
126
self.update_hook = update_hook
126
127
# Called on delete
131
132
self.logger = logger
133
134
self._update_timer_callback_tag = None
134
self._update_timer_callback_lock = 0
136
136
# The widget shown normally
137
137
self._text_widget = urwid.Text("")
138
138
# The widget shown when we have focus
139
139
self._focus_text_widget = urwid.Text("")
140
super(MandosClientWidget, self).__init__(
141
update_hook=update_hook, delete_hook=delete_hook,
140
super(MandosClientWidget, self).__init__(**kwargs)
144
142
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
144
self.match_objects = (
156
145
self.proxy.connect_to_signal("CheckerCompleted",
157
146
self.checker_completed,
176
165
#self.logger('Created client {0}'
177
166
# .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
168
def using_timer(self, flag):
191
169
"""Call this method with True or False when timer should be
192
170
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:
172
if flag and self._update_timer_callback_tag is None:
200
173
# Will update the shown timer value every second
201
174
self._update_timer_callback_tag = (gobject.timeout_add
203
176
self.update_timer))
204
elif old and self._update_timer_callback_lock == 0:
177
elif not (flag or self._update_timer_callback_tag is None):
205
178
gobject.source_remove(self._update_timer_callback_tag)
206
179
self._update_timer_callback_tag = None
281
253
"bold-underline-blink":
282
254
"bold-underline-blink-standout",
285
257
# Rebuild focus and non-focus widgets using current properties
287
259
# Base part of a client. Name!
288
260
base = '{name}: '.format(name=self.properties["Name"])
289
261
if not self.properties["Enabled"]:
290
262
message = "DISABLED"
263
self.using_timer(False)
291
264
elif self.properties["ApprovalPending"]:
292
265
timeout = datetime.timedelta(milliseconds
293
266
= self.properties
295
268
last_approval_request = isoformat_to_datetime(
296
269
self.properties["LastApprovalRequest"])
297
270
if last_approval_request is not None:
298
timer = timeout - (datetime.datetime.utcnow()
299
- last_approval_request)
271
timer = max(timeout - (datetime.datetime.utcnow()
272
- last_approval_request),
273
datetime.timedelta())
301
275
timer = datetime.timedelta()
302
276
if self.properties["ApprovedByDefault"]:
303
277
message = "Approval in {0}. (d)eny?"
305
279
message = "Denial in {0}. (a)pprove?"
306
message = message.format(unicode(timer).rsplit(".", 1)[0])
280
message = message.format(str(timer).rsplit(".", 1)[0])
281
self.using_timer(True)
307
282
elif self.properties["LastCheckerStatus"] != 0:
308
283
# When checker has failed, show timer until client expires
309
284
expires = self.properties["Expires"]
313
288
expires = (datetime.datetime.strptime
314
289
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
315
timer = expires - datetime.datetime.utcnow()
290
timer = max(expires - datetime.datetime.utcnow(),
291
datetime.timedelta())
316
292
message = ('A checker has failed! Time until client'
317
293
' gets disabled: {0}'
318
.format(unicode(timer).rsplit(".", 1)[0]))
294
.format(str(timer).rsplit(".", 1)[0]))
295
self.using_timer(True)
320
297
message = "enabled"
298
self.using_timer(False)
321
299
self._text = "{0}{1}".format(base, message)
323
301
if not urwid.supports_unicode():
324
302
self._text = self._text.encode("ascii", "replace")
325
303
textlist = [("normal", self._text)]
343
321
return True # Keep calling this
345
def delete(self, *args, **kwargs):
323
def delete(self, **kwargs):
346
324
if self._update_timer_callback_tag is not None:
347
325
gobject.source_remove(self._update_timer_callback_tag)
348
326
self._update_timer_callback_tag = None
351
329
self.match_objects = ()
352
330
if self.delete_hook is not None:
353
331
self.delete_hook(self)
354
return super(MandosClientWidget, self).delete(*args, **kwargs)
332
return super(MandosClientWidget, self).delete(**kwargs)
356
334
def render(self, maxcolrow, focus=False):
357
335
"""Render differently if we have focus.
402
def property_changed(self, property=None, value=None,
380
def property_changed(self, property=None, **kwargs):
404
381
"""Call self.update() if old value is not new value.
405
382
This overrides the method from MandosClientPropertyCache"""
406
property_name = unicode(property)
383
property_name = str(property)
407
384
old_value = self.properties.get(property_name)
408
385
super(MandosClientWidget, self).property_changed(
409
property=property, value=value, *args, **kwargs)
386
property=property, **kwargs)
410
387
if self.properties.get(property_name) != old_value:
416
393
"down" key presses, thus not allowing any containing widgets to
417
394
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)
396
def keypress(self, *args, **kwargs):
397
ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
421
398
if ret in ("up", "down"):
437
414
"default", "default", None),
439
"default", "default", "bold"),
416
"bold", "default", "bold"),
440
417
("underline-blink",
441
"default", "default", "underline"),
418
"underline,blink", "default", "underline,blink"),
443
"default", "default", "standout"),
420
"standout", "default", "standout"),
444
421
("bold-underline-blink",
445
"default", "default", ("bold", "underline")),
422
"bold,underline,blink", "default", "bold,underline,blink"),
446
423
("bold-standout",
447
"default", "default", ("bold", "standout")),
424
"bold,standout", "default", "bold,standout"),
448
425
("underline-blink-standout",
449
"default", "default", ("underline", "standout")),
426
"underline,blink,standout", "default",
427
"underline,blink,standout"),
450
428
("bold-underline-blink-standout",
451
"default", "default", ("bold", "underline",
429
"bold,underline,blink,standout", "default",
430
"bold,underline,blink,standout"),
455
433
if urwid.supports_unicode():
606
580
mandos_clients = (self.mandos_serv
607
581
.GetAllClientsWithProperties())
582
if not mandos_clients:
583
self.log_message_raw(("bold", "Note: Server has no clients."))
608
584
except dbus.exceptions.DBusException:
585
self.log_message_raw(("bold", "Note: No Mandos server running."))
609
586
mandos_clients = dbus.Dictionary()
611
588
(self.mandos_serv
623
600
self.client_not_found,
624
601
dbus_interface=server_interface,
625
602
byte_arrays=True))
626
for path, client in mandos_clients.iteritems():
603
for path, client in mandos_clients.items():
627
604
client_proxy_object = self.bus.get_object(self.busname,
629
606
self.add_client(MandosClientWidget(server_proxy_object