diff options
-rw-r--r-- | client/src/leap/soledad/client/secrets.py | 122 | ||||
-rw-r--r-- | client/src/leap/soledad/client/shared_db.py | 30 | ||||
-rw-r--r-- | common/src/leap/soledad/common/errors.py | 61 |
3 files changed, 49 insertions, 164 deletions
diff --git a/client/src/leap/soledad/client/secrets.py b/client/src/leap/soledad/client/secrets.py index e2a5a1d7..a72aac0d 100644 --- a/client/src/leap/soledad/client/secrets.py +++ b/client/src/leap/soledad/client/secrets.py @@ -33,7 +33,6 @@ from hashlib import sha256 from leap.soledad.common import soledad_assert from leap.soledad.common import soledad_assert_type from leap.soledad.common import document -from leap.soledad.common import errors from leap.soledad.client import events from leap.soledad.client.crypto import encrypt_sym, decrypt_sym @@ -81,6 +80,7 @@ class BootstrapSequenceError(SecretsException): # Secrets handler # + class SoledadSecrets(object): """ @@ -162,17 +162,12 @@ class SoledadSecrets(object): :param shared_db: The shared database that stores user secrets. :type shared_db: leap.soledad.client.shared_db.SoledadSharedDatabase """ - # XXX removed since not in use - # We will pick the first secret available. - # param secret_id: The id of the storage secret to be used. - self._uuid = uuid self._userid = userid self._passphrase = passphrase self._secrets_path = secrets_path self._shared_db = shared_db self._secrets = {} - self._secret_id = None def bootstrap(self): @@ -197,47 +192,19 @@ class SoledadSecrets(object): # STAGE 1 - verify if secrets exist locally if not self._has_secret(): # try to load from local storage. - # STAGE 2 - there are no secrets in local storage, so try to fetch - # encrypted secrets from server. - logger.info( - 'Trying to fetch cryptographic secrets from shared recovery ' - 'database...') - - # --- start of atomic operation in shared db --- - - # obtain lock on shared db - token = timeout = None - try: - token, timeout = self._shared_db.lock() - except errors.AlreadyLockedError: - raise BootstrapSequenceError('Database is already locked.') - except errors.LockTimedOutError: - raise BootstrapSequenceError('Lock operation timed out.') + # STAGE 2 - there are no secrets in local storage and this is the + # first time we are running soledad with the specified + # secrets_path. Try to fetch encrypted secrets from + # server. + self._download_crypto_secrets() - self._get_or_gen_crypto_secrets() + if not self._has_secret(): - # release the lock on shared db - try: - self._shared_db.unlock(token) - self._shared_db.close() - except errors.NotLockedError: - # for some reason the lock expired. Despite that, secret - # loading or generation/storage must have been executed - # successfully, so we pass. - pass - except errors.InvalidTokenError: - # here, our lock has not only expired but also some other - # client application has obtained a new lock and is currently - # doing its thing in the shared database. Using the same - # reasoning as above, we assume everything went smooth and - # pass. - pass - except Exception as e: - logger.error("Unhandled exception when unlocking shared " - "database.") - logger.exception(e) - - # --- end of atomic operation in shared db --- + # STAGE 3 - there are no secrets in server also, so we want to + # generate the secrets and store them in the remote + # db. + self._gen_crypto_secrets() + self._upload_crypto_secrets() def _has_secret(self): """ @@ -295,13 +262,14 @@ class SoledadSecrets(object): self._store_secrets() self._put_secrets_in_shared_db() - def _get_or_gen_crypto_secrets(self): + def _download_crypto_secrets(self): """ - Retrieves or generates the crypto secrets. - - :raises BootstrapSequenceError: Raised when unable to store secrets in - shared database. + Downloads the crypto secrets. """ + logger.info( + 'Trying to fetch cryptographic secrets from shared recovery ' + 'database...') + if self._shared_db.syncable: doc = self._get_secrets_from_shared_db() else: @@ -314,31 +282,39 @@ class SoledadSecrets(object): _, active_secret = self._import_recovery_document(doc.content) self._maybe_set_active_secret(active_secret) self._store_secrets() # save new secrets in local file - else: - # STAGE 3 - there are no secrets in server also, so - # generate a secret and store it in remote db. - logger.info( - 'No cryptographic secrets found, creating new ' - ' secrets...') - self.set_secret_id(self._gen_secret()) - if self._shared_db.syncable: + def _gen_crypto_secrets(self): + """ + Generate the crypto secrets. + """ + logger.info('No cryptographic secrets found, creating new secrets...') + secret_id = self._gen_secret() + self.set_secret_id(secret_id) + + def _upload_crypto_secrets(self): + """ + Send crypto secrets to shared db. + + :raises BootstrapSequenceError: Raised when unable to store secrets in + shared database. + """ + if self._shared_db.syncable: + try: + self._put_secrets_in_shared_db() + except Exception as ex: + # storing generated secret in shared db failed for + # some reason, so we erase the generated secret and + # raise. try: - self._put_secrets_in_shared_db() - except Exception as ex: - # storing generated secret in shared db failed for - # some reason, so we erase the generated secret and - # raise. - try: - os.unlink(self._secrets_path) - except OSError as e: - if e.errno != errno.ENOENT: - # no such file or directory - logger.exception(e) - logger.exception(ex) - raise BootstrapSequenceError( - 'Could not store generated secret in the shared ' - 'database, bailing out...') + os.unlink(self._secrets_path) + except OSError as e: + if e.errno != errno.ENOENT: + # no such file or directory + logger.exception(e) + logger.exception(ex) + raise BootstrapSequenceError( + 'Could not store generated secret in the shared ' + 'database, bailing out...') # # Shared DB related methods diff --git a/client/src/leap/soledad/client/shared_db.py b/client/src/leap/soledad/client/shared_db.py index 6abf8ea3..a1d95fbe 100644 --- a/client/src/leap/soledad/client/shared_db.py +++ b/client/src/leap/soledad/client/shared_db.py @@ -151,33 +151,3 @@ class SoledadSharedDatabase(http_database.HTTPDatabase, TokenBasedAuth): http_database.HTTPDatabase.__init__(self, url, document_factory, creds) self._uuid = uuid - - def lock(self): - """ - Obtain a lock on document with id C{doc_id}. - - :return: A tuple containing the token to unlock and the timeout until - lock expiration. - :rtype: (str, float) - - :raise HTTPError: Raised if any HTTP error occurs. - """ - if self.syncable: - res, headers = self._request_json( - 'PUT', ['lock', self._uuid], body={}) - return res['token'], res['timeout'] - else: - return None, None - - def unlock(self, token): - """ - Release the lock on shared database. - - :param token: The token returned by a previous call to lock(). - :type token: str - - :raise HTTPError: - """ - if self.syncable: - _, _ = self._request_json( - 'DELETE', ['lock', self._uuid], params={'token': token}) diff --git a/common/src/leap/soledad/common/errors.py b/common/src/leap/soledad/common/errors.py index 0b6bb4e6..76a7240d 100644 --- a/common/src/leap/soledad/common/errors.py +++ b/common/src/leap/soledad/common/errors.py @@ -71,67 +71,6 @@ class InvalidAuthTokenError(errors.Unauthorized): # -# LockResource errors -# - -@register_exception -class InvalidTokenError(SoledadError): - - """ - Exception raised when trying to unlock shared database with invalid token. - """ - - wire_description = "unlock unauthorized" - status = 401 - - -@register_exception -class NotLockedError(SoledadError): - - """ - Exception raised when trying to unlock shared database when it is not - locked. - """ - - wire_description = "lock not found" - status = 404 - - -@register_exception -class AlreadyLockedError(SoledadError): - - """ - Exception raised when trying to lock shared database but it is already - locked. - """ - - wire_description = "lock is locked" - status = 403 - - -@register_exception -class LockTimedOutError(SoledadError): - - """ - Exception raised when timing out while trying to lock the shared database. - """ - - wire_description = "lock timed out" - status = 408 - - -@register_exception -class CouldNotObtainLockError(SoledadError): - - """ - Exception raised when timing out while trying to lock the shared database. - """ - - wire_description = "error obtaining lock" - status = 500 - - -# # SoledadBackend errors # u1db error statuses also have to be updated http_errors.ERROR_STATUSES = set( |