102
103
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)
108
def property_changed(self, property=None, value=None):
109
"""This is called whenever we get a PropertyChanged signal
110
It updates the changed property in the "properties" dict.
105
def properties_changed(self, interface, properties, invalidated):
106
"""This is called whenever we get a PropertiesChanged signal
107
It updates the changed properties in the "properties" dict.
112
109
# Update properties dict with new value
113
self.properties[property] = value
110
if interface == client_interface:
111
self.properties.update(properties)
115
113
def delete(self):
116
114
self.property_changed_match.remove()
172
170
if flag and self._update_timer_callback_tag is None:
173
171
# Will update the shown timer value every second
174
self._update_timer_callback_tag = (gobject.timeout_add
172
self._update_timer_callback_tag = (GLib.timeout_add
176
174
self.update_timer))
177
175
elif not (flag or self._update_timer_callback_tag is None):
178
gobject.source_remove(self._update_timer_callback_tag)
176
GLib.source_remove(self._update_timer_callback_tag)
179
177
self._update_timer_callback_tag = None
181
179
def checker_completed(self, exitstatus, condition, command):
182
180
if exitstatus == 0:
181
self.logger('Checker for client {} (command "{}")'
182
' succeeded'.format(self.properties["Name"],
186
187
if os.WIFEXITED(condition):
187
self.logger('Checker for client {0} (command "{1}")'
188
' failed with exit code {2}'
188
self.logger('Checker for client {} (command "{}") failed'
189
190
.format(self.properties["Name"], command,
190
191
os.WEXITSTATUS(condition)))
191
192
elif os.WIFSIGNALED(condition):
192
self.logger('Checker for client {0} (command "{1}") was'
193
' killed by signal {2}'
193
self.logger('Checker for client {} (command "{}") was'
194
' killed by signal {}'
194
195
.format(self.properties["Name"], command,
195
196
os.WTERMSIG(condition)))
196
elif os.WCOREDUMP(condition):
197
self.logger('Checker for client {0} (command "{1}")'
199
.format(self.properties["Name"], command))
201
self.logger('Checker for client {0} completed'
203
.format(self.properties["Name"]))
206
199
def checker_started(self, command):
207
"""Server signals that a checker started. This could be useful
208
to log in the future. """
209
#self.logger('Client {0} started checker "{1}"'
210
# .format(self.properties["Name"],
200
"""Server signals that a checker started."""
201
self.logger('Client {} started checker "{}"'
202
.format(self.properties["Name"],
214
205
def got_secret(self):
215
self.logger('Client {0} received its secret'
206
self.logger('Client {} received its secret'
216
207
.format(self.properties["Name"]))
218
209
def need_approval(self, timeout, default):
220
message = 'Client {0} needs approval within {1} seconds'
211
message = 'Client {} needs approval within {} seconds'
222
message = 'Client {0} will get its secret in {1} seconds'
213
message = 'Client {} will get its secret in {} seconds'
223
214
self.logger(message.format(self.properties["Name"],
226
217
def rejected(self, reason):
227
self.logger('Client {0} was rejected; reason: {1}'
218
self.logger('Client {} was rejected; reason: {}'
228
219
.format(self.properties["Name"], reason))
230
221
def selectable(self):
275
266
timer = datetime.timedelta()
276
267
if self.properties["ApprovedByDefault"]:
277
message = "Approval in {0}. (d)eny?"
268
message = "Approval in {}. (d)eny?"
279
message = "Denial in {0}. (a)pprove?"
270
message = "Denial in {}. (a)pprove?"
280
271
message = message.format(str(timer).rsplit(".", 1)[0])
281
272
self.using_timer(True)
282
273
elif self.properties["LastCheckerStatus"] != 0:
290
281
timer = max(expires - datetime.datetime.utcnow(),
291
282
datetime.timedelta())
292
283
message = ('A checker has failed! Time until client'
293
' gets disabled: {0}'
294
285
.format(str(timer).rsplit(".", 1)[0]))
295
286
self.using_timer(True)
297
288
message = "enabled"
298
289
self.using_timer(False)
299
self._text = "{0}{1}".format(base, message)
290
self._text = "{}{}".format(base, message)
301
292
if not urwid.supports_unicode():
302
293
self._text = self._text.encode("ascii", "replace")
315
306
self.update_hook()
317
308
def update_timer(self):
318
"""called by gobject. Will indefinitely loop until
319
gobject.source_remove() on tag is called"""
309
"""called by GLib. Will indefinitely loop until
310
GLib.source_remove() on tag is called
321
313
return True # Keep calling this
323
315
def delete(self, **kwargs):
324
316
if self._update_timer_callback_tag is not None:
325
gobject.source_remove(self._update_timer_callback_tag)
317
GLib.source_remove(self._update_timer_callback_tag)
326
318
self._update_timer_callback_tag = None
327
319
for match in self.match_objects:
342
334
This overrides the method from urwid.FlowWidget"""
344
self.proxy.Enable(dbus_interface = client_interface,
336
self.proxy.Set(client_interface, "Enabled",
337
dbus.Boolean(True), ignore_reply = True,
338
dbus_interface = dbus.PROPERTIES_IFACE)
347
self.proxy.Disable(dbus_interface = client_interface,
340
self.proxy.Set(client_interface, "Enabled", False,
342
dbus_interface = dbus.PROPERTIES_IFACE)
350
344
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
351
345
dbus_interface = client_interface,
360
354
ignore_reply=True)
362
self.proxy.StartChecker(dbus_interface = client_interface,
356
self.proxy.Set(client_interface, "CheckerRunning",
357
dbus.Boolean(True), ignore_reply = True,
358
dbus_interface = dbus.PROPERTIES_IFACE)
365
self.proxy.StopChecker(dbus_interface = client_interface,
360
self.proxy.Set(client_interface, "CheckerRunning",
361
dbus.Boolean(False), ignore_reply = True,
362
dbus_interface = dbus.PROPERTIES_IFACE)
368
364
self.proxy.CheckedOK(dbus_interface = client_interface,
369
365
ignore_reply=True)
380
def property_changed(self, property=None, **kwargs):
381
"""Call self.update() if old value is not new value.
376
def properties_changed(self, interface, properties, invalidated):
377
"""Call self.update() if any properties changed.
382
378
This overrides the method from MandosClientPropertyCache"""
383
property_name = str(property)
384
old_value = self.properties.get(property_name)
385
super(MandosClientWidget, self).property_changed(
386
property=property, **kwargs)
387
if self.properties.get(property_name) != old_value:
379
old_values = { key: self.properties.get(key)
380
for key in properties.keys() }
381
super(MandosClientWidget, self).properties_changed(
382
interface, properties, invalidated)
383
if any(old_values[key] != self.properties.get(key)
384
for key in old_values):
404
401
"""This is the entire user interface - the whole screen
405
402
with boxes, lists of client widgets, etc.
407
def __init__(self, max_log_length=1000):
404
def __init__(self, max_log_length=1000, log_level=1):
408
405
DBusGMainLoop(set_as_default=True)
410
407
self.screen = urwid.curses_display.Screen()
464
463
"q: Quit ?: Help"))
466
465
self.busname = domain + '.Mandos'
467
self.main_loop = gobject.MainLoop()
466
self.main_loop = GLib.MainLoop()
469
468
def client_not_found(self, fingerprint, address):
470
self.log_message("Client with address {0} and fingerprint"
471
" {1} could not be found"
469
self.log_message("Client with address {} and fingerprint {}"
470
" could not be found"
472
471
.format(address, fingerprint))
474
473
def rebuild(self):
487
486
self.uilist.append(self.logbox)
488
487
self.topwidget = urwid.Pile(self.uilist)
490
def log_message(self, message):
489
def log_message(self, message, level=1):
491
490
"""Log message formatted with timestamp"""
491
if level < self.log_level:
492
493
timestamp = datetime.datetime.now().isoformat()
493
self.log_message_raw(timestamp + ": " + message)
494
self.log_message_raw("{}: {}".format(timestamp, message),
495
def log_message_raw(self, markup):
497
def log_message_raw(self, markup, level=1):
496
498
"""Add a log message to the log buffer."""
499
if level < self.log_level:
497
501
self.log.append(urwid.Text(markup, wrap=self.log_wrap))
498
502
if (self.max_log_length
499
503
and len(self.log) > self.max_log_length):
506
510
"""Toggle visibility of the log buffer."""
507
511
self.log_visible = not self.log_visible
509
#self.log_message("Log visibility changed to: "
510
# + str(self.log_visible))
513
self.log_message("Log visibility changed to: {}"
514
.format(self.log_visible), level=0)
512
516
def change_log_display(self):
513
517
"""Change type of log display.
518
522
self.log_wrap = "clip"
519
523
for textwidget in self.log:
520
524
textwidget.set_wrap_mode(self.log_wrap)
521
#self.log_message("Wrap mode: " + self.log_wrap)
525
self.log_message("Wrap mode: {}".format(self.log_wrap),
523
def find_and_remove_client(self, path, name):
528
def find_and_remove_client(self, path, interfaces):
524
529
"""Find a client by its object path and remove it.
526
This is connected to the ClientRemoved signal from the
531
This is connected to the InterfacesRemoved signal from the
527
532
Mandos server object."""
533
if client_interface not in interfaces:
534
# Not a Mandos client object; ignore
529
537
client = self.clients_dict[path]
532
self.log_message("Unknown client {0!r} ({1!r}) removed"
540
self.log_message("Unknown client {!r} removed"
537
def add_new_client(self, path):
545
def add_new_client(self, path, ifs_and_props):
546
"""Find a client by its object path and remove it.
548
This is connected to the InterfacesAdded signal from the
549
Mandos server object.
551
if client_interface not in ifs_and_props:
552
# Not a Mandos client object; ignore
538
554
client_proxy_object = self.bus.get_object(self.busname, path)
539
555
self.add_client(MandosClientWidget(server_proxy_object
540
556
=self.mandos_serv,
586
605
mandos_clients = dbus.Dictionary()
588
607
(self.mandos_serv
589
.connect_to_signal("ClientRemoved",
608
.connect_to_signal("InterfacesRemoved",
590
609
self.find_and_remove_client,
591
dbus_interface=server_interface,
611
= dbus.OBJECT_MANAGER_IFACE,
592
612
byte_arrays=True))
593
613
(self.mandos_serv
594
.connect_to_signal("ClientAdded",
614
.connect_to_signal("InterfacesAdded",
595
615
self.add_new_client,
596
dbus_interface=server_interface,
617
= dbus.OBJECT_MANAGER_IFACE,
597
618
byte_arrays=True))
598
619
(self.mandos_serv
599
620
.connect_to_signal("ClientNotFound",