diff options
10 files changed, 180 insertions, 165 deletions
| diff --git a/client/src/leap/soledad/client/sync.py b/client/src/leap/soledad/client/sync.py index 5d545a77..c158f2a7 100644 --- a/client/src/leap/soledad/client/sync.py +++ b/client/src/leap/soledad/client/sync.py @@ -29,8 +29,6 @@ Extend u1db Synchronizer with the ability to:  """ -import json -  import logging  import traceback  from threading import Lock diff --git a/client/src/leap/soledad/client/target.py b/client/src/leap/soledad/client/target.py index 01e1231a..12175f48 100644 --- a/client/src/leap/soledad/client/target.py +++ b/client/src/leap/soledad/client/target.py @@ -28,12 +28,10 @@ import logging  import re  import urllib  import threading -import urlparse  from collections import defaultdict  from time import sleep  from uuid import uuid4 -from contextlib import contextmanager  import simplejson as json  from taskthread import TimerTask @@ -44,7 +42,6 @@ from u1db.remote.http_client import _encode_query_parameter, HTTPClientBase  from zope.proxy import ProxyBase  from zope.proxy import sameProxiedObjects, setProxiedObject -from leap.soledad.common import soledad_assert  from leap.soledad.common.document import SoledadDocument  from leap.soledad.client.auth import TokenBasedAuth  from leap.soledad.client.crypto import is_symmetrically_encrypted @@ -87,7 +84,7 @@ class DocumentSyncerThread(threading.Thread):      """      def __init__(self, doc_syncer, release_method, failed_method, -            idx, total, last_request_lock=None, last_callback_lock=None): +                 idx, total, last_request_lock=None, last_callback_lock=None):          """          Initialize a new syncer thread. @@ -246,7 +243,7 @@ class DocumentSyncerPool(object):      """      def __init__(self, raw_url, raw_creds, query_string, headers, -            ensure_callback, stop_method): +                 ensure_callback, stop_method):          """          Initialize the document syncer pool. @@ -279,7 +276,7 @@ class DocumentSyncerPool(object):          self._threads = []      def new_syncer_thread(self, idx, total, last_request_lock=None, -            last_callback_lock=None): +                          last_callback_lock=None):          """          Yield a new document syncer thread. @@ -619,7 +616,7 @@ class HTTPDocumentSyncer(HTTPClientBase, TokenBasedAuth):          self._conn.endheaders()      def _get_doc(self, received, sync_id, last_known_generation, -            last_known_trans_id): +                 last_known_trans_id):          """          Get a sync document from server by means of a POST request. @@ -658,7 +655,7 @@ class HTTPDocumentSyncer(HTTPClientBase, TokenBasedAuth):          return self._response()      def _put_doc(self, sync_id, last_known_generation, last_known_trans_id, -            id, rev, content, gen, trans_id, number_of_docs, doc_idx): +                 id, rev, content, gen, trans_id, number_of_docs, doc_idx):          """          Put a sync document on server by means of a POST request. @@ -765,7 +762,7 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):      #      def __init__(self, url, source_replica_uid=None, creds=None, crypto=None, -            sync_db=None, sync_db_write_lock=None): +                 sync_db=None, sync_db_write_lock=None):          """          Initialize the SoledadSyncTarget. @@ -925,7 +922,7 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):          """          new_generation, new_transaction_id, number_of_changes, doc_id, \              rev, content, gen, trans_id = \ -                self._parse_received_doc_response(response) +            self._parse_received_doc_response(response)          if doc_id is not None:              # decrypt incoming document and insert into local database              # ------------------------------------------------------------- @@ -1134,11 +1131,14 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):          """          self._ensure_callback = ensure_callback -        if defer_decryption: +        if defer_decryption and self._sync_db is not None:              self._sync_exchange_lock.acquire()              self._setup_sync_decr_pool(last_known_generation)              self._setup_sync_watcher()              self._defer_decryption = True +        else: +            # fall back +            defer_decryption = False          self.start() @@ -1149,7 +1149,7 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):          setProxiedObject(self._insert_doc_cb[source_replica_uid],                           return_doc_cb) -        if not self.clear_to_sync(): +        if defer_decryption is True and not self.clear_to_sync():              raise PendingReceivedDocsSyncError          self._ensure_connection() @@ -1171,7 +1171,6 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):              self._raw_url, self._raw_creds, url, headers, ensure_callback,              self.stop)          threads = [] -        last_request_lock = None          last_callback_lock = None          sent = 0          total = len(docs_by_generations) @@ -1227,7 +1226,8 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):              t.doc_syncer.set_request_method(                  'put', sync_id, cur_target_gen, cur_target_trans_id,                  id=doc.doc_id, rev=doc.rev, content=doc_json, gen=gen, -                trans_id=trans_id, number_of_docs=number_of_docs, doc_idx=sent + 1) +                trans_id=trans_id, number_of_docs=number_of_docs, +                doc_idx=sent + 1)              # set the success calback              def _success_callback(idx, total, response): @@ -1251,7 +1251,6 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):              # save thread and append              t.start()              threads.append((t, doc)) -            last_request_lock = t.request_lock              last_callback_lock = t.callback_lock              sent += 1 @@ -1275,7 +1274,7 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):          if last_successful_thread is not None:              response_dict = json.loads(last_successful_thread.response[0])[0]              gen_after_send = response_dict['new_generation'] -            trans_id_after_send  = response_dict['new_transaction_id'] +            trans_id_after_send = response_dict['new_transaction_id']          # get docs from target          if self.stopped is False: @@ -1356,7 +1355,6 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):              # no doc found              return None -      def delete_encrypted_docs_from_db(self, docs_ids):          """          Delete several encrypted documents from the database of symmetrically @@ -1467,7 +1465,7 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth):          decrypter = self._sync_decr_pool          decrypter.decrypt_received_docs() -        done = decrypter.process_decrypted() +        decrypter.process_decrypted()      def _sign_request(self, method, url_query, params):          """ diff --git a/common/src/leap/soledad/common/tests/test_crypto.py b/common/src/leap/soledad/common/tests/test_crypto.py index 1071af14..ccff5e46 100644 --- a/common/src/leap/soledad/common/tests/test_crypto.py +++ b/common/src/leap/soledad/common/tests/test_crypto.py @@ -57,23 +57,23 @@ class EncryptedSyncTestCase(BaseSoledadTest):  class RecoveryDocumentTestCase(BaseSoledadTest):      def test_export_recovery_document_raw(self): -        rd = self._soledad.export_recovery_document() -        secret_id = rd[self._soledad.STORAGE_SECRETS_KEY].items()[0][0] -        secret = rd[self._soledad.STORAGE_SECRETS_KEY][secret_id] -        self.assertEqual(secret_id, self._soledad._secret_id) -        self.assertEqual(secret, self._soledad._secrets[secret_id]) -        self.assertTrue(self._soledad.CIPHER_KEY in secret) -        self.assertTrue(secret[self._soledad.CIPHER_KEY] == 'aes256') -        self.assertTrue(self._soledad.LENGTH_KEY in secret) -        self.assertTrue(self._soledad.SECRET_KEY in secret) +        rd = self._soledad.secrets._export_recovery_document() +        secret_id = rd[self._soledad.secrets.STORAGE_SECRETS_KEY].items()[0][0] +        secret = rd[self._soledad.secrets.STORAGE_SECRETS_KEY][secret_id] +        self.assertEqual(secret_id, self._soledad.secrets._secret_id) +        self.assertEqual(secret, self._soledad.secrets._secrets[secret_id]) +        self.assertTrue(self._soledad.secrets.CIPHER_KEY in secret) +        self.assertTrue(secret[self._soledad.secrets.CIPHER_KEY] == 'aes256') +        self.assertTrue(self._soledad.secrets.LENGTH_KEY in secret) +        self.assertTrue(self._soledad.secrets.SECRET_KEY in secret)      def test_import_recovery_document(self): -        rd = self._soledad.export_recovery_document() +        rd = self._soledad.secrets._export_recovery_document()          s = self._soledad_instance() -        s.import_recovery_document(rd) -        s._set_secret_id(self._soledad._secret_id) -        self.assertEqual(self._soledad._get_storage_secret(), -                         s._get_storage_secret(), +        s.secrets._import_recovery_document(rd) +        s.set_secret_id(self._soledad.secrets._secret_id) +        self.assertEqual(self._soledad.storage_secret, +                         s.storage_secret,                           'Failed settinng secret for symmetric encryption.')          s.close() @@ -83,13 +83,13 @@ class SoledadSecretsTestCase(BaseSoledadTest):      def test__gen_secret(self):          # instantiate and save secret_id          sol = self._soledad_instance(user='user@leap.se') -        self.assertTrue(len(sol._secrets) == 1) +        self.assertTrue(len(sol.secrets._secrets) == 1)          secret_id_1 = sol.secret_id          # assert id is hash of secret          self.assertTrue(              secret_id_1 == hashlib.sha256(sol.storage_secret).hexdigest())          # generate new secret -        secret_id_2 = sol._gen_secret() +        secret_id_2 = sol.secrets._gen_secret()          self.assertTrue(secret_id_1 != secret_id_2)          sol.close()          # re-instantiate @@ -97,18 +97,20 @@ class SoledadSecretsTestCase(BaseSoledadTest):              user='user@leap.se',              secret_id=secret_id_1)          # assert ids are valid -        self.assertTrue(len(sol._secrets) == 2) -        self.assertTrue(secret_id_1 in sol._secrets) -        self.assertTrue(secret_id_2 in sol._secrets) +        self.assertTrue(len(sol.secrets._secrets) == 2) +        self.assertTrue(secret_id_1 in sol.secrets._secrets) +        self.assertTrue(secret_id_2 in sol.secrets._secrets)          # assert format of secret 1          self.assertTrue(sol.storage_secret is not None)          self.assertIsInstance(sol.storage_secret, str) -        self.assertTrue(len(sol.storage_secret) == sol.GENERATED_SECRET_LENGTH) +        secret_length = sol.secrets.LOCAL_STORAGE_SECRET_LENGTH \ +            + sol.secrets.REMOTE_STORAGE_SECRET_LENGTH +        self.assertTrue(len(sol.storage_secret) == secret_length)          # assert format of secret 2 -        sol._set_secret_id(secret_id_2) +        sol.set_secret_id(secret_id_2)          self.assertTrue(sol.storage_secret is not None)          self.assertIsInstance(sol.storage_secret, str) -        self.assertTrue(len(sol.storage_secret) == sol.GENERATED_SECRET_LENGTH) +        self.assertTrue(len(sol.storage_secret) == secret_length)          # assert id is hash of new secret          self.assertTrue(              secret_id_2 == hashlib.sha256(sol.storage_secret).hexdigest()) @@ -117,16 +119,18 @@ class SoledadSecretsTestCase(BaseSoledadTest):      def test__has_secret(self):          sol = self._soledad_instance(              user='user@leap.se', prefix=self.rand_prefix) -        self.assertTrue(sol._has_secret(), "Should have a secret at " -                                           "this point") +        self.assertTrue( +            sol.secrets._has_secret(), +            "Should have a secret at this point")          # setting secret id to None should not interfere in the fact we have a          # secret. -        sol._set_secret_id(None) -        self.assertTrue(sol._has_secret(), "Should have a secret at " -                                           "this point") +        sol.set_secret_id(None) +        self.assertTrue( +            sol.secrets._has_secret(), +            "Should have a secret at this point")          # but not being able to decrypt correctly should -        sol._secrets[sol.secret_id][sol.SECRET_KEY] = None -        self.assertFalse(sol._has_secret()) +        sol.secrets._secrets[sol.secret_id][sol.secrets.SECRET_KEY] = None +        self.assertFalse(sol.secrets._has_secret())          sol.close() diff --git a/common/src/leap/soledad/common/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py index cb5348b4..acd0a54c 100644 --- a/common/src/leap/soledad/common/tests/test_server.py +++ b/common/src/leap/soledad/common/tests/test_server.py @@ -302,6 +302,7 @@ class EncryptedSyncTestCase(              put_doc = mock.Mock(side_effect=_put_doc_side_effect)              lock = mock.Mock(return_value=('atoken', 300))              unlock = mock.Mock() +            close = mock.Mock()              def __call__(self):                  return self @@ -373,9 +374,9 @@ class EncryptedSyncTestCase(          sol2 = self._soledad_instance(prefix='x', auth_token='auth-token')          _, doclist = sol2.get_all_docs()          self.assertEqual([], doclist) -        sol2._secrets_path = sol1.secrets_path -        sol2._load_secrets() -        sol2._set_secret_id(sol1._secret_id) +        sol2.secrets_path = sol1.secrets_path +        sol2.secrets._load_secrets() +        sol2.set_secret_id(sol1.secret_id)          # sync the new instance          sol2._server_url = self.getURL()          sol2.sync() @@ -435,9 +436,9 @@ class EncryptedSyncTestCase(          )          _, doclist = sol2.get_all_docs()          self.assertEqual([], doclist) -        sol2._secrets_path = sol1.secrets_path -        sol2._load_secrets() -        sol2._set_secret_id(sol1._secret_id) +        sol2.secrets_path = sol1.secrets_path +        sol2.secrets._load_secrets() +        sol2.set_secret_id(sol1.secret_id)          # sync the new instance          sol2._server_url = self.getURL()          sol2.sync() @@ -479,9 +480,9 @@ class EncryptedSyncTestCase(          sol2 = self._soledad_instance(prefix='x', auth_token='auth-token')          _, doclist = sol2.get_all_docs()          self.assertEqual([], doclist) -        sol2._secrets_path = sol1.secrets_path -        sol2._load_secrets() -        sol2._set_secret_id(sol1._secret_id) +        sol2.secrets_path = sol1.secrets_path +        sol2.secrets._load_secrets() +        sol2.set_secret_id(sol1.secret_id)          # sync the new instance          sol2._server_url = self.getURL()          sol2.sync() @@ -524,9 +525,9 @@ class EncryptedSyncTestCase(          sol2 = self._soledad_instance(prefix='x', auth_token='auth-token')          _, doclist = sol2.get_all_docs()          self.assertEqual([], doclist) -        sol2._secrets_path = sol1.secrets_path -        sol2._load_secrets() -        sol2._set_secret_id(sol1._secret_id) +        sol2.secrets_path = sol1.secrets_path +        sol2.secrets._load_secrets() +        sol2.set_secret_id(sol1.secret_id)          # sync the new instance          sol2._server_url = self.getURL()          sol2.sync() diff --git a/common/src/leap/soledad/common/tests/test_soledad.py b/common/src/leap/soledad/common/tests/test_soledad.py index 11e43423..12bfbc3e 100644 --- a/common/src/leap/soledad/common/tests/test_soledad.py +++ b/common/src/leap/soledad/common/tests/test_soledad.py @@ -29,8 +29,9 @@ from leap.soledad.common.tests import (  from leap import soledad  from leap.soledad.common.document import SoledadDocument  from leap.soledad.common.crypto import WrongMac -from leap.soledad.client import Soledad, PassphraseTooShort -from leap.soledad.client.crypto import SoledadCrypto +from leap.soledad.client import Soledad +from leap.soledad.client.sqlcipher import SQLCipherDatabase +from leap.soledad.client.secrets import PassphraseTooShort  from leap.soledad.client.shared_db import SoledadSharedDatabase  from leap.soledad.client.target import SoledadSyncTarget @@ -39,7 +40,6 @@ class AuxMethodsTestCase(BaseSoledadTest):      def test__init_dirs(self):          sol = self._soledad_instance(prefix='_init_dirs') -        sol._init_dirs()          local_db_dir = os.path.dirname(sol.local_db_path)          secrets_path = os.path.dirname(sol.secrets_path)          self.assertTrue(os.path.isdir(local_db_dir)) @@ -47,16 +47,9 @@ class AuxMethodsTestCase(BaseSoledadTest):          sol.close()      def test__init_db(self): -        sol = self._soledad_instance() -        sol._init_dirs() -        sol._crypto = SoledadCrypto(sol) -        #self._soledad._gpg.import_keys(PUBLIC_KEY) -        if not sol._has_secret(): -            sol._gen_secret() -        sol._load_secrets() -        sol._init_db() -        from leap.soledad.client.sqlcipher import SQLCipherDatabase +        sol = self._soledad_instance(prefix='_init_db')          self.assertIsInstance(sol._db, SQLCipherDatabase) +        self.assertTrue(os.path.isfile(sol.local_db_path))          sol.close()      def test__init_config_defaults(self): @@ -71,16 +64,21 @@ class AuxMethodsTestCase(BaseSoledadTest):          # instantiate without initializing so we just test _init_config()          sol = SoledadMock() -        Soledad._init_config(sol, None, None, '') +        sol._passphrase = u'' +        sol._secrets_path = None +        sol._local_db_path = None +        sol._server_url = '' +        sol._init_config()          # assert value of secrets_path          self.assertEquals(              os.path.join(                  sol.DEFAULT_PREFIX, Soledad.STORAGE_SECRETS_FILE_NAME), -            sol.secrets_path) +            sol._secrets_path)          # assert value of local_db_path          self.assertEquals(              os.path.join(sol.DEFAULT_PREFIX, 'soledad.u1db'),              sol.local_db_path) +        sol.close()      def test__init_config_from_params(self):          """ @@ -174,8 +172,8 @@ class SoledadSharedDBTestCase(BaseSoledadTest):          """          Ensure the shared db is queried with the correct doc_id.          """ -        doc_id = self._soledad._shared_db_doc_id() -        self._soledad._get_secrets_from_shared_db() +        doc_id = self._soledad.secrets._shared_db_doc_id() +        self._soledad.secrets._get_secrets_from_shared_db()          self.assertTrue(              self._soledad._shared_db().get_doc.assert_called_with(                  doc_id) is None, @@ -185,8 +183,8 @@ class SoledadSharedDBTestCase(BaseSoledadTest):          """          Ensure recovery document is put into shared recover db.          """ -        doc_id = self._soledad._shared_db_doc_id() -        self._soledad._put_secrets_in_shared_db() +        doc_id = self._soledad.secrets._shared_db_doc_id() +        self._soledad.secrets._put_secrets_in_shared_db()          self.assertTrue(              self._soledad._shared_db().get_doc.assert_called_with(                  doc_id) is None, @@ -210,6 +208,7 @@ class SoledadSignalingTestCase(BaseSoledadTest):      def setUp(self):          # mock signaling          soledad.client.signal = Mock() +        soledad.client.secrets.signal = Mock()          # run parent's setUp          BaseSoledadTest.setUp(self) @@ -231,57 +230,57 @@ class SoledadSignalingTestCase(BaseSoledadTest):            - downloading keys / done downloading keys.            - uploading keys / done uploading keys.          """ -        soledad.client.signal.reset_mock() +        soledad.client.secrets.signal.reset_mock()          # get a fresh instance so it emits all bootstrap signals          sol = self._soledad_instance(              secrets_path='alternative_stage3.json',              local_db_path='alternative_stage3.u1db')          # reverse call order so we can verify in the order the signals were          # expected -        soledad.client.signal.mock_calls.reverse() -        soledad.client.signal.call_args = \ -            soledad.client.signal.call_args_list[0] -        soledad.client.signal.call_args_list.reverse() +        soledad.client.secrets.signal.mock_calls.reverse() +        soledad.client.secrets.signal.call_args = \ +            soledad.client.secrets.signal.call_args_list[0] +        soledad.client.secrets.signal.call_args_list.reverse()          # downloading keys signals -        soledad.client.signal.assert_called_with( +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DOWNLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DONE_DOWNLOADING_KEYS,              ADDRESS,          )          # creating keys signals -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_CREATING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DONE_CREATING_KEYS,              ADDRESS,          )          # downloading once more (inside _put_keys_in_shared_db) -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DOWNLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DONE_DOWNLOADING_KEYS,              ADDRESS,          )          # uploading keys signals -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_UPLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DONE_UPLOADING_KEYS,              ADDRESS,          ) @@ -298,8 +297,8 @@ class SoledadSignalingTestCase(BaseSoledadTest):          # get existing instance so we have access to keys          sol = self._soledad_instance()          # create a document with secrets -        doc = SoledadDocument(doc_id=sol._shared_db_doc_id()) -        doc.content = sol.export_recovery_document() +        doc = SoledadDocument(doc_id=sol.secrets._shared_db_doc_id()) +        doc.content = sol.secrets._export_recovery_document()          class Stage2MockSharedDB(object): @@ -313,7 +312,7 @@ class SoledadSignalingTestCase(BaseSoledadTest):          sol.close()          # reset mock -        soledad.client.signal.reset_mock() +        soledad.client.secrets.signal.reset_mock()          # get a fresh instance so it emits all bootstrap signals          sol = self._soledad_instance(              secrets_path='alternative_stage2.json', @@ -321,17 +320,17 @@ class SoledadSignalingTestCase(BaseSoledadTest):              shared_db_class=Stage2MockSharedDB)          # reverse call order so we can verify in the order the signals were          # expected -        soledad.client.signal.mock_calls.reverse() -        soledad.client.signal.call_args = \ -            soledad.client.signal.call_args_list[0] -        soledad.client.signal.call_args_list.reverse() +        soledad.client.secrets.signal.mock_calls.reverse() +        soledad.client.secrets.signal.call_args = \ +            soledad.client.secrets.signal.call_args_list[0] +        soledad.client.secrets.signal.call_args_list.reverse()          # assert download keys signals -        soledad.client.signal.assert_called_with( +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DOWNLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.signal) -        soledad.client.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.signal) +        soledad.client.secrets.signal.assert_called_with(              proto.SOLEDAD_DONE_DOWNLOADING_KEYS,              ADDRESS,          ) diff --git a/common/src/leap/soledad/common/tests/test_sqlcipher.py b/common/src/leap/soledad/common/tests/test_sqlcipher.py index 595966ec..273ac06e 100644 --- a/common/src/leap/soledad/common/tests/test_sqlcipher.py +++ b/common/src/leap/soledad/common/tests/test_sqlcipher.py @@ -24,8 +24,6 @@ import threading  from pysqlcipher import dbapi2 -from StringIO import StringIO -from urlparse import urljoin  # u1db stuff. @@ -79,6 +77,7 @@ class TestSQLCipherBackendImpl(tests.TestCase):          self.assertEqual(34, len(doc_id1))          int(doc_id1[len('D-'):], 16)          self.assertNotEqual(doc_id1, db._allocate_doc_id()) +        db.close()  #----------------------------------------------------------------------------- @@ -123,9 +122,6 @@ class SQLCipherIndexTests(test_backends.DatabaseIndexTests):      scenarios = SQLCIPHER_SCENARIOS -load_tests = tests.load_with_scenarios - -  #-----------------------------------------------------------------------------  # The following tests come from `u1db.tests.test_sqlite_backend`.  #----------------------------------------------------------------------------- @@ -174,6 +170,8 @@ class TestSQLCipherDatabase(test_sqlite_backend.TestSQLiteDatabase):          self.assertIsInstance(outcome2[0], SQLCipherDatabaseTesting)          db2 = outcome2[0]          self.assertTrue(db2._is_initialized(db1._get_sqlite_handle().cursor())) +        db1.close() +        db2.close()  class TestAlternativeDocument(SoledadDocument): @@ -190,22 +188,22 @@ class TestSQLCipherPartialExpandDatabase(      def setUp(self):          test_sqlite_backend.TestSQLitePartialExpandDatabase.setUp(self)          self.db = SQLCipherDatabase(':memory:', PASSWORD) -        self.db._set_replica_uid('test') + +    def tearDown(self): +        self.db.close() +        test_sqlite_backend.TestSQLitePartialExpandDatabase.tearDown(self)      def test_default_replica_uid(self): -        self.db = SQLCipherDatabase(':memory:', PASSWORD)          self.assertIsNot(None, self.db._replica_uid)          self.assertEqual(32, len(self.db._replica_uid))          int(self.db._replica_uid, 16)      def test__parse_index(self): -        self.db = SQLCipherDatabase(':memory:', PASSWORD)          g = self.db._parse_index_definition('fieldname')          self.assertIsInstance(g, query_parser.ExtractField)          self.assertEqual(['fieldname'], g.field)      def test__update_indexes(self): -        self.db = SQLCipherDatabase(':memory:', PASSWORD)          g = self.db._parse_index_definition('fieldname')          c = self.db._get_sqlite_handle().cursor()          self.db._update_indexes('doc-id', {'fieldname': 'val'}, @@ -216,7 +214,6 @@ class TestSQLCipherPartialExpandDatabase(      def test__set_replica_uid(self):          # Start from scratch, so that replica_uid isn't set. -        self.db = SQLCipherDatabase(':memory:', PASSWORD)          self.assertIsNot(None, self.db._real_replica_uid)          self.assertIsNot(None, self.db._replica_uid)          self.db._set_replica_uid('foo') @@ -231,19 +228,23 @@ class TestSQLCipherPartialExpandDatabase(      def test__open_database(self):          temp_dir = self.createTempDir(prefix='u1db-test-')          path = temp_dir + '/test.sqlite' -        SQLCipherDatabase(path, PASSWORD) +        db1 = SQLCipherDatabase(path, PASSWORD)          db2 = SQLCipherDatabase._open_database(path, PASSWORD)          self.assertIsInstance(db2, SQLCipherDatabase) +        db1.close() +        db2.close()      def test__open_database_with_factory(self):          temp_dir = self.createTempDir(prefix='u1db-test-')          path = temp_dir + '/test.sqlite' -        SQLCipherDatabase(path, PASSWORD) +        db1 = SQLCipherDatabase(path, PASSWORD)          db2 = SQLCipherDatabase._open_database(              path, PASSWORD,              document_factory=TestAlternativeDocument)          doc = db2.create_doc({})          self.assertTrue(isinstance(doc, SoledadDocument)) +        db1.close() +        db2.close()      def test__open_database_non_existent(self):          temp_dir = self.createTempDir(prefix='u1db-test-') @@ -258,7 +259,9 @@ class TestSQLCipherPartialExpandDatabase(          db = SQLCipherDatabase.__new__(              SQLCipherDatabase)          db._db_handle = dbapi2.connect(path)  # db is there but not yet init-ed +        db._sync_db = None          db._syncers = {} +        db.sync_queue = None          c = db._db_handle.cursor()          c.execute('PRAGMA key="%s"' % PASSWORD)          self.addCleanup(db.close) @@ -281,6 +284,8 @@ class TestSQLCipherPartialExpandDatabase(              [None,               SQLCipherDatabase._index_storage_value],              observed) +        db.close() +        db2.close()      def test__open_database_invalid(self):          class SQLiteDatabaseTesting(SQLCipherDatabase): @@ -301,26 +306,32 @@ class TestSQLCipherPartialExpandDatabase(      def test_open_database_existing(self):          temp_dir = self.createTempDir(prefix='u1db-test-')          path = temp_dir + '/existing.sqlite' -        SQLCipherDatabase(path, PASSWORD) +        db1 = SQLCipherDatabase(path, PASSWORD)          db2 = SQLCipherDatabase.open_database(path, PASSWORD, create=False)          self.assertIsInstance(db2, SQLCipherDatabase) +        db1.close() +        db2.close()      def test_open_database_with_factory(self):          temp_dir = self.createTempDir(prefix='u1db-test-')          path = temp_dir + '/existing.sqlite' -        SQLCipherDatabase(path, PASSWORD) +        db1 = SQLCipherDatabase(path, PASSWORD)          db2 = SQLCipherDatabase.open_database(              path, PASSWORD, create=False,              document_factory=TestAlternativeDocument)          doc = db2.create_doc({})          self.assertTrue(isinstance(doc, SoledadDocument)) +        db1.close() +        db2.close()      def test_open_database_create(self):          temp_dir = self.createTempDir(prefix='u1db-test-')          path = temp_dir + '/new.sqlite' -        SQLCipherDatabase.open_database(path, PASSWORD, create=True) +        db1 = SQLCipherDatabase.open_database(path, PASSWORD, create=True)          db2 = SQLCipherDatabase.open_database(path, PASSWORD, create=False)          self.assertIsInstance(db2, SQLCipherDatabase) +        db1.close() +        db2.close()      def test_create_database_initializes_schema(self):          # This test had to be cloned because our implementation of SQLCipher @@ -331,7 +342,8 @@ class TestSQLCipherPartialExpandDatabase(          c = raw_db.cursor()          c.execute("SELECT * FROM u1db_config")          config = dict([(r[0], r[1]) for r in c.fetchall()]) -        self.assertEqual({'sql_schema': '0', 'replica_uid': 'test', +        replica_uid = self.db._replica_uid +        self.assertEqual({'sql_schema': '0', 'replica_uid': replica_uid,                            'index_storage': 'expand referenced encrypted'},                           config) @@ -444,6 +456,22 @@ class SQLCipherDatabaseSyncTests(      def tearDown(self):          test_sync.DatabaseSyncTests.tearDown(self) +        if hasattr(self, 'db1') and isinstance(self.db1, SQLCipherDatabase): +            self.db1.close() +        if hasattr(self, 'db1_copy') \ +                and isinstance(self.db1_copy, SQLCipherDatabase): +            self.db1_copy.close() +        if hasattr(self, 'db2') \ +                and isinstance(self.db2, SQLCipherDatabase): +            self.db2.close() +        if hasattr(self, 'db2_copy') \ +                and isinstance(self.db2_copy, SQLCipherDatabase): +            self.db2_copy.close() +        if hasattr(self, 'db3') \ +                and isinstance(self.db3, SQLCipherDatabase): +            self.db3.close() + +      def test_sync_autoresolves(self):          """ @@ -612,6 +640,9 @@ class SQLCipherDatabaseSyncTests(                  doc3.doc_id, doc3.rev, key, secret))          self.assertEqual(doc4.get_json(), doc3.get_json())          self.assertFalse(doc3.has_conflicts) +        self.db1.close() +        self.db2.close() +        db3.close()      def test_sync_puts_changes(self):          """ @@ -778,6 +809,7 @@ class SQLCipherEncryptionTest(BaseLeapTest):              doc = db.get_doc(doc.doc_id)              self.assertEqual(tests.simple_doc, doc.get_json(),                               'decrypted content mismatch') +            db.close()      def test_try_to_open_raw_db_with_sqlcipher_backend(self):          """ @@ -790,7 +822,8 @@ class SQLCipherEncryptionTest(BaseLeapTest):          try:              # trying to open the a non-encrypted database with sqlcipher              # backend should raise a DatabaseIsNotEncrypted exception. -            SQLCipherDatabase(self.DB_FILE, PASSWORD) +            db = SQLCipherDatabase(self.DB_FILE, PASSWORD) +            db.close()              raise dbapi2.DatabaseError(                  "SQLCipher backend should not be able to open non-encrypted "                  "dbs.") diff --git a/common/src/leap/soledad/common/tests/test_sync_deferred.py b/common/src/leap/soledad/common/tests/test_sync_deferred.py index 48e3150f..7643b27c 100644 --- a/common/src/leap/soledad/common/tests/test_sync_deferred.py +++ b/common/src/leap/soledad/common/tests/test_sync_deferred.py @@ -37,9 +37,6 @@ DEFER_DECRYPTION = True  WAIT_STEP = 1  MAX_WAIT = 10 -from leap.soledad.common.tests import test_sqlcipher as ts -from leap.soledad.server import SoledadApp -  from leap.soledad.client.sqlcipher import open as open_sqlcipher  from leap.soledad.common.tests.util import SoledadWithCouchServerMixin @@ -89,11 +86,8 @@ class BaseSoledadDeferredEncTest(SoledadWithCouchServerMixin):          self._soledad.close()          # XXX should not access "private" attrs -        for f in [self._soledad._local_db_path, -                  self._soledad._secrets_path, -                  self.db1._sync_db_path]: -            if os.path.isfile(f): -                os.unlink(f) +        import shutil +        shutil.rmtree(os.path.dirname(self._soledad._local_db_path))  #SQLCIPHER_SCENARIOS = [ diff --git a/common/src/leap/soledad/common/tests/test_sync_target.py b/common/src/leap/soledad/common/tests/test_sync_target.py index edc4589b..45009f4e 100644 --- a/common/src/leap/soledad/common/tests/test_sync_target.py +++ b/common/src/leap/soledad/common/tests/test_sync_target.py @@ -23,29 +23,15 @@ import os  import simplejson as json  import u1db -from uuid import uuid4 -  from u1db.remote import http_database -from u1db import SyncTarget -from u1db.sync import Synchronizer -from u1db.remote import ( -    http_client, -    http_database, -    http_target, -) - -from leap.soledad import client  from leap.soledad.client import (      target,      auth,      crypto, -    VerifiedHTTPSConnection,      sync,  )  from leap.soledad.common.document import SoledadDocument -from leap.soledad.server.auth import SoledadTokenAuthMiddleware -  from leap.soledad.common.tests import u1db_tests as tests  from leap.soledad.common.tests import BaseSoledadTest @@ -58,13 +44,6 @@ from leap.soledad.common.tests.util import (  from leap.soledad.common.tests.u1db_tests import test_backends  from leap.soledad.common.tests.u1db_tests import test_remote_sync_target  from leap.soledad.common.tests.u1db_tests import test_sync -from leap.soledad.common.tests.test_couch import ( -    CouchDBTestCase, -    CouchDBWrapper, -) - -from leap.soledad.server import SoledadApp -from leap.soledad.server.auth import SoledadTokenAuthMiddleware  #----------------------------------------------------------------------------- @@ -279,8 +258,9 @@ class TestSoledadSyncTarget(      def tearDown(self):          SoledadWithCouchServerMixin.tearDown(self)          tests.TestCaseWithServer.tearDown(self) -        db, _ = self.request_state.ensure_database('test2') -        db.delete_database() +        db2, _ = self.request_state.ensure_database('test2') +        db2.delete_database() +        self.db1.close()      def test_sync_exchange_send(self):          """ @@ -540,6 +520,10 @@ class TestSoledadDbSync(          self.main_test_class = test_sync.TestDbSync          SoledadWithCouchServerMixin.setUp(self) +    def tearDown(self): +        SoledadWithCouchServerMixin.tearDown(self) +        self.db.close() +      def do_sync(self, target_name):          """          Perform sync using SoledadSynchronizer, SoledadSyncTarget diff --git a/common/src/leap/soledad/common/tests/test_target.py b/common/src/leap/soledad/common/tests/test_target.py index 6242099d..eb5e2874 100644 --- a/common/src/leap/soledad/common/tests/test_target.py +++ b/common/src/leap/soledad/common/tests/test_target.py @@ -22,17 +22,14 @@ Test Leap backend bits.  import u1db  import os -import ssl  import simplejson as json  import cStringIO -from u1db import SyncTarget  from u1db.sync import Synchronizer  from u1db.remote import (      http_client,      http_database, -    http_target,  )  from leap.soledad import client @@ -40,7 +37,6 @@ from leap.soledad.client import (      target,      auth,      VerifiedHTTPSConnection, -    sync,  )  from leap.soledad.common.document import SoledadDocument  from leap.soledad.server.auth import SoledadTokenAuthMiddleware @@ -61,10 +57,6 @@ from leap.soledad.common.tests.u1db_tests import test_document  from leap.soledad.common.tests.u1db_tests import test_remote_sync_target  from leap.soledad.common.tests.u1db_tests import test_https  from leap.soledad.common.tests.u1db_tests import test_sync -from leap.soledad.common.tests.test_couch import ( -    CouchDBTestCase, -    CouchDBWrapper, -)  #----------------------------------------------------------------------------- @@ -391,6 +383,10 @@ class TestSoledadSyncTarget(          tests.TestCaseWithServer.tearDown(self)          db, _ = self.request_state.ensure_database('test2')          db.delete_database() +        for i in ['db1', 'db2']: +            if hasattr(self, i): +                db = getattr(self, i) +                db.close()      def test_sync_exchange_send(self):          """ @@ -413,6 +409,7 @@ class TestSoledadSyncTarget(          self.assertEqual(1, new_gen)          self.assertGetEncryptedDoc(              db, 'doc-here', 'replica:1', '{"value": "here"}', False) +        db.close()      def test_sync_exchange_send_failure_and_retry_scenario(self):          """ @@ -486,6 +483,7 @@ class TestSoledadSyncTarget(          self.assertEqual(              ('doc-here', 'replica:1', '{"value": "here"}', 1),              other_changes[0][:-1]) +        db.close()      def test_sync_exchange_send_ensure_callback(self):          """ @@ -515,6 +513,7 @@ class TestSoledadSyncTarget(          self.assertEqual(db._replica_uid, replica_uid_box[0])          self.assertGetEncryptedDoc(              db, 'doc-here', 'replica:1', '{"value": "here"}', False) +        db.close()      def test_sync_exchange_in_stream_error(self):          # we bypass this test because our sync_exchange process does not @@ -747,6 +746,10 @@ class TestSoledadDbSync(          self.main_test_class = test_sync.TestDbSync          SoledadWithCouchServerMixin.setUp(self) +    def tearDown(self): +        SoledadWithCouchServerMixin.tearDown(self) +        self.db.close() +      def do_sync(self, target_name):          """          Perform sync using SoledadSyncTarget and Token auth. diff --git a/common/src/leap/soledad/common/tests/u1db_tests/test_backends.py b/common/src/leap/soledad/common/tests/u1db_tests/test_backends.py index 86e76fad..54adcde1 100644 --- a/common/src/leap/soledad/common/tests/u1db_tests/test_backends.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_backends.py @@ -363,6 +363,7 @@ class LocalDatabaseTests(tests.DatabaseBaseTests):          db2 = self.create_database('other-uid')          doc2 = db2.create_doc_from_json(simple_doc)          self.assertNotEqual(doc1.doc_id, doc2.doc_id) +        db2.close()      def test_put_doc_refuses_slashes_picky(self):          doc = self.make_document('/a', None, simple_doc) | 
