From f0117969b19e05d568a108b12390c47a011576f6 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Thu, 8 Jun 2017 11:14:38 +0200 Subject: [feat] push private key updates into nicknym Deal as well with sending key if key is outdated in the providers nicknym. - Resolves: #8819, #8832 --- src/leap/bitmask/core/mail_services.py | 125 ++++++++++++++------------------- src/leap/bitmask/core/service.py | 2 + 2 files changed, 55 insertions(+), 72 deletions(-) (limited to 'src/leap/bitmask/core') diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py index 16b20ed3..d1ccdb95 100644 --- a/src/leap/bitmask/core/mail_services.py +++ b/src/leap/bitmask/core/mail_services.py @@ -29,8 +29,6 @@ from collections import namedtuple from twisted.application import service from twisted.internet import defer -from twisted.internet import reactor -from twisted.internet import task from twisted.logger import Logger from leap.common.events import catalog, emit_async @@ -74,6 +72,8 @@ class ImproperlyConfigured(Exception): class SoledadContainer(Container): + log = Logger() + def __init__(self, service=None, basedir=DEFAULT_BASEDIR): self._basedir = os.path.expanduser(basedir) self._usermap = UserMap() @@ -105,6 +105,14 @@ class SoledadContainer(Container): 'soledad': soledad} self.service.trigger_hook('on_new_soledad_instance', **data) + self.log.debug('Syncing soledad for the first time...') + d = soledad.sync() + d.addCallbacks( + lambda _: + self.service.trigger_hook('on_soledad_first_sync', **data), + lambda _: + self.log.failure('Something failed on soledad first sync')) + def _create_soledad_instance(self, uuid, passphrase, soledad_path, server_url, cert_file, token): # setup soledad info @@ -230,10 +238,6 @@ class KeymanagerContainer(Container): keymanager = self._create_keymanager_instance( userid, token, uuid, soledad) super(KeymanagerContainer, self).add_instance(userid, keymanager) - d = self._get_or_generate_keys(keymanager, userid) - d.addCallback(self._on_keymanager_ready_cb, userid, soledad) - d.addCallback(lambda _: self._set_status(userid, "on", keys="found")) - return d def set_remote_auth_token(self, userid, token): self.get_instance(userid).token = token @@ -243,87 +247,58 @@ class KeymanagerContainer(Container): return {'status': 'off', 'error': None, 'keys': None} return self._status[userid] - def _set_status(self, address, status, error=None, keys=None): - self._status[address] = {"status": status, - "error": error, "keys": keys} - emit_async(catalog.MAIL_STATUS_CHANGED, address) - - def _on_keymanager_ready_cb(self, keymanager, userid, soledad): - data = {'userid': userid, 'soledad': soledad, 'keymanager': keymanager} - self.service.trigger_hook('on_new_keymanager_instance', **data) - - def _get_or_generate_keys(self, keymanager, userid): - - def _get_key(_): - self.log.info('Looking up private key for %s' % userid) - return keymanager.get_key(userid, private=True, fetch_remote=False) + def get_or_generate_keys(self, userid): + keymanager = self.get_instance(userid) def _found_key(key): self.log.info('Found key: %r' % key) + return key def _if_not_found_generate(failure): failure.trap(KeyNotFound) self.log.info('Key not found, generating key for %s' % (userid,)) self._set_status(userid, "starting", keys="generating") d = keymanager.gen_key() - d.addCallbacks(_send_key, _log_key_error("generating")) + d.addErrback(_log_key_error) return d - def _send_key(ignored): - # ---------------------------------------------------------------- - # It might be the case that we have generated a key-pair - # but this hasn't been successfully uploaded. How do we know that? - # XXX Should this be a method of bonafide instead? - # ----------------------------------------------------------------- - self.log.info('Key generated for %s' % userid) - - if not keymanager.token: - self.log.debug( - 'Token not available, scheduling ' - 'a new key sending attempt...') - return task.deferLater(reactor, 5, _send_key, None) - - self.log.info('Sending public key to server') - d = keymanager.send_key() - d.addCallbacks( - lambda _: self.log.info('Key sent to server'), - _log_key_error("sending")) - return d + def _log_key_error(failure): + self.log.failure('Error while generating key!') + error = "Error generating key: %s" % failure.getErrorMessage() + self._set_status(userid, "failure", error=error) + return failure - def _log_key_error(step): - def log_error(failure): - self.log.error('Error while %s key!' % step) - self.log.failure('error!') - error = "Error generating key: %s" % failure.getErrorMessage() - self._set_status(userid, "failure", error=error) - return failure - return log_error - - def _sync_if_never_synced(ever_synced): - if ever_synced: - self.log.debug('Soledad has synced in the past') - return defer.succeed(None) - - self.log.debug('Soledad has never synced') - - if not keymanager.token: - self.log.debug('No token to sync now, scheduling a new check') - d = task.deferLater(reactor, 5, keymanager.ever_synced) - d.addCallback(_sync_if_never_synced) - return d - - self.log.debug('Syncing soledad for the first time...') - self._set_status(userid, "starting", keys="sync") - return keymanager._soledad.sync() - - self.log.debug('Checking if soledad has ever synced...') - d = keymanager.ever_synced() - d.addCallback(_sync_if_never_synced) - d.addCallback(_get_key) + self.log.info('Looking up private key for %s' % userid) + d = keymanager.get_key(userid, private=True, fetch_remote=False) d.addCallbacks(_found_key, _if_not_found_generate) - d.addCallback(lambda _: keymanager) + d.addCallback(self._on_keymanager_ready_cb, keymanager, userid) + self._set_status(userid, "on", keys="found") return d + @defer.inlineCallbacks + def send_if_outdated_key_in_nicknym(self, userid): + keymanager = self.get_instance(userid) + key = yield keymanager.get_key(userid, fetch_remote=False) + try: + remote = yield keymanager._nicknym.fetch_key_with_address(userid) + except Exception: + remote = {} + + if (keymanager.OPENPGP_KEY not in remote or + key.key_data != remote[KeyManager.OPENPGP_KEY]): + yield keymanager.send_key() + + def _set_status(self, address, status, error=None, keys=None): + self._status[address] = {"status": status, + "error": error, "keys": keys} + emit_async(catalog.MAIL_STATUS_CHANGED, address) + + def _on_keymanager_ready_cb(self, key, keymanager, userid): + soledad = keymanager._soledad + data = {'userid': userid, 'soledad': soledad, 'keymanager': keymanager} + self.service.trigger_hook('on_new_keymanager_instance', **data) + return key + def _create_keymanager_instance(self, userid, token, uuid, soledad): user, provider = userid.split('@') nickserver_uri = self._get_nicknym_uri(provider) @@ -384,6 +359,12 @@ class KeymanagerService(HookableService): token = self.tokens.get(user) container.add_instance(user, token, uuid, soledad) + def hook_on_soledad_first_sync(self, **kw): + userid = kw['user'] + d = self._container.get_or_generate_keys(userid) + d.addCallback( + lambda _: self._container.send_if_outdated_key_in_nicknym(userid)) + def hook_on_bonafide_auth(self, **kw): userid = kw['username'] provider = _get_provider_from_full_userid(userid) diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index 310ac08e..0a3ac6bd 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -178,6 +178,8 @@ class BitmaskBackend(configurable.ConfigurableService): if sol: sol.register_hook( 'on_new_soledad_instance', listener='keymanager') + sol.register_hook( + 'on_soledad_first_sync', listener='keymanager') # XXX this might not be the right place for hooking the sessions. # If we want to be offline, we need to authenticate them after -- cgit v1.2.3