diff options
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | README.rst | 16 | ||||
-rw-r--r-- | client/changes/feature_3118-provide-a-way-to-access-the-saved-password (renamed from soledad/changes/feature_3118-provide-a-way-to-access-the-saved-password) | 0 | ||||
-rw-r--r-- | client/changes/feature_3487-split-soledad-into-common-client-and-server | 1 | ||||
-rw-r--r-- | client/setup.py (renamed from soledad/setup.py) | 21 | ||||
-rw-r--r-- | client/src/leap/__init__.py (renamed from soledad/src/leap/__init__.py) | 0 | ||||
-rw-r--r-- | client/src/leap/soledad/__init__.py (renamed from soledad_server/src/leap/__init__.py) | 0 | ||||
-rw-r--r-- | client/src/leap/soledad/client/__init__.py (renamed from soledad/src/leap/soledad/__init__.py) | 64 | ||||
-rw-r--r-- | client/src/leap/soledad/client/auth.py (renamed from soledad/src/leap/soledad/auth.py) | 3 | ||||
-rw-r--r-- | client/src/leap/soledad/client/crypto.py (renamed from soledad/src/leap/soledad/crypto.py) | 2 | ||||
-rw-r--r-- | client/src/leap/soledad/client/dbwrapper.py (renamed from soledad/src/leap/soledad/dbwrapper.py) | 2 | ||||
-rw-r--r-- | client/src/leap/soledad/client/shared_db.py (renamed from soledad/src/leap/soledad/shared_db.py) | 2 | ||||
-rw-r--r-- | client/src/leap/soledad/client/sqlcipher.py (renamed from soledad/src/leap/soledad/sqlcipher.py) | 117 | ||||
-rw-r--r-- | client/src/leap/soledad/client/target.py (renamed from soledad/src/leap/soledad/target.py) | 50 | ||||
-rw-r--r-- | common/changes/feature_3487-split-soledad-into-common-client-and-server | 1 | ||||
-rw-r--r-- | common/setup.py | 79 | ||||
-rw-r--r-- | common/src/leap/__init__.py | 6 | ||||
-rw-r--r-- | common/src/leap/soledad/__init__.py | 6 | ||||
-rw-r--r-- | common/src/leap/soledad/common/__init__.py | 64 | ||||
-rw-r--r-- | common/src/leap/soledad/common/couch.py (renamed from soledad_server/src/leap/soledad_server/couch.py) | 3 | ||||
-rw-r--r-- | common/src/leap/soledad/common/crypto.py | 55 | ||||
-rw-r--r-- | common/src/leap/soledad/common/document.py (renamed from soledad/src/leap/soledad/document.py) | 0 | ||||
-rw-r--r-- | common/src/leap/soledad/common/objectstore.py (renamed from soledad_server/src/leap/soledad_server/objectstore.py) | 3 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/__init__.py (renamed from soledad/src/leap/soledad/tests/__init__.py) | 24 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/couchdb.ini.template (renamed from soledad/src/leap/soledad/tests/couchdb.ini.template) | 0 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/test_couch.py (renamed from soledad/src/leap/soledad/tests/test_couch.py) | 10 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/test_crypto.py (renamed from soledad/src/leap/soledad/tests/test_crypto.py) | 12 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/test_server.py (renamed from soledad/src/leap/soledad/tests/test_server.py) | 22 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/test_soledad.py (renamed from soledad/src/leap/soledad/tests/test_soledad.py) | 111 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/test_sqlcipher.py (renamed from soledad/src/leap/soledad/tests/test_sqlcipher.py) | 25 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/test_target.py (renamed from soledad/src/leap/soledad/tests/test_target.py) | 44 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/README (renamed from soledad/src/leap/soledad/tests/u1db_tests/README) | 0 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/__init__.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/__init__.py) | 7 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_backends.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_backends.py) | 4 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_document.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_document.py) | 2 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_http_app.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_http_app.py) | 2 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_http_client.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_http_client.py) | 2 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_http_database.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_http_database.py) | 4 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_https.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_https.py) | 6 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_open.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_open.py) | 4 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_remote_sync_target.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py) | 2 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_sqlite_backend.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py) | 4 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/test_sync.py (renamed from soledad/src/leap/soledad/tests/u1db_tests/test_sync.py) | 10 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/testing-certs/Makefile (renamed from soledad/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile) | 0 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/testing-certs/cacert.pem (renamed from soledad/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem) | 0 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/testing-certs/testing.cert (renamed from soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert) | 0 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/u1db_tests/testing-certs/testing.key (renamed from soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key) | 0 | ||||
-rw-r--r-- | server/changes/feature_3487-split-soledad-into-common-client-and-server | 1 | ||||
-rw-r--r-- | server/pkg/soledad (renamed from soledad_server/pkg/soledad) | 0 | ||||
-rw-r--r-- | server/setup.py (renamed from soledad_server/setup.py) | 9 | ||||
-rw-r--r-- | server/src/leap/__init__.py | 6 | ||||
-rw-r--r-- | server/src/leap/soledad/__init__.py | 6 | ||||
-rw-r--r-- | server/src/leap/soledad/server/__init__.py (renamed from soledad_server/src/leap/soledad_server/__init__.py) | 6 | ||||
-rw-r--r-- | server/src/leap/soledad/server/auth.py (renamed from soledad_server/src/leap/soledad_server/auth.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/backends/__init__.py | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/backends/leap_backend.py | 73 |
56 files changed, 526 insertions, 371 deletions
@@ -5,4 +5,10 @@ build/ MANIFEST *.egg-info/ *.egg +*.swp +*.swo +*.pyc +*.log +*.*~ + @@ -14,18 +14,16 @@ Library dependencies Tests ----- -Client and server tests are both included in leap.soledad. Because -soledad_server depends on soledad and soledad tests depend on soledad_server, -if you want to run tests in development mode you must first install soledad, -then soledad_server, and then run the tests. +Client and server tests are both included in leap.soledad.common. If you want +to run tests in development mode you must do the following:: -Therefore, tests must be run with:: - - cd soledad + cd common + python setup.py develop + cd ../client python setup.py develop - cd ../soledad_server + cd ../server python setup.py develop - cd ../soledad + cd ../common python setup.py test Note that to run CouchDB tests, be sure you have ``CouchDB`` installed on your diff --git a/soledad/changes/feature_3118-provide-a-way-to-access-the-saved-password b/client/changes/feature_3118-provide-a-way-to-access-the-saved-password index 69cb0b1d..69cb0b1d 100644 --- a/soledad/changes/feature_3118-provide-a-way-to-access-the-saved-password +++ b/client/changes/feature_3118-provide-a-way-to-access-the-saved-password diff --git a/client/changes/feature_3487-split-soledad-into-common-client-and-server b/client/changes/feature_3487-split-soledad-into-common-client-and-server new file mode 100644 index 00000000..2eab6b56 --- /dev/null +++ b/client/changes/feature_3487-split-soledad-into-common-client-and-server @@ -0,0 +1 @@ + o Split soledad package into common, client and server. Closes #3487. diff --git a/soledad/setup.py b/client/setup.py index 6da976a9..291c95fe 100644 --- a/soledad/setup.py +++ b/client/setup.py @@ -29,20 +29,10 @@ install_requirements = [ 'oauth', # this is not strictly needed by us, but we need it # until u1db adds it to its release as a dep. 'u1db', - 'six==1.1.0', 'scrypt', 'pyxdg', 'pycryptopp', -] - - -tests_requirements = [ - 'mock', - 'nose2', - 'testscenarios', - 'leap.common', - 'leap.soledad_server', - 'pyOpenSSL', + 'leap.soledad.common>=0.3.0', ] @@ -60,8 +50,9 @@ trove_classifiers = ( "Topic :: Software Development :: Libraries :: Python Modules" ) + setup( - name='leap.soledad', + name='leap.soledad.client', version='0.3.0', url='https://leap.se/', license='GPLv3+', @@ -73,12 +64,10 @@ setup( "securely shared among devices. It provides, to other parts of the " "LEAP client, an API for data storage and sync." ), - namespace_packages=["leap"], - packages=find_packages('src', exclude=['leap.soledad.tests']), + namespace_packages=["leap", "leap.soledad"], + packages=find_packages('src'), package_dir={'': 'src'}, - test_suite='leap.soledad.tests', install_requires=install_requirements, - tests_require=tests_requirements, classifiers=trove_classifiers, extras_require={'signaling': ['leap.common']}, ) diff --git a/soledad/src/leap/__init__.py b/client/src/leap/__init__.py index f48ad105..f48ad105 100644 --- a/soledad/src/leap/__init__.py +++ b/client/src/leap/__init__.py diff --git a/soledad_server/src/leap/__init__.py b/client/src/leap/soledad/__init__.py index f48ad105..f48ad105 100644 --- a/soledad_server/src/leap/__init__.py +++ b/client/src/leap/soledad/__init__.py diff --git a/soledad/src/leap/soledad/__init__.py b/client/src/leap/soledad/client/__init__.py index 3fe9629f..fc8219fa 100644 --- a/soledad/src/leap/soledad/__init__.py +++ b/client/src/leap/soledad/client/__init__.py @@ -45,49 +45,6 @@ from u1db.remote.ssl_match_hostname import match_hostname # -# Assert functions -# - -# we want to use leap.common.check.leap_assert in case it is available, -# because it also logs in a way other parts of leap can access log messages. - -try: - from leap.common.check import leap_assert as soledad_assert - -except ImportError: - - def soledad_assert(condition, message): - """ - Asserts the condition and displays the message if that's not - met. - - @param condition: condition to check - @type condition: bool - @param message: message to display if the condition isn't met - @type message: str - """ - assert condition, message - -try: - from leap.common.check import leap_assert_type as soledad_assert_type - -except ImportError: - - def soledad_assert_type(var, expectedType): - """ - Helper assert check for a variable's expected type - - @param var: variable to check - @type var: any - @param expectedType: type to check agains - @type expectedType: type - """ - soledad_assert(isinstance(var, expectedType), - "Expected type %r instead of %r" % - (expectedType, type(var))) - - -# # Signaling function # @@ -124,13 +81,14 @@ except ImportError: logger.info("Would signal: %s - %s." % (str(signal), content)) -from leap.soledad.crypto import SoledadCrypto -from leap.soledad.dbwrapper import SQLCipherWrapper -from leap.soledad.document import SoledadDocument -from leap.soledad.shared_db import SoledadSharedDatabase -from leap.soledad.sqlcipher import open as sqlcipher_open -from leap.soledad.sqlcipher import SQLCipherDatabase -from leap.soledad.target import SoledadSyncTarget +from leap.soledad.common import soledad_assert, soledad_assert_type +from leap.soledad.common.document import SoledadDocument +from leap.soledad.client.crypto import SoledadCrypto +from leap.soledad.client.dbwrapper import SQLCipherWrapper +from leap.soledad.client.shared_db import SoledadSharedDatabase +from leap.soledad.client.sqlcipher import open as sqlcipher_open +from leap.soledad.client.sqlcipher import SQLCipherDatabase +from leap.soledad.client.target import SoledadSyncTarget logger = logging.getLogger(name=__name__) @@ -429,8 +387,7 @@ class Soledad(object): buflen=32, # we need a key with 256 bits (32 bytes) ) - # Instantiate a thread-safe wrapper - self._db = SQLCipherWrapper( + self._db = sqlcipher_open( self._local_db_path, binascii.b2a_hex(key), # sqlcipher only accepts the hex version create=True, @@ -444,7 +401,7 @@ class Soledad(object): """ if hasattr(self, '_db') and isinstance( self._db, - SQLCipherWrapper): + SQLCipherDatabase): self._db.close() def __del__(self): @@ -650,6 +607,7 @@ class Soledad(object): self.SECRET_KEY: '%s%s%s' % ( str(iv), self.IV_SEPARATOR, binascii.b2a_base64(ciphertext)), } + self._store_secrets() self._passphrase = new_passphrase diff --git a/soledad/src/leap/soledad/auth.py b/client/src/leap/soledad/client/auth.py index 81e838d2..3cd6dabe 100644 --- a/soledad/src/leap/soledad/auth.py +++ b/client/src/leap/soledad/client/auth.py @@ -67,4 +67,5 @@ class TokenBasedAuth(object): return [('Authorization', 'Token %s' % auth.encode('base64')[:-1])] else: raise errors.UnknownAuthMethod( - 'Wrong credentials: %s' % self._creds)
\ No newline at end of file + 'Wrong credentials: %s' % self._creds) + diff --git a/soledad/src/leap/soledad/crypto.py b/client/src/leap/soledad/client/crypto.py index 3c1061d5..9fcff8e9 100644 --- a/soledad/src/leap/soledad/crypto.py +++ b/client/src/leap/soledad/client/crypto.py @@ -31,7 +31,7 @@ from pycryptopp.cipher.aes import AES from pycryptopp.cipher.xsalsa20 import XSalsa20 -from leap.soledad import ( +from leap.soledad.common import ( soledad_assert, soledad_assert_type, ) diff --git a/soledad/src/leap/soledad/dbwrapper.py b/client/src/leap/soledad/client/dbwrapper.py index c16c4925..a27a933e 100644 --- a/soledad/src/leap/soledad/dbwrapper.py +++ b/client/src/leap/soledad/client/dbwrapper.py @@ -31,7 +31,7 @@ import exceptions from functools import partial -from leap.soledad import sqlcipher +from leap.soledad.client import sqlcipher logger = logging.getLogger(__name__) diff --git a/soledad/src/leap/soledad/shared_db.py b/client/src/leap/soledad/client/shared_db.py index 33c5c484..a6ca504d 100644 --- a/soledad/src/leap/soledad/shared_db.py +++ b/client/src/leap/soledad/client/shared_db.py @@ -26,7 +26,7 @@ import simplejson as json from u1db.remote import http_database -from leap.soledad.auth import TokenBasedAuth +from leap.soledad.client.auth import TokenBasedAuth #----------------------------------------------------------------------------- diff --git a/soledad/src/leap/soledad/sqlcipher.py b/client/src/leap/soledad/client/sqlcipher.py index acbeabe6..c605c28c 100644 --- a/soledad/src/leap/soledad/sqlcipher.py +++ b/client/src/leap/soledad/client/sqlcipher.py @@ -43,23 +43,33 @@ So, as the statements above were introduced for backwards compatibility with SLCipher 1.1 databases, we do not implement them as all SQLCipher databases handled by Soledad should be created by SQLCipher >= 2.0. """ - +import logging import os import time import string +import threading from u1db.backends import sqlite_backend from pysqlcipher import dbapi2 -from u1db import ( - errors, -) -from leap.soledad.document import SoledadDocument +from u1db import errors as u1db_errors +from leap.soledad.common.document import SoledadDocument + +logger = logging.getLogger(__name__) # Monkey-patch u1db.backends.sqlite_backend with pysqlcipher.dbapi2 sqlite_backend.dbapi2 = dbapi2 +# It seems that, as long as we are not using old sqlite versions, serialized +# mode is enabled by default at compile time. So accessing db connections from +# different threads should be safe, as long as no attempt is made to use them +# from multiple threads with no locking. +# See https://sqlite.org/threadsafe.html +# and http://bugs.python.org/issue16509 + +SQLITE_CHECK_SAME_THREAD = False + def open(path, password, create=True, document_factory=None, crypto=None, raw_key=False, cipher='aes-256-cbc', kdf_iter=4000, @@ -125,6 +135,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): """A U1DB implementation that uses SQLCipher as its persistence layer.""" _index_storage_value = 'expand referenced encrypted' + k_lock = threading.Lock() def __init__(self, sqlcipher_file, password, document_factory=None, crypto=None, raw_key=False, cipher='aes-256-cbc', @@ -158,20 +169,23 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): sqlcipher_file, password, raw_key, cipher, kdf_iter, cipher_page_size) # connect to the database - self._db_handle = dbapi2.connect(sqlcipher_file) - # set SQLCipher cryptographic parameters - self._set_crypto_pragmas( - self._db_handle, password, raw_key, cipher, kdf_iter, - cipher_page_size) - self._real_replica_uid = None - self._ensure_schema() - self._crypto = crypto + with self.k_lock: + self._db_handle = dbapi2.connect( + sqlcipher_file, + check_same_thread=SQLITE_CHECK_SAME_THREAD) + # set SQLCipher cryptographic parameters + self._set_crypto_pragmas( + self._db_handle, password, raw_key, cipher, kdf_iter, + cipher_page_size) + self._real_replica_uid = None + self._ensure_schema() + self._crypto = crypto def factory(doc_id=None, rev=None, json='{}', has_conflicts=False, syncable=True): return SoledadDocument(doc_id=doc_id, rev=rev, json=json, - has_conflicts=has_conflicts, - syncable=syncable) + has_conflicts=has_conflicts, + syncable=syncable) self.set_document_factory(factory) @classmethod @@ -205,22 +219,35 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): @rtype: SQLCipherDatabase """ if not os.path.isfile(sqlcipher_file): - raise errors.DatabaseDoesNotExist() + raise u1db_errors.DatabaseDoesNotExist() + tries = 2 + # Note: There seems to be a bug in sqlite 3.5.9 (with python2.6) + # where without re-opening the database on Windows, it + # doesn't see the transaction that was just committed while True: - # Note: There seems to be a bug in sqlite 3.5.9 (with python2.6) - # where without re-opening the database on Windows, it - # doesn't see the transaction that was just committed - db_handle = dbapi2.connect(sqlcipher_file) - # set cryptographic params - cls._set_crypto_pragmas( - db_handle, password, raw_key, cipher, kdf_iter, - cipher_page_size) - c = db_handle.cursor() - v, err = cls._which_index_storage(c) - db_handle.close() - if v is not None: - break + + with cls.k_lock: + db_handle = dbapi2.connect( + sqlcipher_file, + check_same_thread=SQLITE_CHECK_SAME_THREAD) + + try: + # set cryptographic params + cls._set_crypto_pragmas( + db_handle, password, raw_key, cipher, kdf_iter, + cipher_page_size) + c = db_handle.cursor() + # XXX if we use it here, it should be public + v, err = cls._which_index_storage(c) + except Exception as exc: + logger.warning("ERROR OPENING DATABASE!") + logger.debug("error was: %r" % exc) + v, err = None, exc + finally: + db_handle.close() + if v is not None: + break # possibly another process is initializing it, wait for it to be # done if tries == 0: @@ -273,7 +300,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): sqlcipher_file, password, document_factory=document_factory, crypto=crypto, raw_key=raw_key, cipher=cipher, kdf_iter=kdf_iter, cipher_page_size=cipher_page_size) - except errors.DatabaseDoesNotExist: + except u1db_errors.DatabaseDoesNotExist: if not create: raise # TODO: remove backend class from here. @@ -301,17 +328,21 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): @rtype: int """ from u1db.sync import Synchronizer - from leap.soledad.target import SoledadSyncTarget + from leap.soledad.client.target import SoledadSyncTarget return Synchronizer( self, SoledadSyncTarget(url, - creds=creds, - crypto=self._crypto)).sync(autocreate=autocreate) + creds=creds, + crypto=self._crypto)).sync(autocreate=autocreate) def _extra_schema_init(self, c): """ Add any extra fields, etc to the basic table definitions. + This method is called by u1db.backends.sqlite_backend._initialize() + method, which is executed when the database schema is created. Here, + we use it to include the "syncable" property for LeapDocuments. + @param c: The cursor for querying the database. @type c: dbapi2.cursor """ @@ -402,10 +433,15 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): except dbapi2.DatabaseError: # assert that we can access it using SQLCipher with the given # key - db_handle = dbapi2.connect(sqlcipher_file) - cls._set_crypto_pragmas( - db_handle, key, raw_key, cipher, kdf_iter, cipher_page_size) - db_handle.cursor().execute('SELECT count(*) FROM sqlite_master') + with cls.k_lock: + db_handle = dbapi2.connect( + sqlcipher_file, + check_same_thread=SQLITE_CHECK_SAME_THREAD) + cls._set_crypto_pragmas( + db_handle, key, raw_key, cipher, + kdf_iter, cipher_page_size) + db_handle.cursor().execute( + 'SELECT count(*) FROM sqlite_master') @classmethod def _set_crypto_pragmas(cls, db_handle, key, raw_key, cipher, kdf_iter, @@ -649,5 +685,12 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): raise NotAnHexString(key) db_handle.cursor().execute('PRAGMA rekey = "x\'%s"' % passphrase) + def __del__(self): + """ + Closes db_handle upon object destruction. + """ + if self._db_handle is not None: + self._db_handle.close() + sqlite_backend.SQLiteDatabase.register_implementation(SQLCipherDatabase) diff --git a/soledad/src/leap/soledad/target.py b/client/src/leap/soledad/client/target.py index cad51b74..d0bc3706 100644 --- a/soledad/src/leap/soledad/target.py +++ b/client/src/leap/soledad/client/target.py @@ -32,13 +32,23 @@ from u1db.errors import BrokenSyncStream from u1db.remote.http_target import HTTPSyncTarget -from leap.soledad import soledad_assert -from leap.soledad.document import SoledadDocument -from leap.soledad.crypto import ( +from leap.soledad.common import soledad_assert +from leap.soledad.common.crypto import ( + EncryptionSchemes, + MacMethods, + ENC_JSON_KEY, + ENC_SCHEME_KEY, + ENC_METHOD_KEY, + ENC_IV_KEY, + MAC_KEY, + MAC_METHOD_KEY, +) +from leap.soledad.common.document import SoledadDocument +from leap.soledad.client.auth import TokenBasedAuth +from leap.soledad.client.crypto import ( EncryptionMethods, UnknownEncryptionMethod, ) -from leap.soledad.auth import TokenBasedAuth # @@ -74,38 +84,9 @@ class WrongMac(Exception): # -# Encryption schemes used for encryption. -# - -class EncryptionSchemes(object): - """ - Representation of encryption schemes used to encrypt documents. - """ - - NONE = 'none' - SYMKEY = 'symkey' - PUBKEY = 'pubkey' - - -class MacMethods(object): - """ - Representation of MAC methods used to authenticate document's contents. - """ - - HMAC = 'hmac' - - -# # Crypto utilities for a SoledadDocument. # -ENC_JSON_KEY = '_enc_json' -ENC_SCHEME_KEY = '_enc_scheme' -ENC_METHOD_KEY = '_enc_method' -ENC_IV_KEY = '_enc_iv' -MAC_KEY = '_mac' -MAC_METHOD_KEY = '_mac_method' - def mac_doc(crypto, doc_id, doc_rev, ciphertext, mac_method): """ @@ -367,7 +348,8 @@ class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth): #------------------------------------------------------------- # if arriving content was symmetrically encrypted, we decrypt # it. - doc = SoledadDocument(entry['id'], entry['rev'], entry['content']) + doc = SoledadDocument( + entry['id'], entry['rev'], entry['content']) if doc.content and ENC_SCHEME_KEY in doc.content: if doc.content[ENC_SCHEME_KEY] == \ EncryptionSchemes.SYMKEY: diff --git a/common/changes/feature_3487-split-soledad-into-common-client-and-server b/common/changes/feature_3487-split-soledad-into-common-client-and-server new file mode 100644 index 00000000..2eab6b56 --- /dev/null +++ b/common/changes/feature_3487-split-soledad-into-common-client-and-server @@ -0,0 +1 @@ + o Split soledad package into common, client and server. Closes #3487. diff --git a/common/setup.py b/common/setup.py new file mode 100644 index 00000000..6a432608 --- /dev/null +++ b/common/setup.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# setup.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from setuptools import ( + setup, + find_packages +) + + +install_requirements = [ + 'simplejson', + 'oauth', # this is not strictly needed by us, but we need it + # until u1db adds it to its release as a dep. + 'u1db', + 'six==1.1.0', # some tests are incompatible with newer versions of six. +] + + +tests_requirements = [ + 'mock', + 'nose2', + 'testscenarios', + 'leap.common', + 'leap.soledad.server', + 'leap.soledad.client', +] + + +trove_classifiers = ( + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: " + "GNU General Public License v3 or later (GPLv3+)", + "Environment :: Console", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Topic :: Database :: Front-Ends", + "Topic :: Software Development :: Libraries :: Python Modules" +) + + +setup( + name='leap.soledad.common', + version='0.3.0', + url='https://leap.se/', + license='GPLv3+', + description='Synchronization of locally encrypted data among devices.', + author='The LEAP Encryption Access Project', + author_email='info@leap.se', + long_description=( + "Soledad is the part of LEAP that allows application data to be " + "securely shared among devices. It provides, to other parts of the " + "LEAP client, an API for data storage and sync." + ), + namespace_packages=["leap", "leap.soledad"], + packages=find_packages('src', exclude=['leap.soledad.common.tests']), + package_dir={'': 'src'}, + test_suite='leap.soledad.common.tests', + install_requires=install_requirements, + tests_require=tests_requirements, + classifiers=trove_classifiers, +) diff --git a/common/src/leap/__init__.py b/common/src/leap/__init__.py new file mode 100644 index 00000000..f48ad105 --- /dev/null +++ b/common/src/leap/__init__.py @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/common/src/leap/soledad/__init__.py b/common/src/leap/soledad/__init__.py new file mode 100644 index 00000000..f48ad105 --- /dev/null +++ b/common/src/leap/soledad/__init__.py @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/common/src/leap/soledad/common/__init__.py b/common/src/leap/soledad/common/__init__.py new file mode 100644 index 00000000..4e45b828 --- /dev/null +++ b/common/src/leap/soledad/common/__init__.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# __init__.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +""" +Soledad routines common to client and server. +""" + + +# +# Assert functions +# + +# we want to use leap.common.check.leap_assert in case it is available, +# because it also logs in a way other parts of leap can access log messages. + +try: + from leap.common.check import leap_assert as soledad_assert + +except ImportError: + + def soledad_assert(condition, message): + """ + Asserts the condition and displays the message if that's not + met. + + @param condition: condition to check + @type condition: bool + @param message: message to display if the condition isn't met + @type message: str + """ + assert condition, message + +try: + from leap.common.check import leap_assert_type as soledad_assert_type + +except ImportError: + + def soledad_assert_type(var, expectedType): + """ + Helper assert check for a variable's expected type + + @param var: variable to check + @type var: any + @param expectedType: type to check agains + @type expectedType: type + """ + soledad_assert(isinstance(var, expectedType), + "Expected type %r instead of %r" % + (expectedType, type(var))) diff --git a/soledad_server/src/leap/soledad_server/couch.py b/common/src/leap/soledad/common/couch.py index ed5ad6b3..9642e8f3 100644 --- a/soledad_server/src/leap/soledad_server/couch.py +++ b/common/src/leap/soledad/common/couch.py @@ -18,7 +18,6 @@ """A U1DB backend that uses CouchDB as its persistence layer.""" -# general imports import uuid import re import simplejson as json @@ -34,7 +33,7 @@ from couchdb.client import Server, Document as CouchDocument from couchdb.http import ResourceNotFound -from leap.soledad_server.objectstore import ( +from leap.soledad.common.objectstore import ( ObjectStoreDatabase, ObjectStoreSyncTarget, ) diff --git a/common/src/leap/soledad/common/crypto.py b/common/src/leap/soledad/common/crypto.py new file mode 100644 index 00000000..2c6bd7a3 --- /dev/null +++ b/common/src/leap/soledad/common/crypto.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# crypto.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +""" +Soledad common crypto bits. +""" + + +# +# Encryption schemes used for encryption. +# + +class EncryptionSchemes(object): + """ + Representation of encryption schemes used to encrypt documents. + """ + + NONE = 'none' + SYMKEY = 'symkey' + PUBKEY = 'pubkey' + + +class MacMethods(object): + """ + Representation of MAC methods used to authenticate document's contents. + """ + + HMAC = 'hmac' + + +# +# Crypto utilities for a SoledadDocument. +# + +ENC_JSON_KEY = '_enc_json' +ENC_SCHEME_KEY = '_enc_scheme' +ENC_METHOD_KEY = '_enc_method' +ENC_IV_KEY = '_enc_iv' +MAC_KEY = '_mac' +MAC_METHOD_KEY = '_mac_method' diff --git a/soledad/src/leap/soledad/document.py b/common/src/leap/soledad/common/document.py index cc24b53a..cc24b53a 100644 --- a/soledad/src/leap/soledad/document.py +++ b/common/src/leap/soledad/common/document.py diff --git a/soledad_server/src/leap/soledad_server/objectstore.py b/common/src/leap/soledad/common/objectstore.py index 8afac3ec..921cf075 100644 --- a/soledad_server/src/leap/soledad_server/objectstore.py +++ b/common/src/leap/soledad/common/objectstore.py @@ -26,11 +26,12 @@ extended to implement OpenStack or Amazon S3 storage, for example. See U1DB documentation for more information on how to use databases. """ + +from u1db import errors from u1db.backends.inmemory import ( InMemoryDatabase, InMemorySyncTarget, ) -from u1db import errors class ObjectStoreDatabase(InMemoryDatabase): diff --git a/soledad/src/leap/soledad/tests/__init__.py b/common/src/leap/soledad/common/tests/__init__.py index b33f866c..9f47d74a 100644 --- a/soledad/src/leap/soledad/tests/__init__.py +++ b/common/src/leap/soledad/common/tests/__init__.py @@ -3,14 +3,16 @@ Tests to make sure Soledad provides U1DB functionality and more. """ import os +import random +import string import u1db from mock import Mock -from leap.soledad import Soledad -from leap.soledad.document import SoledadDocument -from leap.soledad.crypto import SoledadCrypto -from leap.soledad.target import ( +from leap.soledad.common.document import SoledadDocument +from leap.soledad.client import Soledad +from leap.soledad.client.crypto import SoledadCrypto +from leap.soledad.client.target import ( decrypt_doc, ENC_SCHEME_KEY, ) @@ -40,16 +42,23 @@ class BaseSoledadTest(BaseLeapTest): document_factory=SoledadDocument) self._db2 = u1db.open(self.db2_file, create=True, document_factory=SoledadDocument) + # get a random prefix for each test, so we do not mess with + # concurrency during initialization and shutting down of + # each local db. + self.rand_prefix = ''.join( + map(lambda x: random.choice(string.ascii_letters), range(6))) # initialize soledad by hand so we can control keys - self._soledad = self._soledad_instance(user=self.email) + self._soledad = self._soledad_instance( + prefix=self.rand_prefix, user=self.email) def tearDown(self): self._db1.close() self._db2.close() + self._soledad.close() + # XXX should not access "private" attrs for f in [self._soledad._local_db_path, self._soledad._secrets_path]: if os.path.isfile(f): os.unlink(f) - self._soledad.close() def _soledad_instance(self, user=ADDRESS, passphrase='123', prefix='', @@ -72,7 +81,8 @@ class BaseSoledadTest(BaseLeapTest): return Soledad( user, passphrase, - secrets_path=os.path.join(self.tempdir, prefix, secrets_path), + secrets_path=os.path.join( + self.tempdir, prefix, secrets_path), local_db_path=os.path.join( self.tempdir, prefix, local_db_path), server_url=server_url, # Soledad will fail if not given an url. diff --git a/soledad/src/leap/soledad/tests/couchdb.ini.template b/common/src/leap/soledad/common/tests/couchdb.ini.template index 7d0316f0..7d0316f0 100644 --- a/soledad/src/leap/soledad/tests/couchdb.ini.template +++ b/common/src/leap/soledad/common/tests/couchdb.ini.template diff --git a/soledad/src/leap/soledad/tests/test_couch.py b/common/src/leap/soledad/common/tests/test_couch.py index a84bb46c..2fb799b7 100644 --- a/soledad/src/leap/soledad/tests/test_couch.py +++ b/common/src/leap/soledad/common/tests/test_couch.py @@ -27,12 +27,12 @@ from base64 import b64decode from leap.common.files import mkdir_p -from leap.soledad_server import couch -from leap.soledad.tests import u1db_tests as tests -from leap.soledad.tests.u1db_tests import test_backends -from leap.soledad.tests.u1db_tests import test_sync +from leap.soledad.common.document import SoledadDocument +from leap.soledad.common.tests import u1db_tests as tests +from leap.soledad.common.tests.u1db_tests import test_backends +from leap.soledad.common.tests.u1db_tests import test_sync +from leap.soledad.common import couch import simplejson as json -from leap.soledad.document import SoledadDocument #----------------------------------------------------------------------------- diff --git a/soledad/src/leap/soledad/tests/test_crypto.py b/common/src/leap/soledad/common/tests/test_crypto.py index eea67b45..01b43299 100644 --- a/soledad/src/leap/soledad/tests/test_crypto.py +++ b/common/src/leap/soledad/common/tests/test_crypto.py @@ -32,19 +32,18 @@ from leap.common.testing.basetest import BaseLeapTest from Crypto import Random -from leap.common.testing.basetest import BaseLeapTest -from leap.soledad import ( +from leap.soledad.client import ( Soledad, crypto, target, ) -from leap.soledad.document import SoledadDocument -from leap.soledad.tests import ( +from leap.soledad.common.document import SoledadDocument +from leap.soledad.common.tests import ( BaseSoledadTest, KEY_FINGERPRINT, PRIVATE_KEY, ) -from leap.soledad.tests.u1db_tests import ( +from leap.soledad.common.tests.u1db_tests import ( simple_doc, nested_doc, TestCaseWithServer, @@ -137,7 +136,8 @@ class SoledadSecretsTestCase(BaseSoledadTest): secret_id_2 == hashlib.sha256(sol.storage_secret).hexdigest()) def test__has_secret(self): - sol = self._soledad_instance(user='user@leap.se') + sol = self._soledad_instance( + user='user@leap.se', prefix=self.rand_prefix) self.assertTrue(sol._has_secret(), "Should have a secret at " "this point") # setting secret id to None should not interfere in the fact we have a diff --git a/soledad/src/leap/soledad/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py index 24cd68dc..beb7e04d 100644 --- a/soledad/src/leap/soledad/tests/test_server.py +++ b/common/src/leap/soledad/common/tests/test_server.py @@ -26,27 +26,27 @@ import simplejson as json import mock -from leap.soledad import Soledad -from leap.soledad_server import SoledadApp -from leap.soledad_server.auth import URLToAuthorization -from leap.soledad_server.couch import ( +from leap.common.testing.basetest import BaseLeapTest +from leap.soledad.common.couch import ( CouchServerState, CouchDatabase, ) -from leap.soledad import target - - -from leap.common.testing.basetest import BaseLeapTest -from leap.soledad.tests.u1db_tests import ( +from leap.soledad.common.tests.u1db_tests import ( TestCaseWithServer, simple_doc, ) -from leap.soledad.tests.test_couch import CouchDBTestCase -from leap.soledad.tests.test_target import ( +from leap.soledad.common.tests.test_couch import CouchDBTestCase +from leap.soledad.common.tests.test_target import ( make_token_soledad_app, make_leap_document_for_test, token_leap_sync_target, ) +from leap.soledad.client import ( + Soledad, + target, +) +from leap.soledad.server import SoledadApp +from leap.soledad.server.auth import URLToAuthorization class ServerAuthorizationTestCase(BaseLeapTest): diff --git a/soledad/src/leap/soledad/tests/test_soledad.py b/common/src/leap/soledad/common/tests/test_soledad.py index 63ab5551..0b753647 100644 --- a/soledad/src/leap/soledad/tests/test_soledad.py +++ b/common/src/leap/soledad/common/tests/test_soledad.py @@ -22,25 +22,21 @@ Tests for general Soledad functionality. import os -import re -import tempfile -import simplejson as json from mock import Mock from pysqlcipher.dbapi2 import DatabaseError -from leap.common.testing.basetest import BaseLeapTest from leap.common.events import events_pb2 as proto -from leap.soledad.tests import ( +from leap.soledad.common.tests import ( BaseSoledadTest, ADDRESS, ) from leap import soledad -from leap.soledad import Soledad -from leap.soledad.document import SoledadDocument -from leap.soledad.crypto import SoledadCrypto -from leap.soledad.shared_db import SoledadSharedDatabase -from leap.soledad.target import SoledadSyncTarget +from leap.soledad.common.document import SoledadDocument +from leap.soledad.client import Soledad, PassphraseTooShort +from leap.soledad.client.crypto import SoledadCrypto +from leap.soledad.client.shared_db import SoledadSharedDatabase +from leap.soledad.client.target import SoledadSyncTarget class AuxMethodsTestCase(BaseSoledadTest): @@ -62,7 +58,7 @@ class AuxMethodsTestCase(BaseSoledadTest): sol._gen_secret() sol._load_secrets() sol._init_db() - from leap.soledad.sqlcipher import SQLCipherDatabase + from leap.soledad.client.sqlcipher import SQLCipherDatabase self.assertIsInstance(sol._db, SQLCipherDatabase) def test__init_config_defaults(self): @@ -113,17 +109,26 @@ class AuxMethodsTestCase(BaseSoledadTest): """ sol = self._soledad_instance( 'leap@leap.se', - passphrase='123') + passphrase='123', + prefix=self.rand_prefix, + ) doc = sol.create_doc({'simple': 'doc'}) doc_id = doc.doc_id + # change the passphrase sol.change_passphrase('654321') - # assert we can not use the old passphrase anymore + self.assertRaises( DatabaseError, - self._soledad_instance, 'leap@leap.se', '123') + self._soledad_instance, 'leap@leap.se', + passphrase='123', + prefix=self.rand_prefix) + # use new passphrase and retrieve doc - sol2 = self._soledad_instance('leap@leap.se', '654321') + sol2 = self._soledad_instance( + 'leap@leap.se', + passphrase='654321', + prefix=self.rand_prefix) doc2 = sol2.get_doc(doc_id) self.assertEqual(doc, doc2) @@ -137,7 +142,7 @@ class AuxMethodsTestCase(BaseSoledadTest): passphrase='123') # check that soledad complains about new passphrase length self.assertRaises( - soledad.PassphraseTooShort, + PassphraseTooShort, sol.change_passphrase, '54321') def test_get_passphrase(self): @@ -198,7 +203,7 @@ class SoledadSignalingTestCase(BaseSoledadTest): def setUp(self): BaseSoledadTest.setUp(self) # mock signaling - soledad.signal = Mock() + soledad.client.signal = Mock() def tearDown(self): pass @@ -212,54 +217,54 @@ class SoledadSignalingTestCase(BaseSoledadTest): """ Test that a fresh soledad emits all bootstrap signals. """ - soledad.signal.reset_mock() + soledad.client.signal.reset_mock() # get a fresh instance so it emits all bootstrap signals sol = self._soledad_instance( secrets_path='alternative.json', local_db_path='alternative.u1db') # reverse call order so we can verify in the order the signals were # expected - soledad.signal.mock_calls.reverse() - soledad.signal.call_args = \ - soledad.signal.call_args_list[0] - soledad.signal.call_args_list.reverse() + 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() # assert signals - soledad.signal.assert_called_with( + soledad.client.signal.assert_called_with( proto.SOLEDAD_DOWNLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_DONE_DOWNLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_CREATING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_DONE_CREATING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_DOWNLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_DONE_DOWNLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_UPLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_DONE_UPLOADING_KEYS, ADDRESS, ) @@ -268,32 +273,32 @@ class SoledadSignalingTestCase(BaseSoledadTest): """ Test that an existent soledad emits some of the bootstrap signals. """ - soledad.signal.reset_mock() + soledad.client.signal.reset_mock() # get an existent instance so it emits only some of bootstrap signals sol = self._soledad_instance() # reverse call order so we can verify in the order the signals were # expected - soledad.signal.mock_calls.reverse() - soledad.signal.call_args = \ - soledad.signal.call_args_list[0] - soledad.signal.call_args_list.reverse() + 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() # assert signals - soledad.signal.assert_called_with( + soledad.client.signal.assert_called_with( proto.SOLEDAD_DOWNLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_DONE_DOWNLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_UPLOADING_KEYS, ADDRESS, ) - self._pop_mock_call(soledad.signal) - soledad.signal.assert_called_with( + self._pop_mock_call(soledad.client.signal) + soledad.client.signal.assert_called_with( proto.SOLEDAD_DONE_UPLOADING_KEYS, ADDRESS, ) @@ -302,7 +307,7 @@ class SoledadSignalingTestCase(BaseSoledadTest): """ Test Soledad emits SOLEDAD_CREATING_KEYS signal. """ - soledad.signal.reset_mock() + soledad.client.signal.reset_mock() # get a fresh instance so it emits all bootstrap signals sol = self._soledad_instance() # mock the actual db sync so soledad does not try to connect to the @@ -311,7 +316,7 @@ class SoledadSignalingTestCase(BaseSoledadTest): # do the sync sol.sync() # assert the signal has been emitted - soledad.signal.assert_called_with( + soledad.client.signal.assert_called_with( proto.SOLEDAD_DONE_DATA_SYNC, ADDRESS, ) @@ -320,7 +325,7 @@ class SoledadSignalingTestCase(BaseSoledadTest): """ Test Soledad emits SOLEDAD_CREATING_KEYS signal. """ - soledad.signal.reset_mock() + soledad.client.signal.reset_mock() sol = self._soledad_instance() # mock the sync target old_get_sync_info = SoledadSyncTarget.get_sync_info @@ -330,7 +335,7 @@ class SoledadSignalingTestCase(BaseSoledadTest): # check for new data to sync sol.need_sync('http://provider/userdb') # assert the signal has been emitted - soledad.signal.assert_called_with( + soledad.client.signal.assert_called_with( proto.SOLEDAD_NEW_DATA_TO_SYNC, ADDRESS, ) diff --git a/soledad/src/leap/soledad/tests/test_sqlcipher.py b/common/src/leap/soledad/common/tests/test_sqlcipher.py index 25d04861..66a673b6 100644 --- a/soledad/src/leap/soledad/tests/test_sqlcipher.py +++ b/common/src/leap/soledad/common/tests/test_sqlcipher.py @@ -43,27 +43,27 @@ from u1db.backends.sqlite_backend import SQLitePartialExpandDatabase # soledad stuff. -from leap.soledad.document import SoledadDocument -from leap.soledad.sqlcipher import ( +from leap.soledad.common.document import SoledadDocument +from leap.soledad.client.sqlcipher import ( SQLCipherDatabase, DatabaseIsNotEncrypted, open as u1db_open, ) -from leap.soledad.target import ( +from leap.soledad.common.crypto import ( EncryptionSchemes, - decrypt_doc, ENC_JSON_KEY, ENC_SCHEME_KEY, ) +from leap.soledad.client.target import decrypt_doc # u1db tests stuff. -from leap.soledad.tests import u1db_tests as tests, BaseSoledadTest -from leap.soledad.tests.u1db_tests import test_sqlite_backend -from leap.soledad.tests.u1db_tests import test_backends -from leap.soledad.tests.u1db_tests import test_open -from leap.soledad.tests.u1db_tests import test_sync -from leap.soledad.target import SoledadSyncTarget +from leap.soledad.common.tests import u1db_tests as tests, BaseSoledadTest +from leap.soledad.common.tests.u1db_tests import test_sqlite_backend +from leap.soledad.common.tests.u1db_tests import test_backends +from leap.soledad.common.tests.u1db_tests import test_open +from leap.soledad.common.tests.u1db_tests import test_sync +from leap.soledad.client.target import SoledadSyncTarget from leap.common.testing.basetest import BaseLeapTest PASSWORD = '123456' @@ -438,7 +438,8 @@ def sync_via_synchronizer_and_leap(test, db_source, db_target, if trace_hook: test.skipTest("full trace hook unsupported over http") path = test._http_at[db_target] - target = SoledadSyncTarget.connect(test.getURL(path), test._soledad._crypto) + target = SoledadSyncTarget.connect( + test.getURL(path), test._soledad._crypto) target.set_token_credentials('user-uuid', 'auth-token') if trace_hook_shallow: target._set_trace_hook_shallow(trace_hook_shallow) @@ -682,7 +683,7 @@ class SQLCipherSyncTargetTests( def setUp(self): test_sync.DatabaseSyncTargetTests.setUp(self) - BaseSoledadTest.setUp(self) + #BaseSoledadTest.setUp(self) def tearDown(self): test_sync.DatabaseSyncTargetTests.tearDown(self) diff --git a/soledad/src/leap/soledad/tests/test_target.py b/common/src/leap/soledad/common/tests/test_target.py index ca2878a5..5a541745 100644 --- a/soledad/src/leap/soledad/tests/test_target.py +++ b/common/src/leap/soledad/common/tests/test_target.py @@ -34,25 +34,26 @@ from u1db.remote import ( http_target, ) -from leap import soledad -from leap.soledad import ( +from leap.soledad import client +from leap.soledad.client import ( target, auth, + VerifiedHTTPSConnection, ) -from leap.soledad.document import SoledadDocument -from leap.soledad_server import SoledadApp -from leap.soledad_server.auth import SoledadTokenAuthMiddleware +from leap.soledad.common.document import SoledadDocument +from leap.soledad.server import SoledadApp +from leap.soledad.server.auth import SoledadTokenAuthMiddleware -from leap.soledad.tests import u1db_tests as tests -from leap.soledad.tests import BaseSoledadTest -from leap.soledad.tests.u1db_tests import test_backends -from leap.soledad.tests.u1db_tests import test_http_database -from leap.soledad.tests.u1db_tests import test_http_client -from leap.soledad.tests.u1db_tests import test_document -from leap.soledad.tests.u1db_tests import test_remote_sync_target -from leap.soledad.tests.u1db_tests import test_https -from leap.soledad.tests.u1db_tests import test_sync +from leap.soledad.common.tests import u1db_tests as tests +from leap.soledad.common.tests import BaseSoledadTest +from leap.soledad.common.tests.u1db_tests import test_backends +from leap.soledad.common.tests.u1db_tests import test_http_database +from leap.soledad.common.tests.u1db_tests import test_http_client +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 #----------------------------------------------------------------------------- @@ -77,7 +78,7 @@ def make_token_soledad_app(state): return True return False - # we test for action authorization in leap.soledad.tests.test_server + # we test for action authorization in leap.soledad.common.tests.test_server def _verify_authorization(uuid, environ): return True @@ -504,8 +505,9 @@ def token_leap_https_sync_target(test, host, path): return st -class TestSoledadSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSupport, - BaseSoledadTest): +class TestSoledadSyncTargetHttpsSupport( + test_https.TestHttpSyncTargetHttpsSupport, + BaseSoledadTest): scenarios = [ ('token_soledad_https', @@ -520,8 +522,8 @@ class TestSoledadSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSuppor # run smoothly with standard u1db. test_https.TestHttpSyncTargetHttpsSupport.setUp(self) # so here monkey patch again to test our functionality. - http_client._VerifiedHTTPSConnection = soledad.VerifiedHTTPSConnection - soledad.SOLEDAD_CERT = http_client.CA_CERTS + http_client._VerifiedHTTPSConnection = VerifiedHTTPSConnection + client.SOLEDAD_CERT = http_client.CA_CERTS def test_working(self): """ @@ -532,7 +534,7 @@ class TestSoledadSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSuppor """ self.startServer() db = self.request_state._create_database('test') - self.patch(soledad, 'SOLEDAD_CERT', self.cacert_pem) + self.patch(client, 'SOLEDAD_CERT', self.cacert_pem) remote_target = self.getSyncTarget('localhost', 'test') remote_target.record_sync_info('other-id', 2, 'T-id') self.assertEqual( @@ -548,7 +550,7 @@ class TestSoledadSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSuppor """ self.startServer() self.request_state._create_database('test') - self.patch(soledad, 'SOLEDAD_CERT', self.cacert_pem) + self.patch(client, 'SOLEDAD_CERT', self.cacert_pem) remote_target = self.getSyncTarget('127.0.0.1', 'test') self.assertRaises( http_client.CertificateError, remote_target.record_sync_info, diff --git a/soledad/src/leap/soledad/tests/u1db_tests/README b/common/src/leap/soledad/common/tests/u1db_tests/README index 605f01fa..605f01fa 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/README +++ b/common/src/leap/soledad/common/tests/u1db_tests/README diff --git a/soledad/src/leap/soledad/tests/u1db_tests/__init__.py b/common/src/leap/soledad/common/tests/u1db_tests/__init__.py index 43304b43..3bc12487 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/__init__.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/__init__.py @@ -189,8 +189,11 @@ class DatabaseBaseTests(TestCase): scenarios = LOCAL_DATABASES_SCENARIOS - def create_database(self, replica_uid): - return self.make_database_for_test(self, replica_uid) + def make_database_for_test(self, replica_uid): + return make_memory_database_for_test(self, replica_uid) + + def create_database(self, *args): + return self.make_database_for_test(self, *args) def copy_database(self, db): # DO NOT COPY OR REUSE THIS CODE OUTSIDE TESTS: COPYING U1DB DATABASES diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_backends.py b/common/src/leap/soledad/common/tests/u1db_tests/test_backends.py index a53b01ba..d2a91d11 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_backends.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_backends.py @@ -26,12 +26,12 @@ from u1db import ( vectorclock, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests simple_doc = tests.simple_doc nested_doc = tests.nested_doc -from leap.soledad.tests.u1db_tests.test_remote_sync_target import ( +from leap.soledad.common.tests.u1db_tests.test_remote_sync_target import ( make_http_app, make_oauth_http_app, ) diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_document.py b/common/src/leap/soledad/common/tests/u1db_tests/test_document.py index e706e1a9..8b30ed51 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_document.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_document.py @@ -17,7 +17,7 @@ from u1db import errors -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests class TestDocument(tests.TestCase): diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_http_app.py b/common/src/leap/soledad/common/tests/u1db_tests/test_http_app.py index e0729aa2..789006ba 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_http_app.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_http_app.py @@ -30,7 +30,7 @@ from u1db import ( sync, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests from u1db.remote import ( http_app, diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_http_client.py b/common/src/leap/soledad/common/tests/u1db_tests/test_http_client.py index 42e98461..08e9714e 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_http_client.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_http_client.py @@ -26,7 +26,7 @@ from u1db import ( errors, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests from u1db.remote import ( http_client, diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_http_database.py b/common/src/leap/soledad/common/tests/u1db_tests/test_http_database.py index f21e6da1..3f3c7bba 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_http_database.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_http_database.py @@ -27,13 +27,13 @@ from u1db import ( Document, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests from u1db.remote import ( http_database, http_target, ) -from leap.soledad.tests.u1db_tests.test_remote_sync_target import ( +from leap.soledad.common.tests.u1db_tests.test_remote_sync_target import ( make_http_app, ) diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_https.py b/common/src/leap/soledad/common/tests/u1db_tests/test_https.py index 62180f8c..c086fbc0 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_https.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_https.py @@ -12,8 +12,8 @@ from u1db.remote import ( ) from leap import soledad -from leap.soledad.tests import u1db_tests as tests -from leap.soledad.tests.u1db_tests.test_remote_sync_target import ( +from leap.soledad.common.tests import u1db_tests as tests +from leap.soledad.common.tests.u1db_tests.test_remote_sync_target import ( make_oauth_http_app, ) @@ -75,7 +75,7 @@ class TestHttpSyncTargetHttpsSupport(tests.TestCaseWithServer): # order to maintain the compatibility with u1db default tests, we undo # that replacement here. http_client._VerifiedHTTPSConnection = \ - soledad.old__VerifiedHTTPSConnection + soledad.client.old__VerifiedHTTPSConnection super(TestHttpSyncTargetHttpsSupport, self).setUp() def getSyncTarget(self, host, path=None): diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_open.py b/common/src/leap/soledad/common/tests/u1db_tests/test_open.py index 0ff307e8..13425b4f 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_open.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_open.py @@ -22,9 +22,9 @@ from u1db import ( errors, open as u1db_open, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests from u1db.backends import sqlite_backend -from leap.soledad.tests.u1db_tests.test_backends import TestAlternativeDocument +from leap.soledad.common.tests.u1db_tests.test_backends import TestAlternativeDocument class TestU1DBOpen(tests.TestCase): diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py b/common/src/leap/soledad/common/tests/u1db_tests/test_remote_sync_target.py index 66d404d2..3793e0df 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_remote_sync_target.py @@ -22,7 +22,7 @@ from u1db import ( errors, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests from u1db.remote import ( http_app, diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py b/common/src/leap/soledad/common/tests/u1db_tests/test_sqlite_backend.py index 1380e4b1..a53ea6cc 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_sqlite_backend.py @@ -27,10 +27,10 @@ from u1db import ( query_parser, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests from u1db.backends import sqlite_backend -from leap.soledad.tests.u1db_tests.test_backends import TestAlternativeDocument +from leap.soledad.common.tests.u1db_tests.test_backends import TestAlternativeDocument simple_doc = '{"key": "value"}' diff --git a/soledad/src/leap/soledad/tests/u1db_tests/test_sync.py b/common/src/leap/soledad/common/tests/u1db_tests/test_sync.py index 96aa2736..5346d540 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/test_sync.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/test_sync.py @@ -26,7 +26,7 @@ from u1db import ( SyncTarget, ) -from leap.soledad.tests import u1db_tests as tests +from leap.soledad.common.tests import u1db_tests as tests from u1db.backends import ( inmemory, @@ -35,7 +35,7 @@ from u1db.remote import ( http_target, ) -from leap.soledad.tests.u1db_tests.test_remote_sync_target import ( +from leap.soledad.common.tests.u1db_tests.test_remote_sync_target import ( make_http_app, make_oauth_http_app, ) @@ -85,7 +85,8 @@ class DatabaseSyncTargetTests(tests.DatabaseBaseTests, whitebox = True def setUp(self): - super(DatabaseSyncTargetTests, self).setUp() + tests.DatabaseBaseTests.setUp(self) + tests.TestCaseWithServer.setUp(self) self.db, self.st = self.create_db_and_target(self) self.other_changes = [] @@ -96,6 +97,9 @@ class DatabaseSyncTargetTests(tests.DatabaseBaseTests, del self.db super(DatabaseSyncTargetTests, self).tearDown() + def create_db_and_target(self, *args): + return _make_local_db_and_target(*args) + def receive_doc(self, doc, gen, trans_id): self.other_changes.append( (doc.doc_id, doc.rev, doc.get_json(), gen, trans_id)) diff --git a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/Makefile index 2385e75b..2385e75b 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile +++ b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/Makefile diff --git a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/cacert.pem index c019a730..c019a730 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem +++ b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/cacert.pem diff --git a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/testing.cert index 985684fb..985684fb 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert +++ b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/testing.cert diff --git a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/testing.key index d83d4920..d83d4920 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key +++ b/common/src/leap/soledad/common/tests/u1db_tests/testing-certs/testing.key diff --git a/server/changes/feature_3487-split-soledad-into-common-client-and-server b/server/changes/feature_3487-split-soledad-into-common-client-and-server new file mode 100644 index 00000000..2eab6b56 --- /dev/null +++ b/server/changes/feature_3487-split-soledad-into-common-client-and-server @@ -0,0 +1 @@ + o Split soledad package into common, client and server. Closes #3487. diff --git a/soledad_server/pkg/soledad b/server/pkg/soledad index c233731e..c233731e 100644 --- a/soledad_server/pkg/soledad +++ b/server/pkg/soledad diff --git a/soledad_server/setup.py b/server/setup.py index 6229cf62..89354a57 100644 --- a/soledad_server/setup.py +++ b/server/setup.py @@ -31,10 +31,9 @@ install_requirements = [ 'oauth', # this is not strictly needed by us, but we need it # until u1db adds it to its release as a dep. 'u1db', - 'six==1.1.0', 'routes', 'PyOpenSSL', - 'leap.soledad>=0.3.0', + 'leap.soledad.common>=0.3.0', ] @@ -44,6 +43,7 @@ else: # XXX this should go only for linux/mac data_files = [("/etc/init.d/", ["pkg/soledad"])] + trove_classifiers = ( "Development Status :: 3 - Alpha", "Intended Audience :: Developers", @@ -58,8 +58,9 @@ trove_classifiers = ( "Topic :: Software Development :: Libraries :: Python Modules" ) + setup( - name='leap.soledad_server', + name='leap.soledad.server', version='0.3.0', url='https://leap.se/', license='GPLv3+', @@ -71,7 +72,7 @@ setup( "securely shared among devices. It provides, to other parts of the " "LEAP client, an API for data storage and sync." ), - namespace_packages=["leap"], + namespace_packages=["leap", "leap.soledad"], packages=find_packages('src'), package_dir={'': 'src'}, install_requires=install_requirements, diff --git a/server/src/leap/__init__.py b/server/src/leap/__init__.py new file mode 100644 index 00000000..f48ad105 --- /dev/null +++ b/server/src/leap/__init__.py @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/server/src/leap/soledad/__init__.py b/server/src/leap/soledad/__init__.py new file mode 100644 index 00000000..f48ad105 --- /dev/null +++ b/server/src/leap/soledad/__init__.py @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/soledad_server/src/leap/soledad_server/__init__.py b/server/src/leap/soledad/server/__init__.py index 18a0546e..4ed8efc9 100644 --- a/soledad_server/src/leap/soledad_server/__init__.py +++ b/server/src/leap/soledad/server/__init__.py @@ -43,8 +43,8 @@ if version.base() == "12.0.0": sys.modules['OpenSSL.tsafe'] = old_tsafe -from leap.soledad_server.auth import SoledadTokenAuthMiddleware -from leap.soledad_server.couch import CouchServerState +from leap.soledad.server.auth import SoledadTokenAuthMiddleware +from leap.soledad.common.couch import CouchServerState #----------------------------------------------------------------------------- @@ -122,4 +122,4 @@ state = CouchServerState(conf['couch_url']) # WSGI application that may be used by `twistd -web` application = SoledadTokenAuthMiddleware(SoledadApp(state)) -resource = WSGIResource(reactor, reactor.getThreadPool(), application)
\ No newline at end of file +resource = WSGIResource(reactor, reactor.getThreadPool(), application) diff --git a/soledad_server/src/leap/soledad_server/auth.py b/server/src/leap/soledad/server/auth.py index 3bcfcf04..3bcfcf04 100644 --- a/soledad_server/src/leap/soledad_server/auth.py +++ b/server/src/leap/soledad/server/auth.py diff --git a/soledad/src/leap/soledad/backends/__init__.py b/soledad/src/leap/soledad/backends/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/soledad/src/leap/soledad/backends/__init__.py +++ /dev/null diff --git a/soledad/src/leap/soledad/backends/leap_backend.py b/soledad/src/leap/soledad/backends/leap_backend.py deleted file mode 100644 index a8d76b67..00000000 --- a/soledad/src/leap/soledad/backends/leap_backend.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -# leap_backend.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - - -""" -This file exists to provide backwards compatibility with code that uses -Soledad before the refactor that removed the leap_backend module. -""" - - -import logging -import warnings - - -from leap.soledad import document -from leap.soledad import target - - -logger = logging.getLogger(name=__name__) - - -def warn(oldclass, newclass): - """ - Warns about deprecation of C{oldclass}, which must be substituted by - C{newclass}. - - @param oldclass: The class that is deprecated. - @type oldclass: type - @param newclass: The class that should be used instead. - @type newclass: type - """ - message = \ - "%s is deprecated and will be removed soon. Please use %s instead." \ - % (str(oldclass), str(newclass)) - print message - logger.warning(message) - warnings.warn(message, DeprecationWarning, stacklevel=2) - - -class LeapDocument(document.SoledadDocument): - """ - This class exists to provide backwards compatibility with code that still - uses C{leap.soledad.backends.leap_backend.LeapDocument}. - """ - - def __init__(self, *args, **kwargs): - warn(self.__class__, document.SoledadDocument) - document.SoledadDocument.__init__(self, *args, **kwargs) - - -class EncryptionSchemes(target.EncryptionSchemes): - """ - This class exists to provide backwards compatibility with code that still - uses C{leap.soledad.backends.leap_backend.EncryptionSchemes}. - """ - - def __init__(self, *args, **kwargs): - warn(self.__class__, target.EncryptionSchemes) - target.EncryptionSchemes.__init__(self, *args, **kwargs) |