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-2014 Teddy Hogeborn
7
# Copyright © 2009-2014 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
17
17
# GNU General Public License for more details.
19
19
# You should have received a copy of the GNU General Public License
20
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
# along with this program. If not, see
21
# <http://www.gnu.org/licenses/>.
22
23
# Contact the authors at <mandos@recompile.se>.
25
26
from __future__ import (division, absolute_import, print_function,
29
from future_builtins import *
52
60
domain = 'se.recompile'
53
61
server_interface = domain + '.Mandos'
54
62
client_interface = domain + '.Mandos.Client'
57
# Always run in monochrome mode
58
urwid.curses_display.curses.has_colors = lambda : False
60
# Urwid doesn't support blinking, but we want it. Since we have no
61
# use for underline on its own, we make underline also always blink.
62
urwid.curses_display.curses.A_UNDERLINE |= (
63
urwid.curses_display.curses.A_BLINK)
65
65
def isoformat_to_datetime(iso):
66
66
"Parse an ISO 8601 date string to a datetime.datetime()"
83
83
properties and calls a hook function when any of them are
86
def __init__(self, proxy_object=None, *args, **kwargs):
86
def __init__(self, proxy_object=None, properties=None, **kwargs):
87
87
self.proxy = proxy_object # Mandos Client proxy object
89
self.properties = dict()
88
self.properties = dict() if properties is None else properties
90
89
self.property_changed_match = (
91
90
self.proxy.connect_to_signal("PropertyChanged",
92
self.property_changed,
91
self._property_changed,
96
self.properties.update(
97
self.proxy.GetAll(client_interface,
98
dbus_interface = dbus.PROPERTIES_IFACE))
100
#XXX This breaks good super behaviour
101
# 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)
104
107
def property_changed(self, property=None, value=None):
105
108
"""This is called whenever we get a PropertyChanged signal
108
111
# Update properties dict with new value
109
112
self.properties[property] = value
111
def delete(self, *args, **kwargs):
112
115
self.property_changed_match.remove()
113
super(MandosClientPropertyCache, self).__init__(
117
118
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
121
122
def __init__(self, server_proxy_object=None, update_hook=None,
122
delete_hook=None, logger=None, *args, **kwargs):
123
delete_hook=None, logger=None, **kwargs):
123
124
# Called on update
124
125
self.update_hook = update_hook
125
126
# Called on delete
130
131
self.logger = logger
132
133
self._update_timer_callback_tag = None
133
self._update_timer_callback_lock = 0
135
135
# The widget shown normally
136
136
self._text_widget = urwid.Text("")
137
137
# The widget shown when we have focus
138
138
self._focus_text_widget = urwid.Text("")
139
super(MandosClientWidget, self).__init__(
140
update_hook=update_hook, delete_hook=delete_hook,
139
super(MandosClientWidget, self).__init__(**kwargs)
143
141
self.opened = False
145
last_checked_ok = isoformat_to_datetime(self.properties
148
if self.properties ["LastCheckerStatus"] != 0:
149
self.using_timer(True)
151
if self.need_approval:
152
self.using_timer(True)
154
143
self.match_objects = (
155
144
self.proxy.connect_to_signal("CheckerCompleted",
156
145
self.checker_completed,
175
164
#self.logger('Created client {0}'
176
165
# .format(self.properties["Name"]))
178
def property_changed(self, property=None, value=None):
179
super(self, MandosClientWidget).property_changed(property,
181
if property == "ApprovalPending":
182
using_timer(bool(value))
183
if property == "LastCheckerStatus":
184
using_timer(value != 0)
185
#self.logger('Checker for client {0} (command "{1}") was '
186
# ' successful'.format(self.properties["Name"],
189
167
def using_timer(self, flag):
190
168
"""Call this method with True or False when timer should be
191
169
activated or deactivated.
193
old = self._update_timer_callback_lock
195
self._update_timer_callback_lock += 1
197
self._update_timer_callback_lock -= 1
198
if old == 0 and self._update_timer_callback_lock:
171
if flag and self._update_timer_callback_tag is None:
199
172
# Will update the shown timer value every second
200
173
self._update_timer_callback_tag = (gobject.timeout_add
202
175
self.update_timer))
203
elif old and self._update_timer_callback_lock == 0:
176
elif not (flag or self._update_timer_callback_tag is None):
204
177
gobject.source_remove(self._update_timer_callback_tag)
205
178
self._update_timer_callback_tag = None
280
252
"bold-underline-blink":
281
253
"bold-underline-blink-standout",
284
256
# Rebuild focus and non-focus widgets using current properties
286
258
# Base part of a client. Name!
287
259
base = '{name}: '.format(name=self.properties["Name"])
288
260
if not self.properties["Enabled"]:
289
261
message = "DISABLED"
262
self.using_timer(False)
290
263
elif self.properties["ApprovalPending"]:
291
264
timeout = datetime.timedelta(milliseconds
292
265
= self.properties
294
267
last_approval_request = isoformat_to_datetime(
295
268
self.properties["LastApprovalRequest"])
296
269
if last_approval_request is not None:
297
timer = timeout - (datetime.datetime.utcnow()
298
- last_approval_request)
270
timer = max(timeout - (datetime.datetime.utcnow()
271
- last_approval_request),
272
datetime.timedelta())
300
274
timer = datetime.timedelta()
301
275
if self.properties["ApprovedByDefault"]:
302
276
message = "Approval in {0}. (d)eny?"
304
278
message = "Denial in {0}. (a)pprove?"
305
message = message.format(unicode(timer).rsplit(".", 1)[0])
279
message = message.format(str(timer).rsplit(".", 1)[0])
280
self.using_timer(True)
306
281
elif self.properties["LastCheckerStatus"] != 0:
307
# When checker has failed, print a timer until client expires
282
# When checker has failed, show timer until client expires
308
283
expires = self.properties["Expires"]
309
284
if expires == "":
310
285
timer = datetime.timedelta(0)
312
expires = datetime.datetime.strptime(expires,
313
'%Y-%m-%dT%H:%M:%S.%f')
314
timer = expires - datetime.datetime.utcnow()
287
expires = (datetime.datetime.strptime
288
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
289
timer = max(expires - datetime.datetime.utcnow(),
290
datetime.timedelta())
315
291
message = ('A checker has failed! Time until client'
316
292
' gets disabled: {0}'
317
.format(unicode(timer).rsplit(".", 1)[0]))
293
.format(str(timer).rsplit(".", 1)[0]))
294
self.using_timer(True)
319
296
message = "enabled"
297
self.using_timer(False)
320
298
self._text = "{0}{1}".format(base, message)
322
300
if not urwid.supports_unicode():
323
301
self._text = self._text.encode("ascii", "replace")
324
302
textlist = [("normal", self._text)]
342
320
return True # Keep calling this
344
def delete(self, *args, **kwargs):
322
def delete(self, **kwargs):
345
323
if self._update_timer_callback_tag is not None:
346
324
gobject.source_remove(self._update_timer_callback_tag)
347
325
self._update_timer_callback_tag = None
350
328
self.match_objects = ()
351
329
if self.delete_hook is not None:
352
330
self.delete_hook(self)
353
return super(MandosClientWidget, self).delete(*args, **kwargs)
331
return super(MandosClientWidget, self).delete(**kwargs)
355
333
def render(self, maxcolrow, focus=False):
356
334
"""Render differently if we have focus.
401
def property_changed(self, property=None, value=None,
379
def property_changed(self, property=None, **kwargs):
403
380
"""Call self.update() if old value is not new value.
404
381
This overrides the method from MandosClientPropertyCache"""
405
property_name = unicode(property)
382
property_name = str(property)
406
383
old_value = self.properties.get(property_name)
407
384
super(MandosClientWidget, self).property_changed(
408
property=property, value=value, *args, **kwargs)
385
property=property, **kwargs)
409
386
if self.properties.get(property_name) != old_value:
415
392
"down" key presses, thus not allowing any containing widgets to
416
393
use them as an excuse to shift focus away from this widget.
418
def keypress(self, maxcolrow, key):
419
ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
395
def keypress(self, *args, **kwargs):
396
ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
420
397
if ret in ("up", "down"):
436
413
"default", "default", None),
438
"default", "default", "bold"),
415
"bold", "default", "bold"),
439
416
("underline-blink",
440
"default", "default", "underline"),
417
"underline,blink", "default", "underline,blink"),
442
"default", "default", "standout"),
419
"standout", "default", "standout"),
443
420
("bold-underline-blink",
444
"default", "default", ("bold", "underline")),
421
"bold,underline,blink", "default", "bold,underline,blink"),
445
422
("bold-standout",
446
"default", "default", ("bold", "standout")),
423
"bold,standout", "default", "bold,standout"),
447
424
("underline-blink-standout",
448
"default", "default", ("underline", "standout")),
425
"underline,blink,standout", "default",
426
"underline,blink,standout"),
449
427
("bold-underline-blink-standout",
450
"default", "default", ("bold", "underline",
428
"bold,underline,blink,standout", "default",
429
"bold,underline,blink,standout"),
454
432
if urwid.supports_unicode():
605
579
mandos_clients = (self.mandos_serv
606
580
.GetAllClientsWithProperties())
581
if not mandos_clients:
582
self.log_message_raw(("bold", "Note: Server has no clients."))
607
583
except dbus.exceptions.DBusException:
584
self.log_message_raw(("bold", "Note: No Mandos server running."))
608
585
mandos_clients = dbus.Dictionary()
610
587
(self.mandos_serv
622
599
self.client_not_found,
623
600
dbus_interface=server_interface,
624
601
byte_arrays=True))
625
for path, client in mandos_clients.iteritems():
602
for path, client in mandos_clients.items():
626
603
client_proxy_object = self.bus.get_object(self.busname,
628
605
self.add_client(MandosClientWidget(server_proxy_object