diff options
author | drebs <drebs@leap.se> | 2013-08-19 10:13:51 -0300 |
---|---|---|
committer | drebs <drebs@leap.se> | 2013-08-21 14:40:33 -0300 |
commit | 11ac38cb021e97cca77df2e9cf15f9136b24fc43 (patch) | |
tree | 2f4e3e004f57ac3436d8258937bf2145ca3de576 | |
parent | f424e2fee278bd9f3a9fb838025db7d4c1bb44bb (diff) |
Split soledad into common, client and server.
-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) | 57 | ||||
-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 | 183 | ||||
-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) | 11 | ||||
-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) | 11 | ||||
-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) | 9 | ||||
-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) | 90 | ||||
-rw-r--r-- | common/src/leap/soledad/common/tests/test_sqlcipher.py (renamed from soledad/src/leap/soledad/tests/test_sqlcipher.py) | 23 | ||||
-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) | 0 | ||||
-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) | 4 | ||||
-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 |
54 files changed, 583 insertions, 308 deletions
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 67ef3cc8..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 fc0c10ce..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,12 +81,14 @@ except ImportError: logger.info("Would signal: %s - %s." % (str(signal), content)) -from leap.soledad.crypto import SoledadCrypto -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__) 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/client/src/leap/soledad/client/dbwrapper.py b/client/src/leap/soledad/client/dbwrapper.py new file mode 100644 index 00000000..a27a933e --- /dev/null +++ b/client/src/leap/soledad/client/dbwrapper.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +# dbwrapper.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/>. +""" +Thread-safe wrapper for sqlite/pysqlcipher. + +*TODO* +At some point we surely will want to switch to a twisted way of dealing +with this, using defers and proper callbacks. But I had this tested for +some time so postponing that refactor. +""" +import logging +import threading +import Queue +import time + +import exceptions + +from functools import partial + +from leap.soledad.client import sqlcipher + +logger = logging.getLogger(__name__) + + +class SQLCipherWrapper(threading.Thread): + + def __init__(self, *args, **kwargs): + """ + Initializes a wrapper that proxies method and attribute + access to an underlying SQLCipher instance. We instantiate sqlcipher + in a thread, and all method accesses communicate with it using a + Queue. + + :param *args: position arguments to pass to pysqlcipher initialization + :type args: tuple + + :param **kwargs: keyword arguments to pass to pysqlcipher + initialization + :type kwargs: dict + """ + threading.Thread.__init__(self) + self._db = None + self._wrargs = args, kwargs + + self._queue = Queue.Queue() + self._stopped = threading.Event() + + self.start() + + def _init_db(self): + """ + Initializes sqlcipher database. + + This is called on a separate thread. + """ + # instantiate u1db + args, kwargs = self._wrargs + self._db = sqlcipher.open(*args, **kwargs) + + def run(self): + """ + Main loop for the sqlcipher thread. + """ + logger.debug("SQLCipherWrapper thread started.") + logger.debug("Initializing sqlcipher") + end_mths = ("__end_thread", "_SQLCipherWrapper__end_thread") + + self._init_db() + self._lock = threading.Lock() + + ct = 0 + started = False + + while True: + if self._db is None: + if started: + break + if ct > 10: + break # XXX DEBUG + logger.debug('db not ready yet, waiting...') + time.sleep(1) + ct += 1 + + started = True + + with self._lock: + try: + mth, q, wrargs = self._queue.get() + except: + logger.error("exception getting args from queue") + + res = None + attr = getattr(self._db, mth, None) + if not attr: + if mth not in end_mths: + logger.error('method %s does not exist' % (mth,)) + res = AttributeError( + "_db instance has no attribute %s" % mth) + + elif callable(attr): + # invoke the method with the passed args + args = wrargs.get('args', []) + kwargs = wrargs.get('kwargs', {}) + try: + res = attr(*args, **kwargs) + except Exception as e: + logger.error( + "Error on proxied method %s: '%r'." % ( + attr, e)) + res = e + else: + # non-callable attribute + res = attr + logger.debug('returning proxied db call...') + q.put(res) + + if mth in end_mths: + logger.debug('ending thread') + break + + logger.debug("SQLCipherWrapper thread terminated.") + self._stopped.set() + + def close(self): + """ + Closes the sqlcipher database and finishes the thread. This method + should always be called explicitely. + """ + self.__getattr__('close')() + self.__end_thread() + + def __getattr__(self, attr): + """ + Returns _db proxied attributes. + """ + + def __proxied_mth(method, *args, **kwargs): + if not self._stopped.isSet(): + wrargs = {'args': args, 'kwargs': kwargs} + q = Queue.Queue() + self._queue.put((method, q, wrargs)) + res = q.get() + q.task_done() + + if isinstance(res, exceptions.BaseException): + # XXX should get the original bt + raise res + return res + else: + logger.warning("tried to call proxied meth " + "but stopped is set: %s" % + (method,)) + + rgetattr = object.__getattribute__ + + if attr != "_db": + proxied = partial(__proxied_mth, attr) + return proxied + + # fallback to regular behavior + return rgetattr(self, attr) + + def __del__(self): + """ + Do not trust this get called. No guarantees given. Because of a funny + dance with the refs and the way the gc works, we should be calling the + close method explicitely. + """ + self.close() 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 94d0fb04..c605c28c 100644 --- a/soledad/src/leap/soledad/sqlcipher.py +++ b/client/src/leap/soledad/client/sqlcipher.py @@ -1,4 +1,4 @@ - # -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # sqlcipher.py # Copyright (C) 2013 LEAP # @@ -53,7 +53,7 @@ import threading from u1db.backends import sqlite_backend from pysqlcipher import dbapi2 from u1db import errors as u1db_errors -from leap.soledad.document import SoledadDocument +from leap.soledad.common.document import SoledadDocument logger = logging.getLogger(__name__) @@ -328,7 +328,7 @@ 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, @@ -339,10 +339,13 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): """ 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 """ - # XXX used??? private method not used in module c.execute( 'ALTER TABLE document ' 'ADD COLUMN syncable BOOL NOT NULL DEFAULT TRUE') 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 84882e27..9f47d74a 100644 --- a/soledad/src/leap/soledad/tests/__init__.py +++ b/common/src/leap/soledad/common/tests/__init__.py @@ -9,10 +9,13 @@ import u1db from mock import Mock -from leap.soledad import Soledad -from leap.soledad.document import SoledadDocument -from leap.soledad.target import decrypt_doc -from leap.soledad.target import ENC_SCHEME_KEY +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, +) from leap.common.testing.basetest import BaseLeapTest 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..0c7b8254 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, 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 531bb572..0b753647 100644 --- a/soledad/src/leap/soledad/tests/test_soledad.py +++ b/common/src/leap/soledad/common/tests/test_soledad.py @@ -27,16 +27,16 @@ from mock import Mock from pysqlcipher.dbapi2 import DatabaseError 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): @@ -58,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): @@ -142,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): @@ -203,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 @@ -217,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, ) @@ -273,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, ) @@ -307,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 @@ -316,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, ) @@ -325,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 @@ -335,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 4c4384b8..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) 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 3bc12487..3bc12487 100644 --- a/soledad/src/leap/soledad/tests/u1db_tests/__init__.py +++ b/common/src/leap/soledad/common/tests/u1db_tests/__init__.py 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 a87c1787..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, ) 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) |