diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/soledad/__init__.py | 69 | ||||
| -rw-r--r-- | src/leap/soledad/tests/__init__.py | 34 | ||||
| -rw-r--r-- | src/leap/soledad/tests/test_crypto.py | 21 | ||||
| -rw-r--r-- | src/leap/soledad/tests/test_leap_backend.py | 1 | ||||
| -rw-r--r-- | src/leap/soledad/tests/test_soledad.py | 46 | 
5 files changed, 64 insertions, 107 deletions
| diff --git a/src/leap/soledad/__init__.py b/src/leap/soledad/__init__.py index 407e28f0..4373c53b 100644 --- a/src/leap/soledad/__init__.py +++ b/src/leap/soledad/__init__.py @@ -170,7 +170,7 @@ class Soledad(object):      """      def __init__(self, uuid, passphrase, secrets_path, local_db_path, -                 server_url, cert_file, auth_token=None, bootstrap=True): +                 server_url, cert_file, auth_token=None):          """          Initialize configuration, cryptographic keys and dbs. @@ -193,9 +193,6 @@ class Soledad(object):          @type cert_file: str          @param auth_token: Authorization token for accessing remote databases.          @type auth_token: str -        @param bootstrap: True/False, should bootstrap this instance? Mostly -            for testing purposes but can be useful for initialization control. -        @type bootstrap: bool          """          # get config params          self._uuid = uuid @@ -208,8 +205,8 @@ class Soledad(object):          self._set_token(auth_token)          # configure SSL certificate          shared_db.SOLEDAD_CERT = cert_file -        if bootstrap: -            self._bootstrap() +        # initiate bootstrap sequence +        self._bootstrap()      def _init_config(self, secrets_path, local_db_path, server_url):          """ @@ -241,18 +238,15 @@ class Soledad(object):          Soledad Client bootstrap is the following sequence of stages: -        * Stage 0 - Local environment setup. +        * stage 0 - local environment setup.              - directory initialization. -            - gnupg wrapper initialization. -        * Stage 1 - Keys generation/loading: -            - if keys exists locally, load them. -            - else, if keys exists in server, download them. -            - else, generate keys. -        * Stage 2 - Keys synchronization: -            - if keys exist in server, confirm we have the same keys -              locally. -            - else, send keys to server. -        * Stage 3 - Database initialization. +            - crypto submodule initialization +        * stage 1 - secret generation/loading: +            - if secrets exist locally, load them. +            - else, if secrets exist in server, download them. +            - else, generate a new secret. +        * stage 2 - store secrets in server. +        * stage 3 - database initialization.          This method decides which bootstrap stages have already been performed          and performs the missing ones in order. @@ -261,27 +255,31 @@ class Soledad(object):          #       interrupted).          # TODO: write tests for bootstrap stages.          # TODO: log each bootstrap step. -        # Stage 0  - Local environment setup +        # stage 0  - socal environment setup          self._init_dirs()          self._crypto = SoledadCrypto(self) -        # Stage 1 - Keys generation/loading -        if not self._has_secret(): +        # stage 1 - secret generation/loading +        if not self._has_secret():  # try to load from local storage.              logger.info(                  'Trying to fetch cryptographic secrets from shared recovery '                  'database...') +            # there are no secrets in local storage, so try to fetch encrypted +            # secrets from server.              doc = self._get_secrets_from_shared_db() -            if not doc: -                logger.info( -                    'No cryptographic secrets found, creating new secrets...') -                self._init_keys() -            else: +            if doc: +                # found secrets in server, so import them.                  logger.info(                      'Found cryptographic secrets in shared recovery '                      'database.')                  self.import_recovery_document(                          doc.content[self.SECRET_KEY],                          passphrase=self._passphrase) -        # Stage 2 - Keys synchronization +            else: +                # there are no secrets in server also, so generate a secret. +                logger.info( +                    'No cryptographic secrets found, creating new secrets...') +                self._set_secret_id(self._gen_secret()) +        # Stage 2 - storage of encrypted secrets in the server.          self._put_secrets_in_shared_db()          # Stage 3 - Local database initialization          self._init_db() @@ -299,17 +297,6 @@ class Soledad(object):              logger.info('Creating directory: %s.' % path)              mkdir_p(path) -    def _init_keys(self): -        """ -        Generate (if needed) and load secret for symmetric encryption. -        """ -        events.signal(events.events_pb2.SOLEDAD_CREATING_KEYS, self._uuid) -        # load/generate secret -        if not self._has_secret(): -            self._set_secret_id(self._gen_secret()) -        events.signal( -            events.events_pb2.SOLEDAD_DONE_CREATING_KEYS, self._uuid) -      def _init_db(self):          """          Initialize the database for local storage. @@ -374,9 +361,9 @@ class Soledad(object):          self._secret_id = secret_id          self._crypto.secret = self._get_storage_secret() -    def _load_secret(self): +    def _load_secrets(self):          """ -        Load symmetric storage secret from local file. +        Load storage secrets from local file.          The content of the file has the following format: @@ -425,7 +412,7 @@ class Soledad(object):              return True          # try to load from disk          try: -            self._load_secret() +            self._load_secrets()              return True          except DecryptionFailed:              logger.error('Could not decrypt storage secret.') @@ -479,6 +466,8 @@ class Soledad(object):                  base64_encode(key)),          }          self._store_secrets() +        events.signal( +            events.events_pb2.SOLEDAD_DONE_CREATING_KEYS, self._uuid)          return secret_id      def _store_secrets(self): diff --git a/src/leap/soledad/tests/__init__.py b/src/leap/soledad/tests/__init__.py index 5cd23e45..6787aa9d 100644 --- a/src/leap/soledad/tests/__init__.py +++ b/src/leap/soledad/tests/__init__.py @@ -3,6 +3,7 @@ Tests to make sure Soledad provides U1DB functionality and more.  """  import u1db +from mock import Mock  from leap.soledad import Soledad @@ -37,31 +38,36 @@ class BaseSoledadTest(BaseLeapTest):                                document_factory=LeapDocument)          # initialize soledad by hand so we can control keys          self._soledad = self._soledad_instance(user=self.email) -        self._soledad._init_dirs() -        #self._soledad._gpg.import_keys(PUBLIC_KEY) -        self._soledad._crypto = SoledadCrypto(self._soledad) -        if not self._soledad._has_secret(): -            self._soledad._gen_secret() -        self._soledad._load_secret() -        self._soledad._init_db()      def tearDown(self):          self._db1.close()          self._db2.close()          self._soledad.close() -    def _soledad_instance(self, user='leap@leap.se', prefix='', -                          bootstrap=False, +    def _soledad_instance(self, user='leap@leap.se', passphrase='123', prefix='',                            secrets_path=Soledad.STORAGE_SECRETS_FILE_NAME, -                          local_db_path='/soledad.u1db'): +                          local_db_path='/soledad.u1db', server_url='', +                          cert_file=None): + +        def _put_doc_side_effect(doc): +            self._doc_put = doc + +        class MockSharedDB(object): + +            get_doc = Mock(return_value=None) +            put_doc = Mock(side_effect=_put_doc_side_effect) + +            def __call__(self): +                return self + +        Soledad._shared_db = MockSharedDB()          return Soledad(              user, -            '123', +            passphrase,              secrets_path=self.tempdir+prefix+secrets_path,              local_db_path=self.tempdir+prefix+local_db_path, -            server_url='',  # Soledad will fail if not given an url. -            cert_file=None, -            bootstrap=bootstrap) +            server_url=server_url,  # Soledad will fail if not given an url. +            cert_file=cert_file)      def assertGetEncryptedDoc(self, db, doc_id, doc_rev, content, has_conflicts):          """Assert that the document in the database looks correct.""" diff --git a/src/leap/soledad/tests/test_crypto.py b/src/leap/soledad/tests/test_crypto.py index 8f9145c8..61c5f5b0 100644 --- a/src/leap/soledad/tests/test_crypto.py +++ b/src/leap/soledad/tests/test_crypto.py @@ -216,9 +216,8 @@ class RecoveryDocumentTestCase(BaseSoledadTest):      def test_import_recovery_document_raw(self):          rd = self._soledad.export_recovery_document(None)          s = self._soledad_instance(user='anotheruser@leap.se', prefix='/2') -        s._init_dirs() -        s._crypto = SoledadCrypto(s)          s.import_recovery_document(rd, None) +        s._set_secret_id(self._soledad._secret_id)          self.assertEqual(self._soledad._uuid,                           s._uuid, 'Failed setting user uuid.')          self.assertEqual(self._soledad._get_storage_secret(), @@ -228,8 +227,6 @@ class RecoveryDocumentTestCase(BaseSoledadTest):      def test_import_recovery_document_crypt(self):          rd = self._soledad.export_recovery_document('123456')          s = self._soledad_instance(user='anotheruser@leap.se', prefix='3') -        s._init_dirs() -        s._crypto = SoledadCrypto(s)          s.import_recovery_document(rd, '123456')          self.assertEqual(self._soledad._uuid,                           s._uuid, 'Failed setting user uuid.') @@ -242,17 +239,5 @@ class CryptoMethodsTestCase(BaseSoledadTest):      def test__gen_secret(self):          sol = self._soledad_instance(user='user@leap.se', prefix='/3') -        sol._init_dirs() -        sol._crypto = SoledadCrypto(sol) -        self.assertFalse(sol._has_secret(), "Should not have a secret at " -                                            "this point") -        sol._gen_secret() -        self.assertTrue(sol._has_secret(), "Could not generate secret.") - -    def test__has_secret(self): -        sol = self._soledad_instance(user='leap@leap.se', prefix='/5') -        sol._init_dirs() -        sol._crypto = SoledadCrypto(sol) -        self.assertFalse(sol._has_secret()) -        sol._gen_secret() -        self.assertTrue(sol._has_secret()) +        self.assertTrue(sol._has_secret(), "Should have a secret at " +                                           "this point") diff --git a/src/leap/soledad/tests/test_leap_backend.py b/src/leap/soledad/tests/test_leap_backend.py index b0e0aaec..b58a5473 100644 --- a/src/leap/soledad/tests/test_leap_backend.py +++ b/src/leap/soledad/tests/test_leap_backend.py @@ -618,6 +618,7 @@ class LeapDatabaseSyncTargetTests(          This test was adapted to decrypt remote content before assert.          """ +        sol = _make_local_db_and_leap_target(self)          docs_by_gen = [              (self.make_document('doc-id', 'replica:1', tests.simple_doc), 10,               'T-sid')] diff --git a/src/leap/soledad/tests/test_soledad.py b/src/leap/soledad/tests/test_soledad.py index 018ce8d7..3f750bed 100644 --- a/src/leap/soledad/tests/test_soledad.py +++ b/src/leap/soledad/tests/test_soledad.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*-  # test_soledad.py  # Copyright (C) 2013 LEAP  # @@ -30,7 +29,6 @@ except ImportError:      import json  # noqa -from mock import Mock  from leap.common.testing.basetest import BaseLeapTest  from leap.soledad.tests import BaseSoledadTest  from leap.soledad import Soledad @@ -56,7 +54,7 @@ class AuxMethodsTestCase(BaseSoledadTest):          #self._soledad._gpg.import_keys(PUBLIC_KEY)          if not sol._has_secret():              sol._gen_secret() -        sol._load_secret() +        sol._load_secrets()          sol._init_db()          from leap.soledad.backends.sqlcipher import SQLCipherDatabase          self.assertIsInstance(sol._db, SQLCipherDatabase) @@ -65,9 +63,10 @@ class AuxMethodsTestCase(BaseSoledadTest):          """          Test if configuration defaults point to the correct place.          """ -        sol = Soledad('leap@leap.se', passphrase='123', bootstrap=False, -                      secrets_path=None, local_db_path=None, -                      server_url='', cert_file=None)  # otherwise Soledad will fail. +        sol = Soledad( +            'leap@leap.se', passphrase='123', +            secrets_path=None, local_db_path=None, +            server_url='', cert_file=None)  # otherwise Soledad will fail.          self.assertEquals(              os.path.join(sol.DEFAULT_PREFIX, Soledad.STORAGE_SECRETS_FILE_NAME),              sol.secrets_path) @@ -79,16 +78,15 @@ class AuxMethodsTestCase(BaseSoledadTest):          """          Test if configuration is correctly read from file.          """ -        sol = Soledad( +        sol = self._soledad_instance(              'leap@leap.se',              passphrase='123', -            bootstrap=False,              secrets_path='value_3',              local_db_path='value_2',              server_url='value_1',              cert_file=None) -        self.assertEqual('value_3', sol.secrets_path) -        self.assertEqual('value_2', sol.local_db_path) +        self.assertEqual(self.tempdir+'value_3', sol.secrets_path) +        self.assertEqual(self.tempdir+'value_2', sol.local_db_path)          self.assertEqual('value_1', sol.server_url) @@ -106,19 +104,10 @@ class SoledadSharedDBTestCase(BaseSoledadTest):          """          Ensure the shared db is queried with the correct doc_id.          """ - -        class MockSharedDB(object): - -            get_doc = Mock(return_value=None) - -            def __call__(self): -                return self - -        self._soledad._shared_db = MockSharedDB()          doc_id = self._soledad._uuid_hash()          self._soledad._get_secrets_from_shared_db()          self.assertTrue( -            self._soledad._shared_db().get_doc.assert_called_once_with( +            self._soledad._shared_db().get_doc.assert_called_with(                  doc_id) is None,              'Wrong doc_id when fetching recovery document.') @@ -126,27 +115,14 @@ class SoledadSharedDBTestCase(BaseSoledadTest):          """          Ensure recovery document is put into shared recover db.          """ - -        def _put_doc_side_effect(doc): -            self._doc_put = doc - -        class MockSharedDB(object): - -            get_doc = Mock(return_value=None) -            put_doc = Mock(side_effect=_put_doc_side_effect) - -            def __call__(self): -                return self - -        self._soledad._shared_db = MockSharedDB()          doc_id = self._soledad._uuid_hash()          self._soledad._put_secrets_in_shared_db()          self.assertTrue( -            self._soledad._shared_db().get_doc.assert_called_once_with( +            self._soledad._shared_db().get_doc.assert_called_with(                  doc_id) is None,              'Wrong doc_id when fetching recovery document.')          self.assertTrue( -            self._soledad._shared_db.put_doc.assert_called_once_with( +            self._soledad._shared_db.put_doc.assert_called_with(                  self._doc_put) is None,              'Wrong document when putting recovery document.')          self.assertTrue( | 
