/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-monitor

  • Committer: Teddy Hogeborn
  • Date: 2012-06-22 23:33:56 UTC
  • Revision ID: teddy@recompile.se-20120622233356-odoqqt2ki2gssn37
* Makefile (check): Also check mandos-ctl.
* mandos-ctl: All options taking a time interval argument can now take
              an RFC 3339 duration.
  (rfc3339_duration_to_delta): New function.
  (string_to_delta): Try rfc3339_duration_to_delta first.
  (main): New "--check" option.
* mandos-ctl.xml (SYNOPSIS, OPTIONS): Document new "--check" option.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
domain = 'se.recompile'
56
56
server_interface = domain + '.Mandos'
57
57
client_interface = domain + '.Mandos.Client'
58
 
version = "1.5.3"
 
58
version = "1.6.0"
59
59
 
60
60
# Always run in monochrome mode
61
61
urwid.curses_display.curses.has_colors = lambda : False
86
86
    properties and calls a hook function when any of them are
87
87
    changed.
88
88
    """
89
 
    def __init__(self, proxy_object=None, *args, **kwargs):
 
89
    def __init__(self, proxy_object=None, properties=None, **kwargs):
90
90
        self.proxy = proxy_object # Mandos Client proxy object
91
 
        
92
 
        self.properties = dict()
 
91
        self.properties = dict() if properties is None else properties
93
92
        self.property_changed_match = (
94
93
            self.proxy.connect_to_signal("PropertyChanged",
95
 
                                         self.property_changed,
 
94
                                         self._property_changed,
96
95
                                         client_interface,
97
96
                                         byte_arrays=True))
98
97
        
99
 
        self.properties.update(
100
 
            self.proxy.GetAll(client_interface,
101
 
                              dbus_interface = dbus.PROPERTIES_IFACE))
102
 
 
103
 
        #XXX This breaks good super behaviour
104
 
#        super(MandosClientPropertyCache, self).__init__(
105
 
#            *args, **kwargs)
 
98
        if properties is None:
 
99
            self.properties.update(
 
100
                self.proxy.GetAll(client_interface,
 
101
                                  dbus_interface
 
102
                                  = dbus.PROPERTIES_IFACE))
 
103
        
 
104
        super(MandosClientPropertyCache, self).__init__(**kwargs)
 
105
    
 
106
    def _property_changed(self, property, value):
 
107
        """Helper which takes positional arguments"""
 
108
        return self.property_changed(property=property, value=value)
106
109
    
107
110
    def property_changed(self, property=None, value=None):
108
111
        """This is called whenever we get a PropertyChanged signal
111
114
        # Update properties dict with new value
112
115
        self.properties[property] = value
113
116
    
114
 
    def delete(self, *args, **kwargs):
 
117
    def delete(self):
115
118
        self.property_changed_match.remove()
116
 
        super(MandosClientPropertyCache, self).__init__(
117
 
            *args, **kwargs)
118
119
 
119
120
 
120
121
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
122
123
    """
123
124
    
124
125
    def __init__(self, server_proxy_object=None, update_hook=None,
125
 
                 delete_hook=None, logger=None, *args, **kwargs):
 
126
                 delete_hook=None, logger=None, **kwargs):
126
127
        # Called on update
127
128
        self.update_hook = update_hook
128
129
        # Called on delete
133
134
        self.logger = logger
134
135
        
135
136
        self._update_timer_callback_tag = None
136
 
        self._update_timer_callback_lock = 0
137
137
        
138
138
        # The widget shown normally
139
139
        self._text_widget = urwid.Text("")
140
140
        # The widget shown when we have focus
141
141
        self._focus_text_widget = urwid.Text("")
142
 
        super(MandosClientWidget, self).__init__(
143
 
            update_hook=update_hook, delete_hook=delete_hook,
144
 
            *args, **kwargs)
 
142
        super(MandosClientWidget, self).__init__(**kwargs)
145
143
        self.update()
146
144
        self.opened = False
147
145
        
148
 
        last_checked_ok = isoformat_to_datetime(self.properties
149
 
                                                ["LastCheckedOK"])
150
 
        
151
 
        if self.properties ["LastCheckerStatus"] != 0:
152
 
            self.using_timer(True)
153
 
        
154
 
        if self.need_approval:
155
 
            self.using_timer(True)
156
 
        
157
146
        self.match_objects = (
158
147
            self.proxy.connect_to_signal("CheckerCompleted",
159
148
                                         self.checker_completed,
178
167
        #self.logger('Created client {0}'
179
168
        #            .format(self.properties["Name"]))
180
169
    
181
 
    def property_changed(self, property=None, value=None):
182
 
        super(self, MandosClientWidget).property_changed(property,
183
 
                                                         value)
184
 
        if property == "ApprovalPending":
185
 
            using_timer(bool(value))
186
 
        if property == "LastCheckerStatus":
187
 
            using_timer(value != 0)
188
 
            #self.logger('Checker for client {0} (command "{1}") was '
189
 
            #            ' successful'.format(self.properties["Name"],
190
 
            #                                 command))
191
 
    
192
170
    def using_timer(self, flag):
193
171
        """Call this method with True or False when timer should be
194
172
        activated or deactivated.
195
173
        """
196
 
        old = self._update_timer_callback_lock
197
 
        if flag:
198
 
            self._update_timer_callback_lock += 1
199
 
        else:
200
 
            self._update_timer_callback_lock -= 1
201
 
        if old == 0 and self._update_timer_callback_lock:
 
174
        if flag and self._update_timer_callback_tag is None:
202
175
            # Will update the shown timer value every second
203
176
            self._update_timer_callback_tag = (gobject.timeout_add
204
177
                                               (1000,
205
178
                                                self.update_timer))
206
 
        elif old and self._update_timer_callback_lock == 0:
 
179
        elif not (flag or self._update_timer_callback_tag is None):
207
180
            gobject.source_remove(self._update_timer_callback_tag)
208
181
            self._update_timer_callback_tag = None
209
182
    
251
224
            message = 'Client {0} will get its secret in {1} seconds'
252
225
        self.logger(message.format(self.properties["Name"],
253
226
                                   timeout/1000))
254
 
        self.using_timer(True)
255
227
    
256
228
    def rejected(self, reason):
257
229
        self.logger('Client {0} was rejected; reason: {1}'
283
255
                          "bold-underline-blink":
284
256
                              "bold-underline-blink-standout",
285
257
                          }
286
 
 
 
258
        
287
259
        # Rebuild focus and non-focus widgets using current properties
288
 
 
 
260
        
289
261
        # Base part of a client. Name!
290
262
        base = '{name}: '.format(name=self.properties["Name"])
291
263
        if not self.properties["Enabled"]:
292
264
            message = "DISABLED"
 
265
            self.using_timer(False)
293
266
        elif self.properties["ApprovalPending"]:
294
267
            timeout = datetime.timedelta(milliseconds
295
268
                                         = self.properties
297
270
            last_approval_request = isoformat_to_datetime(
298
271
                self.properties["LastApprovalRequest"])
299
272
            if last_approval_request is not None:
300
 
                timer = timeout - (datetime.datetime.utcnow()
301
 
                                   - last_approval_request)
 
273
                timer = max(timeout - (datetime.datetime.utcnow()
 
274
                                       - last_approval_request),
 
275
                            datetime.timedelta())
302
276
            else:
303
277
                timer = datetime.timedelta()
304
278
            if self.properties["ApprovedByDefault"]:
306
280
            else:
307
281
                message = "Denial in {0}. (a)pprove?"
308
282
            message = message.format(unicode(timer).rsplit(".", 1)[0])
 
283
            self.using_timer(True)
309
284
        elif self.properties["LastCheckerStatus"] != 0:
310
285
            # When checker has failed, show timer until client expires
311
286
            expires = self.properties["Expires"]
314
289
            else:
315
290
                expires = (datetime.datetime.strptime
316
291
                           (expires, '%Y-%m-%dT%H:%M:%S.%f'))
317
 
                timer = expires - datetime.datetime.utcnow()
 
292
                timer = max(expires - datetime.datetime.utcnow(),
 
293
                            datetime.timedelta())
318
294
            message = ('A checker has failed! Time until client'
319
295
                       ' gets disabled: {0}'
320
296
                       .format(unicode(timer).rsplit(".", 1)[0]))
 
297
            self.using_timer(True)
321
298
        else:
322
299
            message = "enabled"
 
300
            self.using_timer(False)
323
301
        self._text = "{0}{1}".format(base, message)
324
 
            
 
302
        
325
303
        if not urwid.supports_unicode():
326
304
            self._text = self._text.encode("ascii", "replace")
327
305
        textlist = [("normal", self._text)]
344
322
        self.update()
345
323
        return True             # Keep calling this
346
324
    
347
 
    def delete(self, *args, **kwargs):
 
325
    def delete(self, **kwargs):
348
326
        if self._update_timer_callback_tag is not None:
349
327
            gobject.source_remove(self._update_timer_callback_tag)
350
328
            self._update_timer_callback_tag = None
353
331
        self.match_objects = ()
354
332
        if self.delete_hook is not None:
355
333
            self.delete_hook(self)
356
 
        return super(MandosClientWidget, self).delete(*args, **kwargs)
 
334
        return super(MandosClientWidget, self).delete(**kwargs)
357
335
    
358
336
    def render(self, maxcolrow, focus=False):
359
337
        """Render differently if we have focus.
401
379
        else:
402
380
            return key
403
381
    
404
 
    def property_changed(self, property=None, value=None,
405
 
                         *args, **kwargs):
 
382
    def property_changed(self, property=None, **kwargs):
406
383
        """Call self.update() if old value is not new value.
407
384
        This overrides the method from MandosClientPropertyCache"""
408
385
        property_name = unicode(property)
409
386
        old_value = self.properties.get(property_name)
410
387
        super(MandosClientWidget, self).property_changed(
411
 
            property=property, value=value, *args, **kwargs)
 
388
            property=property, **kwargs)
412
389
        if self.properties.get(property_name) != old_value:
413
390
            self.update()
414
391
 
418
395
    "down" key presses, thus not allowing any containing widgets to
419
396
    use them as an excuse to shift focus away from this widget.
420
397
    """
421
 
    def keypress(self, maxcolrow, key):
422
 
        ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
 
398
    def keypress(self, *args, **kwargs):
 
399
        ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
423
400
        if ret in ("up", "down"):
424
401
            return
425
402
        return ret
640
617
                                               logger
641
618
                                               =self.log_message),
642
619
                            path=path)
643
 
 
 
620
        
644
621
        self.refresh()
645
622
        self._input_callback_tag = (gobject.io_add_watch
646
623
                                    (sys.stdin.fileno(),