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>.
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
108
114
# Update properties dict with new value
109
115
self.properties[property] = value
111
def delete(self, *args, **kwargs):
112
118
self.property_changed_match.remove()
113
super(MandosClientPropertyCache, self).__init__(
117
121
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
121
125
def __init__(self, server_proxy_object=None, update_hook=None,
122
delete_hook=None, logger=None, *args, **kwargs):
126
delete_hook=None, logger=None, **kwargs):
123
127
# Called on update
124
128
self.update_hook = update_hook
125
129
# Called on delete
130
134
self.logger = logger
132
136
self._update_timer_callback_tag = None
133
self._update_timer_callback_lock = 0
135
138
# The widget shown normally
136
139
self._text_widget = urwid.Text("")
137
140
# The widget shown when we have focus
138
141
self._focus_text_widget = urwid.Text("")
139
super(MandosClientWidget, self).__init__(
140
update_hook=update_hook, delete_hook=delete_hook,
142
super(MandosClientWidget, self).__init__(**kwargs)
143
144
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
146
self.match_objects = (
155
147
self.proxy.connect_to_signal("CheckerCompleted",
156
148
self.checker_completed,
175
167
#self.logger('Created client {0}'
176
168
# .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
170
def using_timer(self, flag):
190
171
"""Call this method with True or False when timer should be
191
172
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:
174
if flag and self._update_timer_callback_tag is None:
199
175
# Will update the shown timer value every second
200
176
self._update_timer_callback_tag = (gobject.timeout_add
202
178
self.update_timer))
203
elif old and self._update_timer_callback_lock == 0:
179
elif not (flag or self._update_timer_callback_tag is None):
204
180
gobject.source_remove(self._update_timer_callback_tag)
205
181
self._update_timer_callback_tag = None
248
224
message = 'Client {0} will get its secret in {1} seconds'
249
225
self.logger(message.format(self.properties["Name"],
251
self.using_timer(True)
253
228
def rejected(self, reason):
254
229
self.logger('Client {0} was rejected; reason: {1}'
280
255
"bold-underline-blink":
281
256
"bold-underline-blink-standout",
284
259
# Rebuild focus and non-focus widgets using current properties
286
261
# Base part of a client. Name!
287
262
base = '{name}: '.format(name=self.properties["Name"])
288
263
if not self.properties["Enabled"]:
289
264
message = "DISABLED"
265
self.using_timer(False)
290
266
elif self.properties["ApprovalPending"]:
291
267
timeout = datetime.timedelta(milliseconds
292
268
= self.properties
294
270
last_approval_request = isoformat_to_datetime(
295
271
self.properties["LastApprovalRequest"])
296
272
if last_approval_request is not None:
297
timer = timeout - (datetime.datetime.utcnow()
298
- last_approval_request)
273
timer = max(timeout - (datetime.datetime.utcnow()
274
- last_approval_request),
275
datetime.timedelta())
300
277
timer = datetime.timedelta()
301
278
if self.properties["ApprovedByDefault"]:
304
281
message = "Denial in {0}. (a)pprove?"
305
282
message = message.format(unicode(timer).rsplit(".", 1)[0])
283
self.using_timer(True)
306
284
elif self.properties["LastCheckerStatus"] != 0:
307
# When checker has failed, print a timer until client expires
285
# When checker has failed, show timer until client expires
308
286
expires = self.properties["Expires"]
309
287
if expires == "":
310
288
timer = datetime.timedelta(0)
312
expires = datetime.datetime.strptime(expires,
313
'%Y-%m-%dT%H:%M:%S.%f')
314
timer = expires - datetime.datetime.utcnow()
290
expires = (datetime.datetime.strptime
291
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
292
timer = max(expires - datetime.datetime.utcnow(),
293
datetime.timedelta())
315
294
message = ('A checker has failed! Time until client'
316
295
' gets disabled: {0}'
317
296
.format(unicode(timer).rsplit(".", 1)[0]))
297
self.using_timer(True)
319
299
message = "enabled"
300
self.using_timer(False)
320
301
self._text = "{0}{1}".format(base, message)
322
303
if not urwid.supports_unicode():
323
304
self._text = self._text.encode("ascii", "replace")
324
305
textlist = [("normal", self._text)]
342
323
return True # Keep calling this
344
def delete(self, *args, **kwargs):
325
def delete(self, **kwargs):
345
326
if self._update_timer_callback_tag is not None:
346
327
gobject.source_remove(self._update_timer_callback_tag)
347
328
self._update_timer_callback_tag = None
350
331
self.match_objects = ()
351
332
if self.delete_hook is not None:
352
333
self.delete_hook(self)
353
return super(MandosClientWidget, self).delete(*args, **kwargs)
334
return super(MandosClientWidget, self).delete(**kwargs)
355
336
def render(self, maxcolrow, focus=False):
356
337
"""Render differently if we have focus.
401
def property_changed(self, property=None, value=None,
382
def property_changed(self, property=None, **kwargs):
403
383
"""Call self.update() if old value is not new value.
404
384
This overrides the method from MandosClientPropertyCache"""
405
385
property_name = unicode(property)
406
386
old_value = self.properties.get(property_name)
407
387
super(MandosClientWidget, self).property_changed(
408
property=property, value=value, *args, **kwargs)
388
property=property, **kwargs)
409
389
if self.properties.get(property_name) != old_value:
415
395
"down" key presses, thus not allowing any containing widgets to
416
396
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)
398
def keypress(self, *args, **kwargs):
399
ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
420
400
if ret in ("up", "down"):