60
55
domain = 'se.recompile'
61
56
server_interface = domain + '.Mandos'
62
57
client_interface = domain + '.Mandos.Client'
66
dbus.OBJECT_MANAGER_IFACE
67
except AttributeError:
68
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
60
# Always run in monochrome mode
61
urwid.curses_display.curses.has_colors = lambda : False
63
# Urwid doesn't support blinking, but we want it. Since we have no
64
# use for underline on its own, we make underline also always blink.
65
urwid.curses_display.curses.A_UNDERLINE |= (
66
urwid.curses_display.curses.A_BLINK)
70
68
def isoformat_to_datetime(iso):
71
69
"Parse an ISO 8601 date string to a datetime.datetime()"
106
104
super(MandosClientPropertyCache, self).__init__(**kwargs)
108
def properties_changed(self, interface, properties, invalidated):
109
"""This is called whenever we get a PropertiesChanged signal
110
It updates the changed properties in the "properties" dict.
106
def _property_changed(self, property, value):
107
"""Helper which takes positional arguments"""
108
return self.property_changed(property=property, value=value)
110
def property_changed(self, property=None, value=None):
111
"""This is called whenever we get a PropertyChanged signal
112
It updates the changed property in the "properties" dict.
112
114
# Update properties dict with new value
113
if interface == client_interface:
114
self.properties.update(properties)
115
self.properties[property] = value
116
117
def delete(self):
117
118
self.property_changed_match.remove()
164
175
client_interface,
165
176
byte_arrays=True))
166
self.logger('Created client {}'
167
.format(self.properties["Name"]), level=0)
177
#self.logger('Created client {0}'
178
# .format(self.properties["Name"]))
180
def property_changed(self, property=None, value=None):
181
super(self, MandosClientWidget).property_changed(property,
183
if property == "ApprovalPending":
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"],
169
191
def using_timer(self, flag):
170
192
"""Call this method with True or False when timer should be
171
193
activated or deactivated.
173
if flag and self._update_timer_callback_tag is None:
195
old = self._update_timer_callback_lock
197
self._update_timer_callback_lock += 1
199
self._update_timer_callback_lock -= 1
200
if old == 0 and self._update_timer_callback_lock:
174
201
# Will update the shown timer value every second
175
202
self._update_timer_callback_tag = (gobject.timeout_add
177
204
self.update_timer))
178
elif not (flag or self._update_timer_callback_tag is None):
205
elif old and self._update_timer_callback_lock == 0:
179
206
gobject.source_remove(self._update_timer_callback_tag)
180
207
self._update_timer_callback_tag = None
182
209
def checker_completed(self, exitstatus, condition, command):
183
210
if exitstatus == 0:
184
self.logger('Checker for client {} (command "{}")'
185
' succeeded'.format(self.properties["Name"],
190
214
if os.WIFEXITED(condition):
191
self.logger('Checker for client {} (command "{}") failed'
215
self.logger('Checker for client {0} (command "{1}")'
216
' failed with exit code {2}'
193
217
.format(self.properties["Name"], command,
194
218
os.WEXITSTATUS(condition)))
195
219
elif os.WIFSIGNALED(condition):
196
self.logger('Checker for client {} (command "{}") was'
197
' killed by signal {}'
220
self.logger('Checker for client {0} (command "{1}") was'
221
' killed by signal {2}'
198
222
.format(self.properties["Name"], command,
199
223
os.WTERMSIG(condition)))
224
elif os.WCOREDUMP(condition):
225
self.logger('Checker for client {0} (command "{1}")'
227
.format(self.properties["Name"], command))
229
self.logger('Checker for client {0} completed'
231
.format(self.properties["Name"]))
202
234
def checker_started(self, command):
203
"""Server signals that a checker started."""
204
self.logger('Client {} started checker "{}"'
205
.format(self.properties["Name"],
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"],
208
242
def got_secret(self):
209
self.logger('Client {} received its secret'
243
self.logger('Client {0} received its secret'
210
244
.format(self.properties["Name"]))
212
246
def need_approval(self, timeout, default):
214
message = 'Client {} needs approval within {} seconds'
248
message = 'Client {0} needs approval within {1} seconds'
216
message = 'Client {} will get its secret in {} seconds'
250
message = 'Client {0} will get its secret in {1} seconds'
217
251
self.logger(message.format(self.properties["Name"],
253
self.using_timer(True)
220
255
def rejected(self, reason):
221
self.logger('Client {} was rejected; reason: {}'
256
self.logger('Client {0} was rejected; reason: {1}'
222
257
.format(self.properties["Name"], reason))
224
259
def selectable(self):
262
296
last_approval_request = isoformat_to_datetime(
263
297
self.properties["LastApprovalRequest"])
264
298
if last_approval_request is not None:
265
timer = max(timeout - (datetime.datetime.utcnow()
266
- last_approval_request),
267
datetime.timedelta())
299
timer = timeout - (datetime.datetime.utcnow()
300
- last_approval_request)
269
302
timer = datetime.timedelta()
270
303
if self.properties["ApprovedByDefault"]:
271
message = "Approval in {}. (d)eny?"
304
message = "Approval in {0}. (d)eny?"
273
message = "Denial in {}. (a)pprove?"
274
message = message.format(str(timer).rsplit(".", 1)[0])
275
self.using_timer(True)
306
message = "Denial in {0}. (a)pprove?"
307
message = message.format(unicode(timer).rsplit(".", 1)[0])
276
308
elif self.properties["LastCheckerStatus"] != 0:
277
309
# When checker has failed, show timer until client expires
278
310
expires = self.properties["Expires"]
282
314
expires = (datetime.datetime.strptime
283
315
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
284
timer = max(expires - datetime.datetime.utcnow(),
285
datetime.timedelta())
316
timer = expires - datetime.datetime.utcnow()
286
317
message = ('A checker has failed! Time until client'
288
.format(str(timer).rsplit(".", 1)[0]))
289
self.using_timer(True)
318
' gets disabled: {0}'
319
.format(unicode(timer).rsplit(".", 1)[0]))
291
321
message = "enabled"
292
self.using_timer(False)
293
self._text = "{}{}".format(base, message)
322
self._text = "{0}{1}".format(base, message)
295
324
if not urwid.supports_unicode():
296
325
self._text = self._text.encode("ascii", "replace")
297
326
textlist = [("normal", self._text)]
336
365
This overrides the method from urwid.FlowWidget"""
338
self.proxy.Set(client_interface, "Enabled",
339
dbus.Boolean(True), ignore_reply = True,
340
dbus_interface = dbus.PROPERTIES_IFACE)
367
self.proxy.Enable(dbus_interface = client_interface,
342
self.proxy.Set(client_interface, "Enabled", False,
344
dbus_interface = dbus.PROPERTIES_IFACE)
370
self.proxy.Disable(dbus_interface = client_interface,
346
373
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
347
374
dbus_interface = client_interface,
356
383
ignore_reply=True)
358
self.proxy.Set(client_interface, "CheckerRunning",
359
dbus.Boolean(True), ignore_reply = True,
360
dbus_interface = dbus.PROPERTIES_IFACE)
385
self.proxy.StartChecker(dbus_interface = client_interface,
362
self.proxy.Set(client_interface, "CheckerRunning",
363
dbus.Boolean(False), ignore_reply = True,
364
dbus_interface = dbus.PROPERTIES_IFACE)
388
self.proxy.StopChecker(dbus_interface = client_interface,
366
391
self.proxy.CheckedOK(dbus_interface = client_interface,
367
392
ignore_reply=True)
378
def properties_changed(self, interface, properties, invalidated):
379
"""Call self.update() if any properties changed.
403
def property_changed(self, property=None, **kwargs):
404
"""Call self.update() if old value is not new value.
380
405
This overrides the method from MandosClientPropertyCache"""
381
old_values = { key: self.properties.get(key)
382
for key in properties.keys() }
383
super(MandosClientWidget, self).properties_changed(
384
interface, properties, invalidated)
385
if any(old_values[key] != self.properties.get(key)
386
for key in old_values):
406
property_name = unicode(property)
407
old_value = self.properties.get(property_name)
408
super(MandosClientWidget, self).property_changed(
409
property=property, **kwargs)
410
if self.properties.get(property_name) != old_value:
413
437
"default", "default", None),
415
"bold", "default", "bold"),
439
"default", "default", "bold"),
416
440
("underline-blink",
417
"underline,blink", "default", "underline,blink"),
441
"default", "default", "underline"),
419
"standout", "default", "standout"),
443
"default", "default", "standout"),
420
444
("bold-underline-blink",
421
"bold,underline,blink", "default", "bold,underline,blink"),
445
"default", "default", ("bold", "underline")),
422
446
("bold-standout",
423
"bold,standout", "default", "bold,standout"),
447
"default", "default", ("bold", "standout")),
424
448
("underline-blink-standout",
425
"underline,blink,standout", "default",
426
"underline,blink,standout"),
449
"default", "default", ("underline", "standout")),
427
450
("bold-underline-blink-standout",
428
"bold,underline,blink,standout", "default",
429
"bold,underline,blink,standout"),
451
"default", "default", ("bold", "underline",
432
455
if urwid.supports_unicode():
488
509
self.uilist.append(self.logbox)
489
510
self.topwidget = urwid.Pile(self.uilist)
491
def log_message(self, message, level=1):
492
"""Log message formatted with timestamp"""
493
if level < self.log_level:
512
def log_message(self, message):
495
513
timestamp = datetime.datetime.now().isoformat()
496
self.log_message_raw("{}: {}".format(timestamp, message),
514
self.log_message_raw(timestamp + ": " + message)
499
def log_message_raw(self, markup, level=1):
516
def log_message_raw(self, markup):
500
517
"""Add a log message to the log buffer."""
501
if level < self.log_level:
503
518
self.log.append(urwid.Text(markup, wrap=self.log_wrap))
504
519
if (self.max_log_length
505
520
and len(self.log) > self.max_log_length):
524
539
self.log_wrap = "clip"
525
540
for textwidget in self.log:
526
541
textwidget.set_wrap_mode(self.log_wrap)
527
self.log_message("Wrap mode: {}".format(self.log_wrap),
542
#self.log_message("Wrap mode: " + self.log_wrap)
530
def find_and_remove_client(self, path, interfaces):
544
def find_and_remove_client(self, path, name):
531
545
"""Find a client by its object path and remove it.
533
This is connected to the InterfacesRemoved signal from the
547
This is connected to the ClientRemoved signal from the
534
548
Mandos server object."""
535
if client_interface not in interfaces:
536
# Not a Mandos client object; ignore
539
550
client = self.clients_dict[path]
542
self.log_message("Unknown client {!r} removed"
553
self.log_message("Unknown client {0!r} ({1!r}) removed"
547
def add_new_client(self, path, ifs_and_props):
548
"""Find a client by its object path and remove it.
550
This is connected to the InterfacesAdded signal from the
551
Mandos server object.
553
if client_interface not in ifs_and_props:
554
# Not a Mandos client object; ignore
558
def add_new_client(self, path):
556
559
client_proxy_object = self.bus.get_object(self.busname, path)
557
560
self.add_client(MandosClientWidget(server_proxy_object
558
561
=self.mandos_serv,
601
606
mandos_clients = (self.mandos_serv
602
607
.GetAllClientsWithProperties())
603
if not mandos_clients:
604
self.log_message_raw(("bold", "Note: Server has no clients."))
605
608
except dbus.exceptions.DBusException:
606
self.log_message_raw(("bold", "Note: No Mandos server running."))
607
609
mandos_clients = dbus.Dictionary()
609
611
(self.mandos_serv
610
.connect_to_signal("InterfacesRemoved",
612
.connect_to_signal("ClientRemoved",
611
613
self.find_and_remove_client,
613
= dbus.OBJECT_MANAGER_IFACE,
614
dbus_interface=server_interface,
614
615
byte_arrays=True))
615
616
(self.mandos_serv
616
.connect_to_signal("InterfacesAdded",
617
.connect_to_signal("ClientAdded",
617
618
self.add_new_client,
619
= dbus.OBJECT_MANAGER_IFACE,
619
dbus_interface=server_interface,
620
620
byte_arrays=True))
621
621
(self.mandos_serv
622
622
.connect_to_signal("ClientNotFound",
623
623
self.client_not_found,
624
624
dbus_interface=server_interface,
625
625
byte_arrays=True))
626
for path, client in mandos_clients.items():
626
for path, client in mandos_clients.iteritems():
627
627
client_proxy_object = self.bus.get_object(self.busname,
629
629
self.add_client(MandosClientWidget(server_proxy_object