=== modified file 'DBUS-API' --- DBUS-API 2010-09-12 03:00:40 +0000 +++ DBUS-API 2010-09-26 17:36:30 +0000 @@ -76,25 +76,26 @@ "clients.conf", in which case they are fully documented in mandos-clients.conf(5). - | Name | Type | Access | clients.conf | - |----------------------+------+------------+---------------------| - | ApprovedByDefault | b | Read/Write | approved_by_default | - | ApprovalDelay (a) | t | Read/Write | approval_delay | - | ApprovalDuration (a) | t | Read/Write | approval_duration | - | ApprovalPending (b) | b | Read | N/A | - | Checker | s | Read/Write | checker | - | CheckerRunning (c) | b | Read/Write | N/A | - | Created (d) | s | Read | N/A | - | Enabled (e) | b | Read/Write | N/A | - | Fingerprint | s | Read | fingerprint | - | Host | s | Read/Write | host | - | Interval (a) | t | Read/Write | interval | - | LastCheckedOK (f) | s | Read/Write | N/A | - | LastEnabled (g) | s | Read | N/A | - | Name | s | Read | (Section name) | - | ObjectPath | o | Read | N/A | - | Secret (h) | ay | Write | secret (or secfile) | - | Timeout (a) | t | Read/Write | timeout | + | Name | Type | Access | clients.conf | + |-------------------------+------+------------+---------------------| + | ApprovedByDefault | b | Read/Write | approved_by_default | + | ApprovalDelay (a) | t | Read/Write | approval_delay | + | ApprovalDuration (a) | t | Read/Write | approval_duration | + | ApprovalPending (b) | b | Read | N/A | + | Checker | s | Read/Write | checker | + | CheckerRunning (c) | b | Read/Write | N/A | + | Created (d) | s | Read | N/A | + | Enabled (e) | b | Read/Write | N/A | + | Fingerprint | s | Read | fingerprint | + | Host | s | Read/Write | host | + | Interval (a) | t | Read/Write | interval | + | LastApprovalRequest (f) | s | Read | N/A | + | LastCheckedOK (g) | s | Read/Write | N/A | + | LastEnabled (h) | s | Read | N/A | + | Name | s | Read | (Section name) | + | ObjectPath | o | Read | N/A | + | Secret (i) | ay | Write | secret (or secfile) | + | Timeout (a) | t | Read/Write | timeout | a) Represented as milliseconds. @@ -108,17 +109,20 @@ e) Setting this property is equivalent to calling Enable() or Disable(). - f) The last time a checker was successful, as a RFC 3339 string, or + f) The time of the last approval request, as a RFC 3339 string, or + an empty string if this has not happened. + + g) The last time a checker was successful, as a RFC 3339 string, or an empty string if this has not happened. Setting this property is equivalent to calling CheckedOK(), i.e. the current time is set, regardless of the string sent. Please always use an empty string when setting this property, to allow for possible future expansion. - g) The last time this client was enabled, as a RFC 3339 string, or + h) The last time this client was enabled, as a RFC 3339 string, or an empty string if this has not happened. - h) A raw byte array, not hexadecimal digits. + i) A raw byte array, not hexadecimal digits. ** Signals *** CheckerCompleted(n: Exitcode, x: Waitstatus, s: Command) === modified file 'TODO' --- TODO 2010-09-25 21:24:49 +0000 +++ TODO 2010-09-26 17:36:30 +0000 @@ -106,6 +106,7 @@ *** Properties popup ** Nicer crashes. Stack traces Messes up shell. *** Print a nice "We are sorry" message, save stack trace to log. +** Show timeout countdown for approval * mandos-keygen ** TODO Loop until passwords match when run interactively === modified file 'mandos' --- mandos 2010-09-25 23:52:17 +0000 +++ mandos 2010-09-26 17:36:30 +0000 @@ -246,7 +246,7 @@ checker: subprocess.Popen(); a running checker process used to see if the client lives. 'None' if no process is running. - checker_callback_tag: - '' - + checker_callback_tag: a gobject event source tag, or None checker_command: string; External command which is run to check if client lives. %() expansions are done at runtime with vars(self) as dict, so that for @@ -255,12 +255,13 @@ created: datetime.datetime(); (UTC) object creation current_checker_command: string; current running checker_command disable_hook: If set, called by disable() as disable_hook(self) - disable_initiator_tag: - '' - + disable_initiator_tag: a gobject event source tag, or None enabled: bool() fingerprint: string (40 or 32 hexadecimal digits); used to uniquely identify the client host: string; available for use by the checker command interval: datetime.timedelta(); How often to start a new checker + last_approval_request: datetime.datetime(); (UTC) or None last_checked_ok: datetime.datetime(); (UTC) or None last_enabled: datetime.datetime(); (UTC) name: string; from the config file, used in log messages and @@ -321,6 +322,7 @@ self.host = config.get(u"host", u"") self.created = datetime.datetime.utcnow() self.enabled = False + self.last_approval_request = None self.last_enabled = None self.last_checked_ok = None self.timeout = string_to_delta(config[u"timeout"]) @@ -422,6 +424,9 @@ (self.timeout_milliseconds(), self.disable)) + def need_approval(self): + self.last_approval_request = datetime.datetime.utcnow() + def start_checker(self): """Start a new checker subprocess if one is not running. @@ -815,6 +820,15 @@ variant_level=1))) return r + def need_approval(self, *args, **kwargs): + r = Client.need_approval(self, *args, **kwargs) + # Emit D-Bus signal + self.PropertyChanged( + dbus.String(u"LastApprovalRequest"), + (self._datetime_to_dbus(self.last_approval_request, + variant_level=1))) + return r + def start_checker(self, *args, **kwargs): old_checker = self.checker if self.checker is not None: @@ -895,15 +909,15 @@ @dbus.service.signal(_interface, signature=u"tb") def NeedApproval(self, timeout, default): "D-Bus signal" - pass + return self.need_approval() ## Methods - + # Approve - method @dbus.service.method(_interface, in_signature=u"b") def Approve(self, value): self.approve(value) - + # CheckedOK - method @dbus.service.method(_interface) def CheckedOK(self): @@ -1029,6 +1043,15 @@ return dbus.String(self._datetime_to_dbus(self .last_checked_ok)) + # LastApprovalRequest - property + @dbus_service_property(_interface, signature=u"s", access=u"read") + def LastApprovalRequest_dbus_property(self): + if self.last_approval_request is None: + return dbus.String(u"") + return dbus.String(self. + _datetime_to_dbus(self + .last_approval_request)) + # Timeout - property @dbus_service_property(_interface, signature=u"t", access=u"readwrite") === modified file 'mandos-monitor' --- mandos-monitor 2010-09-25 23:52:17 +0000 +++ mandos-monitor 2010-09-26 17:36:30 +0000 @@ -102,6 +102,7 @@ self.logger = logger self._update_timer_callback_tag = None + self._update_timer_callback_lock = 0 self.last_checker_failed = False # The widget shown normally @@ -113,6 +114,25 @@ *args, **kwargs) self.update() self.opened = False + + last_checked_ok = isoformat_to_datetime(self.properties + [u"LastCheckedOK"]) + if last_checked_ok is None: + self.last_checker_failed = True + else: + self.last_checker_failed = ((datetime.datetime.utcnow() + - last_checked_ok) + > datetime.timedelta + (milliseconds= + self.properties + [u"Interval"])) + + if self.last_checker_failed: + self.using_timer(True) + + if self.need_approval: + self.using_timer(True) + self.proxy.connect_to_signal(u"CheckerCompleted", self.checker_completed, client_interface, @@ -133,28 +153,35 @@ self.rejected, client_interface, byte_arrays=True) - last_checked_ok = isoformat_to_datetime(self.properties - [u"LastCheckedOK"]) - if last_checked_ok is None: - self.last_checker_failed = True + + def property_changed(self, property=None, value=None): + super(self, MandosClientWidget).property_changed(property, + value) + if property == u"ApprovalPending": + using_timer(bool(value)) + + def using_timer(self, flag): + """Call this method with True or False when timer should be + activated or deactivated. + """ + old = self._update_timer_callback_lock + if flag: + self._update_timer_callback_lock += 1 else: - self.last_checker_failed = ((datetime.datetime.utcnow() - - last_checked_ok) - > datetime.timedelta - (milliseconds= - self.properties - [u"Interval"])) - if self.last_checker_failed: + self._update_timer_callback_lock -= 1 + if old == 0 and self._update_timer_callback_lock: self._update_timer_callback_tag = (gobject.timeout_add (1000, self.update_timer)) + elif old and self._update_timer_callback_lock == 0: + gobject.source_remove(self._update_timer_callback_tag) + self._update_timer_callback_tag = None def checker_completed(self, exitstatus, condition, command): if exitstatus == 0: if self.last_checker_failed: self.last_checker_failed = False - gobject.source_remove(self._update_timer_callback_tag) - self._update_timer_callback_tag = None + self.using_timer(False) #self.logger(u'Checker for client %s (command "%s")' # u' was successful' # % (self.properties[u"Name"], command)) @@ -163,9 +190,7 @@ # Checker failed if not self.last_checker_failed: self.last_checker_failed = True - self._update_timer_callback_tag = (gobject.timeout_add - (1000, - self.update_timer)) + self.using_timer(True) if os.WIFEXITED(condition): self.logger(u'Checker for client %s (command "%s")' u' failed with exit code %s' @@ -202,6 +227,7 @@ message = u'Client %s will get its secret in %s seconds' self.logger(message % (self.properties[u"Name"], timeout/1000)) + self.using_timer(True) def rejected(self, reason): self.logger(u'Client %s was rejected; reason: %s' @@ -242,10 +268,21 @@ if not self.properties[u"Enabled"]: message = u"DISABLED" elif self.properties[u"ApprovalPending"]: + timeout = datetime.timedelta(milliseconds + = self.properties + [u"ApprovalDelay"]) + last_approval_request = isoformat_to_datetime( + self.properties[u"LastApprovalRequest"]) + if last_approval_request is not None: + timer = timeout - (datetime.datetime.utcnow() + - last_approval_request) + else: + timer = datetime.timedelta() if self.properties[u"ApprovedByDefault"]: - message = u"Connection established to client. (d)eny?" + message = u"Approval in %s. (d)eny?" else: - message = u"Seeks approval to send secret. (a)pprove?" + message = u"Denial in %s. (a)pprove?" + message = message % unicode(timer).rsplit(".", 1)[0] elif self.last_checker_failed: timeout = datetime.timedelta(milliseconds = self.properties