diff options
Diffstat (limited to 'src/leap/soledad/__init__.py')
-rw-r--r-- | src/leap/soledad/__init__.py | 167 |
1 files changed, 89 insertions, 78 deletions
diff --git a/src/leap/soledad/__init__.py b/src/leap/soledad/__init__.py index 76b5656e..94ebec1a 100644 --- a/src/leap/soledad/__init__.py +++ b/src/leap/soledad/__init__.py @@ -36,12 +36,12 @@ except ImportError: import json # noqa +from xdg import BaseDirectory from hashlib import sha256 from leap.common import events from leap.common.check import leap_assert -from leap.soledad.config import SoledadConfig from leap.soledad.backends import sqlcipher from leap.soledad.backends.leap_backend import ( LeapDocument, @@ -77,7 +77,7 @@ class NotADirectory(Exception): """ -class NoSharedDbUrl(Exception): +class NoServerUrl(Exception): """ Tried to get access to shared recovery database but there's no URL for it. """ @@ -131,9 +131,16 @@ class Soledad(object): Key used to access symmetric keys in recovery documents. """ - def __init__(self, address, passphrase, config_path=None, - secret_path=None, local_db_path=None, - shared_db_url=None, auth_token=None, bootstrap=True): + DEFAULT_PREFIX = os.path.join( + BaseDirectory.xdg_config_home, + 'leap', 'soledad') + """ + Prefix for default values for path. + """ + + def __init__(self, address, passphrase, secret_path=None, + local_db_path=None, server_url=None, auth_token=None, + bootstrap=True): """ Initialize configuration, cryptographic keys and dbs. @@ -142,16 +149,15 @@ class Soledad(object): @param passphrase: The passphrase for locking and unlocking encryption secrets for disk storage. @type passphrase: str - @param config_path: Path for configuration file. - @type config_path: str @param secret_path: Path for storing encrypted key used for symmetric encryption. @type secret_path: str @param local_db_path: Path for local encrypted storage db. @type local_db_path: str - @param shared_db_url: URL for shared Soledad DB for key storage and - unauth retrieval. - @type shared_db_url: str + @param server_url: URL for Soledad server. This is used either to sync + with the user's remote db and to interact with the shared recovery + database. + @type server_url: str @param auth_token: Authorization token for accessing remote databases. @type auth_token: str @param bootstrap: True/False, should bootstrap this instance? Mostly @@ -162,15 +168,33 @@ class Soledad(object): self._address = address self._passphrase = passphrase self._set_token(auth_token) - self._init_config( - config_path=config_path, - secret_path=secret_path, - local_db_path=local_db_path, - shared_db_url=shared_db_url, - ) + self._init_config(secret_path, local_db_path, server_url) if bootstrap: self._bootstrap() + def _init_config(self, secret_path, local_db_path, server_url): + """ + Initialize configuration using default values for missing params. + """ + # initialize secret_path + self._secret_path = secret_path + if self._secret_path is None: + self._secret_path = os.path.join( + self.DEFAULT_PREFIX, 'secret.gpg') + # initialize local_db_path + self._local_db_path = local_db_path + if self._local_db_path is None: + self._local_db_path = os.path.join( + self.DEFAULT_PREFIX, 'soledad.u1db') + # initialize server_url + self._server_url = server_url + if self._server_url is None: + raise NoServerUrl() + + # + # initialization methods + # + def _bootstrap(self): """ Bootstrap local Soledad instance. @@ -225,53 +249,13 @@ class Soledad(object): # Stage 3 - Local database initialization self._init_db() - def _shared_db(self): - """ - Return an instance of the shared recovery database object. - """ - if self._config.get_shared_db_url(): - return SoledadSharedDatabase.open_database( - self._config.get_shared_db_url(), - False, # TODO: eliminate need to create db here. - creds=self._creds) - else: - raise NoSharedDbUrl() - - def _init_config(self, config_path, secret_path, local_db_path, - shared_db_url): - """ - Initialize configuration using SoledadConfig. - - Soledad configuration makes use of BaseLeapConfig to load values from - a file or from default configuration. Parameters passed as arguments - for this method will supersede file and default values. - - @param kwargs: a dictionary with configuration parameter values passed - when instantiating this Soledad instance. - @type kwargs: dict - """ - self._config = SoledadConfig() - if config_path is not None: - self._config.load(path=config_path) - else: - self._config.load(data='') - # overwrite config with passed parameters - if secret_path is not None: - self._config._config_checker.config['secret_path'] = secret_path - if local_db_path is not None: - self._config._config_checker.config['local_db_path'] = \ - local_db_path - if shared_db_url is not None: - self._config._config_checker.config['shared_db_url'] = \ - shared_db_url - def _init_dirs(self): """ Create work directories. """ paths = map( lambda x: os.path.dirname(x), - [self._config.get_local_db_path(), self._config.get_secret_path()]) + [self.local_db_path, self.secret_path]) for path in paths: if not os.path.isfile(path): if not os.path.isdir(path): @@ -302,7 +286,7 @@ class Soledad(object): # TODO: verify if secret for sqlcipher should be the same as the # one for symmetric encryption. self._db = sqlcipher.open( - self._config.get_local_db_path(), + self.local_db_path, self._symkey, create=True, document_factory=LeapDocument, @@ -314,9 +298,9 @@ class Soledad(object): """ self._db.close() - #------------------------------------------------------------------------- - # Management of secret for symmetric encryption - #------------------------------------------------------------------------- + # + # Management of secret for symmetric encryption. + # def _has_symkey(self): """ @@ -328,14 +312,14 @@ class Soledad(object): @rtype: bool """ # does the file exist in disk? - if not os.path.isfile(self._config.get_secret_path()): + if not os.path.isfile(self.secret_path): return False # is it symmetrically encrypted? - with open(self._config.get_secret_path(), 'r') as f: + with open(self.secret_path, 'r') as f: content = f.read() if not self._crypto.is_encrypted_sym(content): raise DocumentNotEncrypted( - "File %s is not encrypted!" % self._config.get_secret_path()) + "File %s is not encrypted!" % self.secret_path) # can we decrypt it? plaintext = self._crypto.decrypt_sym( content, passphrase=self._passphrase) @@ -348,7 +332,7 @@ class Soledad(object): if not self._has_symkey(): raise KeyDoesNotExist("Tried to load key for symmetric " "encryption but it does not exist on disk.") - with open(self._config.get_secret_path()) as f: + with open(self.secret_path) as f: self._symkey = \ self._crypto.decrypt_sym( f.read(), passphrase=self._passphrase) @@ -380,12 +364,12 @@ class Soledad(object): def _store_symkey(self): ciphertext = self._crypto.encrypt_sym( self._symkey, self._passphrase) - with open(self._config.get_secret_path(), 'w') as f: + with open(self.secret_path, 'w') as f: f.write(ciphertext) - #------------------------------------------------------------------------- + # # General crypto utility methods. - #------------------------------------------------------------------------- + # def _has_keys(self): """ @@ -419,6 +403,15 @@ class Soledad(object): """ return sha256('address-%s' % self._address).hexdigest() + def _shared_db(self): + """ + Return an instance of the shared recovery database object. + """ + return SoledadSharedDatabase.open_database( + self.server_url, + False, # TODO: eliminate need to create db here. + creds=self._creds) + def _fetch_keys_from_shared_db(self): """ Retrieve the document with encrypted key material from the shared @@ -451,7 +444,7 @@ class Soledad(object): if doc: remote_symkey = self._crypto.decrypt_sym( doc.content[self.SYMKEY_KEY], - passphrase=self._address_hash()) + passphrase=self._passphrase) leap_assert( remote_symkey == self._symkey, 'Local and remote symmetric secrets differ!') @@ -468,9 +461,9 @@ class Soledad(object): events.signal( events.events_pb2.SOLEDAD_DONE_UPLOADING_KEYS, self._address) - #------------------------------------------------------------------------- - # Document storage, retrieval and sync - #------------------------------------------------------------------------- + # + # Document storage, retrieval and sync. + # # TODO: refactor the following methods to somewhere out of here # (SoledadLocalDatabase, maybe?) @@ -708,7 +701,7 @@ class Soledad(object): """ return self._db.resolve_doc(doc, conflicted_doc_revs) - def sync(self, url): + def sync(self): """ Synchronize the local encrypted replica with a remote replica. @@ -719,7 +712,7 @@ class Soledad(object): performed. @rtype: str """ - local_gen = self._db.sync(url, creds=self._creds, autocreate=True) + local_gen = self._db.sync(self.server_url, creds=self._creds, autocreate=True) events.signal(events.events_pb2.SOLEDAD_DONE_DATA_SYNC, self._address) return local_gen @@ -772,9 +765,9 @@ class Soledad(object): token = property(_get_token, _set_token, doc='The authentication Token.') - #------------------------------------------------------------------------- - # Recovery document export and import - #------------------------------------------------------------------------- + # + # Recovery document export and import methods. + # def export_recovery_document(self, passphrase=None): """ @@ -845,5 +838,23 @@ class Soledad(object): address = property(_get_address, doc='The user address.') + def _get_secret_path(self): + return self._secret_path + + secret_path = property( + _get_secret_path, + doc='The path for the file containing the encrypted symmetric secret.') + + def _get_local_db_path(self): + return self._local_db_path + + local_db_path = property( + _get_local_db_path, + doc='The path for the local database replica.') + + def _get_server_url(self): + return self._server_url -__all__ = ['backends', 'util', 'server', 'shared_db'] + server_url = property( + _get_server_url, + doc='The URL of the Soledad server.') |