summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2014-10-16 15:19:54 +0200
committerKali Kaneko <kali@leap.se>2015-02-11 14:03:17 -0400
commitd19e0d3b3b7a51d2e51800d41f53899254005661 (patch)
tree9fed32eda8a7c8172cc14eef7545519bcb375165
parent133b72e2546ebabb1384583aec313e544aff69e2 (diff)
add syncable property to shared db
-rw-r--r--client/src/leap/soledad/client/api.py20
-rw-r--r--client/src/leap/soledad/client/secrets.py59
-rw-r--r--client/src/leap/soledad/client/shared_db.py31
3 files changed, 73 insertions, 37 deletions
diff --git a/client/src/leap/soledad/client/api.py b/client/src/leap/soledad/client/api.py
index ff6257b2..7886f397 100644
--- a/client/src/leap/soledad/client/api.py
+++ b/client/src/leap/soledad/client/api.py
@@ -113,7 +113,7 @@ class Soledad(object):
def __init__(self, uuid, passphrase, secrets_path, local_db_path,
server_url, cert_file,
- auth_token=None, defer_encryption=False):
+ auth_token=None, defer_encryption=False, syncable=True):
"""
Initialize configuration, cryptographic keys and dbs.
@@ -151,6 +151,11 @@ class Soledad(object):
inline while syncing.
:type defer_encryption: bool
+ :param syncable:
+ If set to ``False``, this database will not attempt to synchronize
+ with remote replicas (default is ``True``)
+ :type syncable: bool
+
:raise BootstrapSequenceError:
Raised when the secret generation and storage on server sequence
has failed for some reason.
@@ -179,13 +184,15 @@ class Soledad(object):
self._secrets_path = secrets_path
# Initialize shared recovery database
- self.init_shared_db(server_url, uuid, self._creds)
+ self.init_shared_db(server_url, uuid, self._creds, syncable=syncable)
# The following can raise BootstrapSequenceError, that will be
# propagated upwards.
self._init_secrets()
self._init_u1db_sqlcipher_backend()
- self._init_u1db_syncer()
+
+ if syncable:
+ self._init_u1db_syncer()
#
# initialization/destruction methods
@@ -467,15 +474,14 @@ class Soledad(object):
# ISharedSecretsStorage
#
- def init_shared_db(self, server_url, uuid, creds):
- # XXX should assert that server_url begins with https
- # Otherwise u1db target will fail.
+ def init_shared_db(self, server_url, uuid, creds, syncable=True):
shared_db_url = urlparse.urljoin(server_url, SHARED_DB_NAME)
self.shared_db = SoledadSharedDatabase.open_database(
shared_db_url,
uuid,
creds=creds,
- create=False) # db should exist at this point.
+ create=False, # db should exist at this point.
+ syncable=syncable)
def _set_secrets_path(self, secrets_path):
self._secrets.secrets_path = secrets_path
diff --git a/client/src/leap/soledad/client/secrets.py b/client/src/leap/soledad/client/secrets.py
index 93f8c25d..81ccb114 100644
--- a/client/src/leap/soledad/client/secrets.py
+++ b/client/src/leap/soledad/client/secrets.py
@@ -289,9 +289,12 @@ class SoledadSecrets(object):
:raises BootstrapSequenceError: Raised when unable to store secrets in
shared database.
"""
- doc = self._get_secrets_from_shared_db()
+ if self._shared_db.syncable:
+ doc = self._get_secrets_from_shared_db()
+ else:
+ doc = None
- if doc:
+ if doc is not None:
logger.info(
'Found cryptographic secrets in shared recovery '
'database.')
@@ -308,21 +311,24 @@ class SoledadSecrets(object):
'No cryptographic secrets found, creating new '
' secrets...')
self.set_secret_id(self._gen_secret())
- 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.
+
+ if self._shared_db.syncable:
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...')
+ 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...')
#
# Shared DB related methods
@@ -434,7 +440,8 @@ class SoledadSecrets(object):
'contents.')
# include secrets in the secret pool.
secret_count = 0
- for secret_id, encrypted_secret in data[self.STORAGE_SECRETS_KEY].items():
+ secrets = data[self.STORAGE_SECRETS_KEY].items()
+ for secret_id, encrypted_secret in secrets:
if secret_id not in self._secrets:
try:
self._secrets[secret_id] = \
@@ -664,8 +671,8 @@ class SoledadSecrets(object):
self._secrets_path = secrets_path
secrets_path = property(
- _get_secrets_path,
- _set_secrets_path,
+ _get_secrets_path,
+ _set_secrets_path,
doc='The path for the file containing the encrypted symmetric secret.')
@property
@@ -689,7 +696,7 @@ class SoledadSecrets(object):
Return the secret for remote storage.
"""
key_start = 0
- key_end = self.REMOTE_STORAGE_SECRET_LENGTH
+ key_end = self.REMOTE_STORAGE_SECRET_LENGTH
return self.storage_secret[key_start:key_end]
#
@@ -703,8 +710,10 @@ class SoledadSecrets(object):
:return: The local storage secret.
:rtype: str
"""
- pwd_start = self.REMOTE_STORAGE_SECRET_LENGTH + self.SALT_LENGTH
- pwd_end = self.REMOTE_STORAGE_SECRET_LENGTH + self.LOCAL_STORAGE_SECRET_LENGTH
+ secret_len = self.REMOTE_STORAGE_SECRET_LENGTH
+ lsecret_len = self.LOCAL_STORAGE_SECRET_LENGTH
+ pwd_start = secret_len + self.SALT_LENGTH
+ pwd_end = secret_len + lsecret_len
return self.storage_secret[pwd_start:pwd_end]
def _get_local_storage_salt(self):
@@ -731,9 +740,9 @@ class SoledadSecrets(object):
buflen=32, # we need a key with 256 bits (32 bytes)
)
- #
- # sync db key
- #
+ #
+ # sync db key
+ #
def _get_sync_db_salt(self):
"""
diff --git a/client/src/leap/soledad/client/shared_db.py b/client/src/leap/soledad/client/shared_db.py
index 31c4e8e8..7ec71991 100644
--- a/client/src/leap/soledad/client/shared_db.py
+++ b/client/src/leap/soledad/client/shared_db.py
@@ -26,6 +26,9 @@ from leap.soledad.client.auth import TokenBasedAuth
# Soledad shared database
# ----------------------------------------------------------------------------
+# TODO could have a hierarchy of soledad exceptions.
+
+
class NoTokenForAuth(Exception):
"""
No token was found for token-based authentication.
@@ -38,6 +41,12 @@ class Unauthorized(Exception):
"""
+class ImproperlyConfiguredError(Exception):
+ """
+ Wrong parameters in the database configuration.
+ """
+
+
class SoledadSharedDatabase(http_database.HTTPDatabase, TokenBasedAuth):
"""
This is a shared recovery database that enables users to store their
@@ -46,6 +55,8 @@ class SoledadSharedDatabase(http_database.HTTPDatabase, TokenBasedAuth):
# TODO: prevent client from messing with the shared DB.
# TODO: define and document API.
+ syncable = True
+
#
# Token auth methods.
#
@@ -82,7 +93,7 @@ class SoledadSharedDatabase(http_database.HTTPDatabase, TokenBasedAuth):
#
@staticmethod
- def open_database(url, uuid, create, creds=None):
+ def open_database(url, uuid, create, creds=None, syncable=True):
# TODO: users should not be able to create the shared database, so we
# have to remove this from here in the future.
"""
@@ -101,8 +112,13 @@ class SoledadSharedDatabase(http_database.HTTPDatabase, TokenBasedAuth):
:return: The shared database in the given url.
:rtype: SoledadSharedDatabase
"""
+ if syncable and not url.startswith('https://'):
+ raise ImproperlyConfiguredError(
+ "Remote soledad server must be an https URI")
db = SoledadSharedDatabase(url, uuid, creds=creds)
- db.open(create)
+ db.syncable = syncable
+ if syncable:
+ db.open(create)
return db
@staticmethod
@@ -145,9 +161,14 @@ class SoledadSharedDatabase(http_database.HTTPDatabase, TokenBasedAuth):
:raise HTTPError: Raised if any HTTP error occurs.
"""
- res, headers = self._request_json('PUT', ['lock', self._uuid],
- body={})
- return res['token'], res['timeout']
+ # TODO ----- if the shared_db is not syncable, should not
+ # attempt to resolve.
+ 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):
"""