summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2017-02-25 08:53:38 -0300
committerdrebs <drebs@leap.se>2017-02-25 08:53:38 -0300
commitb433c1ed736f5d4c19da4cdb21108a02459ca7fd (patch)
tree57058c396516dd1c63ad5dd40f7c8fc713c5e496
parenta2e29d8bebbe7809dc10195982a205ed3709459c (diff)
[refactor] pass soledad object to client secrets api
In order to be able to change passphrase, token and offline status of soledad from the bitmask client api, the secrets api also has to be able to use up-to-date values when encrypting/decrypting secrets and uploading/downloading them to the server. This commit makes public some soledad attributes that were previously "private" (i.e. used to start with "_" and were not meant to be accessed from outside), and passes the whole soledad object to the client secrets api. This makes the code cleaner and also allows for always getting newest values of soledad attributes.
-rw-r--r--client/src/leap/soledad/client/_secrets/__init__.py25
-rw-r--r--client/src/leap/soledad/client/_secrets/crypto.py7
-rw-r--r--client/src/leap/soledad/client/_secrets/storage.py40
-rw-r--r--client/src/leap/soledad/client/_secrets/util.py4
-rw-r--r--client/src/leap/soledad/client/api.py57
-rw-r--r--testing/tests/client/test_aux_methods.py12
-rw-r--r--testing/tests/client/test_deprecated_crypto.py2
-rw-r--r--testing/tests/client/test_secrets.py10
-rw-r--r--testing/tests/server/test_server.py2
9 files changed, 60 insertions, 99 deletions
diff --git a/client/src/leap/soledad/client/_secrets/__init__.py b/client/src/leap/soledad/client/_secrets/__init__.py
index 78cfae5e..43541e16 100644
--- a/client/src/leap/soledad/client/_secrets/__init__.py
+++ b/client/src/leap/soledad/client/_secrets/__init__.py
@@ -43,16 +43,11 @@ class Secrets(EmitMixin):
'local_secret': 448, # local_secret to derive a local_key for storage
}
- def __init__(self, uuid, passphrase, url, local_path, get_token, userid,
- shared_db=None):
- self._uuid = uuid
- self._passphrase = passphrase
- self._userid = userid
+ def __init__(self, soledad):
+ self._soledad = soledad
self._secrets = {}
- self.crypto = SecretsCrypto(self.get_passphrase)
- self.storage = SecretsStorage(
- uuid, self.get_passphrase, url, local_path, get_token, userid,
- shared_db=shared_db)
+ self.crypto = SecretsCrypto(soledad)
+ self.storage = SecretsStorage(soledad)
self._bootstrap()
#
@@ -83,6 +78,8 @@ class Secrets(EmitMixin):
if encrypted['version'] < self.crypto.VERSION or force_storage:
# TODO: what should we do if it's the first run and remote save
# fails?
+ # TODO: we have to actually update the encrypted version before
+ # saving, we are currently not doing it.
self.storage.save_local(encrypted)
self.storage.save_remote(encrypted)
@@ -112,15 +109,7 @@ class Secrets(EmitMixin):
data = {'secret': encrypted, 'version': 2}
return data
- def get_passphrase(self):
- return self._passphrase.encode('utf-8')
-
- @property
- def passphrase(self):
- return self.get_passphrase()
-
- def change_passphrase(self, new_passphrase):
- self._passphrase = new_passphrase
+ def store_secrets(self):
encrypted = self.crypto.encrypt(self._secrets)
self.storage.save_local(encrypted)
self.storage.save_remote(encrypted)
diff --git a/client/src/leap/soledad/client/_secrets/crypto.py b/client/src/leap/soledad/client/_secrets/crypto.py
index 02d7dc02..fa7aaca0 100644
--- a/client/src/leap/soledad/client/_secrets/crypto.py
+++ b/client/src/leap/soledad/client/_secrets/crypto.py
@@ -34,11 +34,12 @@ class SecretsCrypto(object):
VERSION = 2
- def __init__(self, get_pass):
- self._get_pass = get_pass
+ def __init__(self, soledad):
+ self._soledad = soledad
def _get_key(self, salt):
- key = scrypt.hash(self._get_pass(), salt, buflen=32)
+ passphrase = self._soledad.passphrase.encode('utf8')
+ key = scrypt.hash(passphrase, salt, buflen=32)
return key
#
diff --git a/client/src/leap/soledad/client/_secrets/storage.py b/client/src/leap/soledad/client/_secrets/storage.py
index 5fde8988..bb74dba3 100644
--- a/client/src/leap/soledad/client/_secrets/storage.py
+++ b/client/src/leap/soledad/client/_secrets/storage.py
@@ -33,29 +33,26 @@ logger = getLogger(__name__)
class SecretsStorage(EmitMixin):
- def __init__(self, uuid, get_pass, url, local_path, get_token, userid,
- shared_db=None):
- self._uuid = uuid
- self._get_pass = get_pass
- self._local_path = local_path
- self._get_token = get_token
- self._userid = userid
-
- self._shared_db = shared_db or self._init_shared_db(url, self._creds)
+ def __init__(self, soledad):
+ self._soledad = soledad
+ self._shared_db = self._soledad.shared_db or self._init_shared_db()
self.__remote_doc = None
@property
def _creds(self):
- return {'token': {'uuid': self._uuid, 'token': self._get_token()}}
+ uuid = self._soledad.uuid
+ token = self._soledad.token
+ return {'token': {'uuid': uuid, 'token': token}}
#
# local storage
#
def load_local(self):
- logger.info("trying to load secrets from disk: %s" % self._local_path)
+ path = self._soledad.secrets_path
+ logger.info("trying to load secrets from disk: %s" % path)
try:
- with open(self._local_path, 'r') as f:
+ with open(path, 'r') as f:
encrypted = json.loads(f.read())
logger.info("secrets loaded successfully from disk")
return encrypted
@@ -64,23 +61,26 @@ class SecretsStorage(EmitMixin):
return None
def save_local(self, encrypted):
+ path = self._soledad.secrets_path
json_data = json.dumps(encrypted)
- with open(self._local_path, 'w') as f:
+ with open(path, 'w') as f:
f.write(json_data)
#
# remote storage
#
- def _init_shared_db(self, url, creds):
- url = urlparse.urljoin(url, SHARED_DB_NAME)
- db = SoledadSharedDatabase.open_database(
- url, self._uuid, creds=creds)
- self._shared_db = db
+ def _init_shared_db(self):
+ url = urlparse.urljoin(self._soledad.server_url, SHARED_DB_NAME)
+ uuid = self._soledad.uuid
+ creds = self._creds
+ db = SoledadSharedDatabase.open_database(url, uuid, creds)
+ return db
def _remote_doc_id(self):
- passphrase = self._get_pass()
- text = '%s%s' % (passphrase, self._uuid)
+ passphrase = self._soledad.passphrase.encode('utf8')
+ uuid = self._soledad.uuid
+ text = '%s%s' % (passphrase, uuid)
digest = sha256(text).hexdigest()
return digest
diff --git a/client/src/leap/soledad/client/_secrets/util.py b/client/src/leap/soledad/client/_secrets/util.py
index 0dcdd3af..75418518 100644
--- a/client/src/leap/soledad/client/_secrets/util.py
+++ b/client/src/leap/soledad/client/_secrets/util.py
@@ -27,7 +27,9 @@ class EmitMixin(object):
@property
def _user_data(self):
- return {'uuid': self._uuid, 'userid': self._userid}
+ uuid = self._soledad.uuid
+ userid = self._soledad.userid
+ return {'uuid': uuid, 'userid': userid}
def emit(verb):
diff --git a/client/src/leap/soledad/client/api.py b/client/src/leap/soledad/client/api.py
index 16569ec2..4be38cf1 100644
--- a/client/src/leap/soledad/client/api.py
+++ b/client/src/leap/soledad/client/api.py
@@ -177,27 +177,25 @@ class Soledad(object):
some reason.
"""
# store config params
- self._uuid = uuid
- self._passphrase = passphrase
+ self.uuid = uuid
+ self.passphrase = passphrase
+ self.secrets_path = secrets_path
self._local_db_path = local_db_path
- self._server_url = server_url
- self._secrets_path = None
+ self.server_url = server_url
+ self.shared_db = shared_db
+ self.token = auth_token
+ self.offline = offline
+
self._dbsyncer = None
- self._offline = offline
# configure SSL certificate
global SOLEDAD_CERT
SOLEDAD_CERT = cert_file
- self.set_token(auth_token)
-
self._init_config_with_defaults()
self._init_working_dirs()
- self._secrets_path = secrets_path
-
- self._init_secrets(shared_db=shared_db)
-
+ self._secrets = Secrets(self)
self._crypto = SoledadCrypto(self._secrets.remote_secret)
try:
@@ -214,14 +212,6 @@ class Soledad(object):
self._dbpool.close()
raise
- def _get_offline(self):
- return self._offline
-
- def _set_offline(self, offline):
- self._offline = offline
-
- offline = property(_get_offline, _set_offline)
-
#
# initialization/destruction methods
#
@@ -230,7 +220,7 @@ class Soledad(object):
"""
Initialize configuration using default values for missing params.
"""
- soledad_assert_type(self._passphrase, unicode)
+ soledad_assert_type(self.passphrase, unicode)
def initialize(attr, val):
return ((getattr(self, attr, None) is None) and
@@ -241,7 +231,7 @@ class Soledad(object):
initialize("_local_db_path", os.path.join(
self.default_prefix, self.local_db_file_name))
# initialize server_url
- soledad_assert(self._server_url is not None,
+ soledad_assert(self.server_url is not None,
'Missing URL for Soledad server.')
def _init_working_dirs(self):
@@ -255,14 +245,6 @@ class Soledad(object):
for path in paths:
create_path_if_not_exists(path)
- def _init_secrets(self, shared_db=None):
- """
- Initialize Soledad secrets.
- """
- self._secrets = Secrets(
- self._uuid, self._passphrase, self._server_url, self._secrets_path,
- self.get_token, self.userid, shared_db=shared_db)
-
def _init_u1db_sqlcipher_backend(self):
"""
Initialize the U1DB SQLCipher database for local storage.
@@ -647,10 +629,6 @@ class Soledad(object):
return self._local_db_path
@property
- def uuid(self):
- return self._uuid
-
- @property
def userid(self):
return self.uuid
@@ -687,7 +665,7 @@ class Soledad(object):
generation before the synchronization was performed.
:rtype: twisted.internet.defer.Deferred
"""
- sync_url = urlparse.urljoin(self._server_url, 'user-%s' % self.uuid)
+ sync_url = urlparse.urljoin(self.server_url, 'user-%s' % self.uuid)
if not self._dbsyncer:
return
creds = {'token': {'uuid': self.uuid, 'token': self.token}}
@@ -748,14 +726,6 @@ class Soledad(object):
"""
return self.sync_lock.locked
- def set_token(self, token):
- self._token = token
-
- def get_token(self):
- return self._token
-
- token = property(get_token, set_token, doc='The authentication Token.')
-
#
# ISecretsStorage
#
@@ -779,7 +749,8 @@ class Soledad(object):
:raise NoStorageSecret: Raised if there's no storage secret available.
"""
- self._secrets.change_passphrase(new_passphrase)
+ self.passphrase = new_passphrase
+ self._secrets.store_secrets()
#
# Raw SQLCIPHER Queries
diff --git a/testing/tests/client/test_aux_methods.py b/testing/tests/client/test_aux_methods.py
index a08f7d36..729aa28a 100644
--- a/testing/tests/client/test_aux_methods.py
+++ b/testing/tests/client/test_aux_methods.py
@@ -33,7 +33,7 @@ class AuxMethodsTestCase(BaseSoledadTest):
def test__init_dirs(self):
sol = self._soledad_instance(prefix='_init_dirs')
local_db_dir = os.path.dirname(sol.local_db_path)
- secrets_path = os.path.dirname(sol.secrets.storage._local_path)
+ secrets_path = os.path.dirname(sol.secrets_path)
self.assertTrue(os.path.isdir(local_db_dir))
self.assertTrue(os.path.isdir(secrets_path))
@@ -63,8 +63,8 @@ class AuxMethodsTestCase(BaseSoledadTest):
# instantiate without initializing so we just test
# _init_config_with_defaults()
sol = SoledadMock()
- sol._passphrase = u''
- sol._server_url = ''
+ sol.passphrase = u''
+ sol.server_url = ''
sol._init_config_with_defaults()
# assert value of local_db_path
self.assertEquals(
@@ -84,11 +84,11 @@ class AuxMethodsTestCase(BaseSoledadTest):
cert_file=None)
self.assertEqual(
os.path.join(self.tempdir, 'value_3'),
- sol.secrets.storage._local_path)
+ sol.secrets_path)
self.assertEqual(
os.path.join(self.tempdir, 'value_2'),
sol.local_db_path)
- self.assertEqual('value_1', sol._server_url)
+ self.assertEqual('value_1', sol.server_url)
sol.close()
@inlineCallbacks
@@ -128,5 +128,5 @@ class AuxMethodsTestCase(BaseSoledadTest):
Assert passphrase getter works fine.
"""
sol = self._soledad_instance()
- self.assertEqual('123', sol._passphrase)
+ self.assertEqual('123', sol.passphrase)
sol.close()
diff --git a/testing/tests/client/test_deprecated_crypto.py b/testing/tests/client/test_deprecated_crypto.py
index 8c711c22..1af1a130 100644
--- a/testing/tests/client/test_deprecated_crypto.py
+++ b/testing/tests/client/test_deprecated_crypto.py
@@ -51,7 +51,7 @@ class DeprecatedCryptoTest(SoledadWithCouchServerMixin, TestCaseWithServer):
self._soledad_instance(user=user, server_url=server_url))
self.make_app()
- remote = self.request_state._create_database(replica_uid=client._uuid)
+ remote = self.request_state._create_database(replica_uid=client.uuid)
remote = CouchDatabase.open_database(
urljoin(self.couch_url, 'user-' + user),
create=True)
diff --git a/testing/tests/client/test_secrets.py b/testing/tests/client/test_secrets.py
index bbeb1fc2..18ff458b 100644
--- a/testing/tests/client/test_secrets.py
+++ b/testing/tests/client/test_secrets.py
@@ -121,12 +121,10 @@ class SecretsCryptoTestCase(unittest.TestCase):
}
def setUp(self):
- def _get_pass():
- return '123'
- self._crypto = SecretsCrypto(_get_pass)
-
- def test__get_pass(self):
- self.assertEqual(self._crypto._get_pass(), '123')
+ class Soledad(object):
+ passphrase = '123'
+ soledad = Soledad()
+ self._crypto = SecretsCrypto(soledad)
def test__get_key(self):
salt = 'abc'
diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py
index 647ef5a8..4a5ec43f 100644
--- a/testing/tests/server/test_server.py
+++ b/testing/tests/server/test_server.py
@@ -135,7 +135,7 @@ class EncryptedSyncTestCase(
user=user,
prefix='x',
auth_token='auth-token',
- secrets_path=sol1._secrets_path,
+ secrets_path=sol1.secrets_path,
passphrase=passphrase)
# ensure remote db exists before syncing