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
# Contact the authors at <mandos@fukt.bsnet.se>.
23
# Contact the authors at <mandos@recompile.se>.
25
26
from __future__ import (division, absolute_import, print_function,
29
from future_builtins import *
83
86
properties and calls a hook function when any of them are
86
def __init__(self, proxy_object=None, *args, **kwargs):
89
def __init__(self, proxy_object=None, properties=None, **kwargs):
87
90
self.proxy = proxy_object # Mandos Client proxy object
89
self.properties = dict()
91
self.properties = dict() if properties is None else properties
90
92
self.property_changed_match = (
91
93
self.proxy.connect_to_signal("PropertyChanged",
92
self.property_changed,
94
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__(
98
if properties is None:
99
self.properties.update(
100
self.proxy.GetAll(client_interface,
102
= dbus.PROPERTIES_IFACE))
104
super(MandosClientPropertyCache, self).__init__(**kwargs)
106
def _property_changed(self, property, value):
107
"""Helper which takes positional arguments"""
108
return self.property_changed(property=property, value=value)
104
110
def property_changed(self, property=None, value=None):
105
111
"""This is called whenever we get a PropertyChanged signal
132
136
self._update_timer_callback_tag = None
133
137
self._update_timer_callback_lock = 0
134
self.last_checker_failed = False
136
139
# The widget shown normally
137
140
self._text_widget = urwid.Text("")
138
141
# The widget shown when we have focus
139
142
self._focus_text_widget = urwid.Text("")
140
super(MandosClientWidget, self).__init__(
141
update_hook=update_hook, delete_hook=delete_hook,
143
super(MandosClientWidget, self).__init__(**kwargs)
144
145
self.opened = False
146
147
last_checked_ok = isoformat_to_datetime(self.properties
147
148
["LastCheckedOK"])
148
if last_checked_ok is None:
149
self.last_checker_failed = True
151
self.last_checker_failed = ((datetime.datetime.utcnow()
158
if self.last_checker_failed:
150
if self.properties ["LastCheckerStatus"] != 0:
159
151
self.using_timer(True)
161
153
if self.need_approval:
183
175
client_interface,
184
176
byte_arrays=True))
185
#self.logger('Created client %s' % (self.properties["Name"]))
177
#self.logger('Created client {0}'
178
# .format(self.properties["Name"]))
187
180
def property_changed(self, property=None, value=None):
188
181
super(self, MandosClientWidget).property_changed(property,
190
183
if property == "ApprovalPending":
191
184
using_timer(bool(value))
185
if property == "LastCheckerStatus":
186
using_timer(value != 0)
187
#self.logger('Checker for client {0} (command "{1}") was '
188
# ' successful'.format(self.properties["Name"],
193
191
def using_timer(self, flag):
194
192
"""Call this method with True or False when timer should be
195
193
activated or deactivated.
210
209
def checker_completed(self, exitstatus, condition, command):
211
210
if exitstatus == 0:
212
if self.last_checker_failed:
213
self.last_checker_failed = False
214
self.using_timer(False)
215
#self.logger('Checker for client %s (command "%s")'
217
# % (self.properties["Name"], command))
221
if not self.last_checker_failed:
222
self.last_checker_failed = True
223
self.using_timer(True)
224
214
if os.WIFEXITED(condition):
225
self.logger('Checker for client %s (command "%s")'
226
' failed with exit code %s'
227
% (self.properties["Name"], command,
228
os.WEXITSTATUS(condition)))
215
self.logger('Checker for client {0} (command "{1}")'
216
' failed with exit code {2}'
217
.format(self.properties["Name"], command,
218
os.WEXITSTATUS(condition)))
229
219
elif os.WIFSIGNALED(condition):
230
self.logger('Checker for client %s (command "%s")'
231
' was killed by signal %s'
232
% (self.properties["Name"], command,
233
os.WTERMSIG(condition)))
220
self.logger('Checker for client {0} (command "{1}") was'
221
' killed by signal {2}'
222
.format(self.properties["Name"], command,
223
os.WTERMSIG(condition)))
234
224
elif os.WCOREDUMP(condition):
235
self.logger('Checker for client %s (command "%s")'
225
self.logger('Checker for client {0} (command "{1}")'
237
% (self.properties["Name"], command))
227
.format(self.properties["Name"], command))
239
self.logger('Checker for client %s completed'
229
self.logger('Checker for client {0} completed'
231
.format(self.properties["Name"]))
243
234
def checker_started(self, command):
244
#self.logger('Client %s started checker "%s"'
245
# % (self.properties["Name"], unicode(command)))
235
"""Server signals that a checker started. This could be useful
236
to log in the future. """
237
#self.logger('Client {0} started checker "{1}"'
238
# .format(self.properties["Name"],
248
242
def got_secret(self):
249
self.last_checker_failed = False
250
self.logger('Client %s received its secret'
251
% self.properties["Name"])
243
self.logger('Client {0} received its secret'
244
.format(self.properties["Name"]))
253
246
def need_approval(self, timeout, default):
255
message = 'Client %s needs approval within %s seconds'
248
message = 'Client {0} needs approval within {1} seconds'
257
message = 'Client %s will get its secret in %s seconds'
259
% (self.properties["Name"], timeout/1000))
250
message = 'Client {0} will get its secret in {1} seconds'
251
self.logger(message.format(self.properties["Name"],
260
253
self.using_timer(True)
262
255
def rejected(self, reason):
263
self.logger('Client %s was rejected; reason: %s'
264
% (self.properties["Name"], reason))
256
self.logger('Client {0} was rejected; reason: {1}'
257
.format(self.properties["Name"], reason))
266
259
def selectable(self):
267
260
"""Make this a "selectable" widget.
310
302
timer = datetime.timedelta()
311
303
if self.properties["ApprovedByDefault"]:
312
message = "Approval in %s. (d)eny?"
314
message = "Denial in %s. (a)pprove?"
315
message = message % unicode(timer).rsplit(".", 1)[0]
316
elif self.last_checker_failed:
317
timeout = datetime.timedelta(milliseconds
320
last_ok = isoformat_to_datetime(
321
max((self.properties["LastCheckedOK"]
322
or self.properties["Created"]),
323
self.properties["LastEnabled"]))
324
timer = timeout - (datetime.datetime.utcnow() - last_ok)
304
message = "Approval in {0}. (d)eny?"
306
message = "Denial in {0}. (a)pprove?"
307
message = message.format(unicode(timer).rsplit(".", 1)[0])
308
elif self.properties["LastCheckerStatus"] != 0:
309
# When checker has failed, show timer until client expires
310
expires = self.properties["Expires"]
312
timer = datetime.timedelta(0)
314
expires = (datetime.datetime.strptime
315
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
316
timer = expires - datetime.datetime.utcnow()
325
317
message = ('A checker has failed! Time until client'
327
% unicode(timer).rsplit(".", 1)[0])
318
' gets disabled: {0}'
319
.format(unicode(timer).rsplit(".", 1)[0]))
329
321
message = "enabled"
330
self._text = "%s%s" % (base, message)
322
self._text = "{0}{1}".format(base, message)
332
324
if not urwid.supports_unicode():
333
325
self._text = self._text.encode("ascii", "replace")
410
def property_changed(self, property=None, value=None,
403
def property_changed(self, property=None, **kwargs):
412
404
"""Call self.update() if old value is not new value.
413
405
This overrides the method from MandosClientPropertyCache"""
414
406
property_name = unicode(property)
415
407
old_value = self.properties.get(property_name)
416
408
super(MandosClientWidget, self).property_changed(
417
property=property, value=value, *args, **kwargs)
409
property=property, **kwargs)
418
410
if self.properties.get(property_name) != old_value:
424
416
"down" key presses, thus not allowing any containing widgets to
425
417
use them as an excuse to shift focus away from this widget.
427
def keypress(self, maxcolrow, key):
428
ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
419
def keypress(self, *args, **kwargs):
420
ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
429
421
if ret in ("up", "down"):
496
488
self.busname = domain + '.Mandos'
497
489
self.main_loop = gobject.MainLoop()
498
self.bus = dbus.SystemBus()
499
mandos_dbus_objc = self.bus.get_object(
500
self.busname, "/", follow_name_owner_changes=True)
501
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
505
mandos_clients = (self.mandos_serv
506
.GetAllClientsWithProperties())
507
except dbus.exceptions.DBusException:
508
mandos_clients = dbus.Dictionary()
511
.connect_to_signal("ClientRemoved",
512
self.find_and_remove_client,
513
dbus_interface=server_interface,
516
.connect_to_signal("ClientAdded",
518
dbus_interface=server_interface,
521
.connect_to_signal("ClientNotFound",
522
self.client_not_found,
523
dbus_interface=server_interface,
525
for path, client in mandos_clients.iteritems():
526
client_proxy_object = self.bus.get_object(self.busname,
528
self.add_client(MandosClientWidget(server_proxy_object
531
=client_proxy_object,
541
491
def client_not_found(self, fingerprint, address):
542
self.log_message(("Client with address %s and fingerprint %s"
543
" could not be found" % (address,
492
self.log_message("Client with address {0} and fingerprint"
493
" {1} could not be found"
494
.format(address, fingerprint))
546
496
def rebuild(self):
547
497
"""This rebuilds the User Interface.
649
598
"""Start the main loop and exit when it's done."""
599
self.bus = dbus.SystemBus()
600
mandos_dbus_objc = self.bus.get_object(
601
self.busname, "/", follow_name_owner_changes=True)
602
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
606
mandos_clients = (self.mandos_serv
607
.GetAllClientsWithProperties())
608
except dbus.exceptions.DBusException:
609
mandos_clients = dbus.Dictionary()
612
.connect_to_signal("ClientRemoved",
613
self.find_and_remove_client,
614
dbus_interface=server_interface,
617
.connect_to_signal("ClientAdded",
619
dbus_interface=server_interface,
622
.connect_to_signal("ClientNotFound",
623
self.client_not_found,
624
dbus_interface=server_interface,
626
for path, client in mandos_clients.iteritems():
627
client_proxy_object = self.bus.get_object(self.busname,
629
self.add_client(MandosClientWidget(server_proxy_object
632
=client_proxy_object,
651
643
self._input_callback_tag = (gobject.io_add_watch
652
644
(sys.stdin.fileno(),