diff options
-rw-r--r-- | changes/bug_all_backends | 3 | ||||
-rw-r--r-- | changes/bug_modifiable_timeout | 3 | ||||
-rw-r--r-- | changes/feature_split-soledad-client-and-server | 1 | ||||
-rw-r--r-- | soledad/setup.py (renamed from setup.py) | 20 | ||||
-rw-r--r-- | soledad/src/leap/__init__.py (renamed from src/leap/__init__.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/__init__.py (renamed from src/leap/soledad/__init__.py) | 40 | ||||
-rw-r--r-- | soledad/src/leap/soledad/auth.py (renamed from src/leap/soledad/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 | ||||
-rw-r--r-- | soledad/src/leap/soledad/crypto.py (renamed from src/leap/soledad/crypto.py) | 2 | ||||
-rw-r--r-- | soledad/src/leap/soledad/document.py | 111 | ||||
-rw-r--r-- | soledad/src/leap/soledad/shared_db.py (renamed from src/leap/soledad/shared_db.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/sqlcipher.py (renamed from src/leap/soledad/backends/sqlcipher.py) | 8 | ||||
-rw-r--r-- | soledad/src/leap/soledad/target.py (renamed from src/leap/soledad/backends/leap_backend.py) | 103 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/__init__.py (renamed from src/leap/soledad/tests/__init__.py) | 16 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/couchdb.ini.template (renamed from src/leap/soledad/tests/couchdb.ini.template) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/test_couch.py (renamed from src/leap/soledad/tests/test_couch.py) | 9 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/test_crypto.py (renamed from src/leap/soledad/tests/test_crypto.py) | 54 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/test_server.py (renamed from src/leap/soledad/tests/test_server.py) | 22 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/test_soledad.py (renamed from src/leap/soledad/tests/test_soledad.py) | 14 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/test_sqlcipher.py (renamed from src/leap/soledad/tests/test_sqlcipher.py) | 28 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/test_target.py (renamed from src/leap/soledad/tests/test_leap_backend.py) | 62 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/README (renamed from src/leap/soledad/tests/u1db_tests/README) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/__init__.py (renamed from src/leap/soledad/tests/u1db_tests/__init__.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_backends.py (renamed from src/leap/soledad/tests/u1db_tests/test_backends.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_document.py (renamed from src/leap/soledad/tests/u1db_tests/test_document.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_http_app.py (renamed from src/leap/soledad/tests/u1db_tests/test_http_app.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_http_client.py (renamed from src/leap/soledad/tests/u1db_tests/test_http_client.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_http_database.py (renamed from src/leap/soledad/tests/u1db_tests/test_http_database.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_https.py (renamed from src/leap/soledad/tests/u1db_tests/test_https.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_open.py (renamed from src/leap/soledad/tests/u1db_tests/test_open.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py (renamed from src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py (renamed from src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/test_sync.py (renamed from src/leap/soledad/tests/u1db_tests/test_sync.py) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile (renamed from src/leap/soledad/tests/u1db_tests/testing-certs/Makefile) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem (renamed from src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert (renamed from src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert) | 0 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key (renamed from src/leap/soledad/tests/u1db_tests/testing-certs/testing.key) | 0 | ||||
-rw-r--r-- | soledad_server/pkg/soledad (renamed from pkg/soledad) | 0 | ||||
-rw-r--r-- | soledad_server/setup.py | 79 | ||||
-rw-r--r-- | soledad_server/src/leap/__init__.py | 6 | ||||
-rw-r--r-- | soledad_server/src/leap/soledad_server/__init__.py (renamed from src/leap/soledad/server.py) | 11 | ||||
-rw-r--r-- | soledad_server/src/leap/soledad_server/couch.py (renamed from src/leap/soledad/backends/couch.py) | 8 | ||||
-rw-r--r-- | soledad_server/src/leap/soledad_server/objectstore.py (renamed from src/leap/soledad/backends/objectstore.py) | 0 | ||||
-rw-r--r-- | src/leap/soledad/backends/__init__.py | 36 |
45 files changed, 423 insertions, 286 deletions
diff --git a/changes/bug_all_backends b/changes/bug_all_backends new file mode 100644 index 00000000..bfb7253d --- /dev/null +++ b/changes/bug_all_backends @@ -0,0 +1,3 @@ + o Do not list the backends in the __init__'s __all__ to allow not + supporting couch on the client side until the code is diveded into + client and server.
\ No newline at end of file diff --git a/changes/bug_modifiable_timeout b/changes/bug_modifiable_timeout new file mode 100644 index 00000000..be690d5a --- /dev/null +++ b/changes/bug_modifiable_timeout @@ -0,0 +1,3 @@ + o Database request have default timeout too high, a + soledad.SOLEDAD_TIMEOUT variable has been added in order to have + more control over this. Fixes #2713
\ No newline at end of file diff --git a/changes/feature_split-soledad-client-and-server b/changes/feature_split-soledad-client-and-server new file mode 100644 index 00000000..0abab836 --- /dev/null +++ b/changes/feature_split-soledad-client-and-server @@ -0,0 +1 @@ + o Split soledad client and server into two different packages. diff --git a/setup.py b/soledad/setup.py index b989f48a..f0d070da 100644 --- a/setup.py +++ b/soledad/setup.py @@ -16,7 +16,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os from setuptools import ( setup, find_packages @@ -24,20 +23,17 @@ from setuptools import ( install_requirements = [ - 'configparser', - 'couchdb', 'pysqlcipher', + 'pysqlite', # TODO: this should not be a dep, see #2945 'simplejson', - 'twisted>=12.0.0', # TODO: maybe we just want twisted-web? 'oauth', # this is not strictly needed by us, but we need it # until u1db adds it to its release as a dep. 'u1db', - 'requests', 'six==1.1.0', 'scrypt', - 'routes', 'pyxdg', 'pycrypto', + 'pyOpenSSL', ] @@ -46,15 +42,10 @@ tests_requirements = [ 'nose2', 'testscenarios', 'leap.common', + 'leap.soledad_server', ] -if os.environ.get('VIRTUAL_ENV', None): - data_files = None -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", @@ -88,9 +79,6 @@ setup( test_suite='leap.soledad.tests', install_requires=install_requirements, tests_require=tests_requirements, - data_files=data_files, classifiers=trove_classifiers, - extras_require={ - 'signaling': ['leap.common'], - } + extras_require={'signaling': ['leap.common']}, ) diff --git a/src/leap/__init__.py b/soledad/src/leap/__init__.py index f48ad105..f48ad105 100644 --- a/src/leap/__init__.py +++ b/soledad/src/leap/__init__.py diff --git a/src/leap/soledad/__init__.py b/soledad/src/leap/soledad/__init__.py index ea3f676b..2e1155f9 100644 --- a/src/leap/soledad/__init__.py +++ b/soledad/src/leap/soledad/__init__.py @@ -134,13 +134,12 @@ except ImportError: pass -from leap.soledad.backends import sqlcipher -from leap.soledad.backends.leap_backend import ( - LeapDocument, - LeapSyncTarget, +from leap.soledad.document import SoledadDocument +from leap.soledad.sqlcipher import ( + open as sqlcipher_open, + SQLCipherDatabase, ) - -from leap.soledad import shared_db +from leap.soledad.target import SoledadSyncTarget from leap.soledad.shared_db import SoledadSharedDatabase from leap.soledad.crypto import SoledadCrypto @@ -418,11 +417,11 @@ class Soledad(object): secret[salt_start:salt_end], # the salt buflen=32, # we need a key with 256 bits (32 bytes) ) - self._db = sqlcipher.open( + self._db = sqlcipher_open( self._local_db_path, binascii.b2a_hex(key), # sqlcipher only accepts the hex version create=True, - document_factory=LeapDocument, + document_factory=SoledadDocument, crypto=self._crypto, raw_key=True) @@ -432,7 +431,7 @@ class Soledad(object): """ if hasattr(self, '_db') and isinstance( self._db, - sqlcipher.SQLCipherDatabase): + SQLCipherDatabase): self._db.close() def __del__(self): @@ -634,7 +633,7 @@ class Soledad(object): database. @return: a document with encrypted key material in its contents - @rtype: LeapDocument + @rtype: SoledadDocument """ signal(SOLEDAD_DOWNLOADING_KEYS, self._uuid) db = self._shared_db() @@ -661,7 +660,7 @@ class Soledad(object): # try to get secrets doc from server, otherwise create it doc = self._get_secrets_from_shared_db() if doc is None: - doc = LeapDocument(doc_id=self._uuid_hash()) + doc = SoledadDocument(doc_id=self._uuid_hash()) # fill doc with encrypted secrets doc.content = self.export_recovery_document(include_uuid=False) # upload secrets to server @@ -682,7 +681,7 @@ class Soledad(object): Update a document in the local encrypted database. @param doc: the document to update - @type doc: LeapDocument + @type doc: SoledadDocument @return: the new revision identifier for the document @rtype: str @@ -694,7 +693,7 @@ class Soledad(object): Delete a document from the local encrypted database. @param doc: the document to delete - @type doc: LeapDocument + @type doc: SoledadDocument @return: the new revision identifier for the document @rtype: str @@ -713,7 +712,7 @@ class Soledad(object): @type include_deleted: bool @return: the document object or None - @rtype: LeapDocument + @rtype: SoledadDocument """ return self._db.get_doc(doc_id, include_deleted=include_deleted) @@ -758,7 +757,7 @@ class Soledad(object): @type doc_id: str @return: the new document - @rtype: LeapDocument + @rtype: SoledadDocument """ return self._db.create_doc(content, doc_id=doc_id) @@ -777,7 +776,7 @@ class Soledad(object): @param doc_id: An optional identifier specifying the document id. @type doc_id: @return: The new cocument - @rtype: LeapDocument + @rtype: SoledadDocument """ return self._db.create_doc_from_json(json, doc_id=doc_id) @@ -903,7 +902,7 @@ class Soledad(object): Mark a document as no longer conflicted. @param doc: a document with the new content to be inserted. - @type doc: LeapDocument + @type doc: SoledadDocument @param conflicted_doc_revs: a list of revisions that the new content supersedes. @type conflicted_doc_revs: list @@ -937,7 +936,7 @@ class Soledad(object): @return: Whether remote replica and local replica differ. @rtype: bool """ - target = LeapSyncTarget(url, creds=self._creds, crypto=self._crypto) + target = SoledadSyncTarget(url, creds=self._creds, crypto=self._crypto) info = target.get_sync_info(self._db._get_replica_uid()) # compare source generation with target's last known source generation if self._db._get_generation() != info[4]: @@ -1073,6 +1072,9 @@ class Soledad(object): # Monkey patching u1db to be able to provide a custom SSL cert #----------------------------------------------------------------------------- +# We need a more reasonable timeout (in seconds) +SOLEDAD_TIMEOUT = 10 + class VerifiedHTTPSConnection(httplib.HTTPSConnection): """HTTPSConnection verifying server side certificates.""" # derived from httplib.py @@ -1080,7 +1082,7 @@ class VerifiedHTTPSConnection(httplib.HTTPSConnection): def connect(self): "Connect to a host on a given (SSL) port." sock = socket.create_connection((self.host, self.port), - self.timeout, self.source_address) + SOLEDAD_TIMEOUT, self.source_address) if self._tunnel_host: self.sock = sock self._tunnel() diff --git a/src/leap/soledad/auth.py b/soledad/src/leap/soledad/auth.py index 8c093099..8c093099 100644 --- a/src/leap/soledad/auth.py +++ b/soledad/src/leap/soledad/auth.py diff --git a/soledad/src/leap/soledad/backends/__init__.py b/soledad/src/leap/soledad/backends/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/soledad/src/leap/soledad/backends/__init__.py diff --git a/soledad/src/leap/soledad/backends/leap_backend.py b/soledad/src/leap/soledad/backends/leap_backend.py new file mode 100644 index 00000000..a8d76b67 --- /dev/null +++ b/soledad/src/leap/soledad/backends/leap_backend.py @@ -0,0 +1,73 @@ +# -*- 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) diff --git a/src/leap/soledad/crypto.py b/soledad/src/leap/soledad/crypto.py index be83e4a2..bfad66d1 100644 --- a/src/leap/soledad/crypto.py +++ b/soledad/src/leap/soledad/crypto.py @@ -136,7 +136,7 @@ class SoledadCrypto(object): 'Wrong key size: %s (must be 256 bits long).' % len(key)) soledad_assert( 'iv' in kwargs, - 'AES-256-CTR needs an initial value given as.') + 'AES-256-CTR needs an initial value.') ctr = Counter.new(64, prefix=binascii.a2b_base64(kwargs['iv'])) cipher = AES.new(key=key, mode=AES.MODE_CTR, counter=ctr) return cipher.decrypt(data) diff --git a/soledad/src/leap/soledad/document.py b/soledad/src/leap/soledad/document.py new file mode 100644 index 00000000..cc24b53a --- /dev/null +++ b/soledad/src/leap/soledad/document.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# document.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/>. + + +""" +A Soledad Document is an u1db.Document with lasers. +""" + + +from u1db import Document + + +# +# SoledadDocument +# + +class SoledadDocument(Document): + """ + Encryptable and syncable document. + + LEAP Documents can be flagged as syncable or not, so the replicas + might not sync every document. + """ + + def __init__(self, doc_id=None, rev=None, json='{}', has_conflicts=False, + syncable=True): + """ + Container for handling an encryptable document. + + @param doc_id: The unique document identifier. + @type doc_id: str + @param rev: The revision identifier of the document. + @type rev: str + @param json: The JSON string for this document. + @type json: str + @param has_conflicts: Boolean indicating if this document has conflicts + @type has_conflicts: bool + @param syncable: Should this document be synced with remote replicas? + @type syncable: bool + """ + Document.__init__(self, doc_id, rev, json, has_conflicts) + self._syncable = syncable + + def _get_syncable(self): + """ + Return whether this document is syncable. + + @return: Is this document syncable? + @rtype: bool + """ + return self._syncable + + def _set_syncable(self, syncable=True): + """ + Determine if this document should be synced with remote replicas. + + @param syncable: Should this document be synced with remote replicas? + @type syncable: bool + """ + self._syncable = syncable + + syncable = property( + _get_syncable, + _set_syncable, + doc="Determine if document should be synced with server." + ) + + def _get_rev(self): + """ + Get the document revision. + + Returning the revision as string solves the following exception in + Twisted web: + exceptions.TypeError: Can only pass-through bytes on Python 2 + + @return: The document revision. + @rtype: str + """ + if self._rev is None: + return None + return str(self._rev) + + def _set_rev(self, rev): + """ + Set document revision. + + @param rev: The new document revision. + @type rev: bytes + """ + self._rev = rev + + rev = property( + _get_rev, + _set_rev, + doc="Wrapper to ensure `doc.rev` is always returned as bytes.") + + diff --git a/src/leap/soledad/shared_db.py b/soledad/src/leap/soledad/shared_db.py index 33c5c484..33c5c484 100644 --- a/src/leap/soledad/shared_db.py +++ b/soledad/src/leap/soledad/shared_db.py diff --git a/src/leap/soledad/backends/sqlcipher.py b/soledad/src/leap/soledad/sqlcipher.py index d6d62f21..acbeabe6 100644 --- a/src/leap/soledad/backends/sqlcipher.py +++ b/soledad/src/leap/soledad/sqlcipher.py @@ -54,7 +54,7 @@ from pysqlcipher import dbapi2 from u1db import ( errors, ) -from leap.soledad.backends.leap_backend import LeapDocument +from leap.soledad.document import SoledadDocument # Monkey-patch u1db.backends.sqlite_backend with pysqlcipher.dbapi2 @@ -169,7 +169,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): def factory(doc_id=None, rev=None, json='{}', has_conflicts=False, syncable=True): - return LeapDocument(doc_id=doc_id, rev=rev, json=json, + return SoledadDocument(doc_id=doc_id, rev=rev, json=json, has_conflicts=has_conflicts, syncable=syncable) self.set_document_factory(factory) @@ -301,10 +301,10 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): @rtype: int """ from u1db.sync import Synchronizer - from leap.soledad.backends.leap_backend import LeapSyncTarget + from leap.soledad.target import SoledadSyncTarget return Synchronizer( self, - LeapSyncTarget(url, + SoledadSyncTarget(url, creds=creds, crypto=self._crypto)).sync(autocreate=autocreate) diff --git a/src/leap/soledad/backends/leap_backend.py b/soledad/src/leap/soledad/target.py index 4d92db37..8b7aa8c7 100644 --- a/src/leap/soledad/backends/leap_backend.py +++ b/soledad/src/leap/soledad/target.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# leap_backend.py +# target.py # Copyright (C) 2013 LEAP # # This program is free software: you can redistribute it and/or modify @@ -27,13 +27,13 @@ import hmac import binascii -from u1db import Document from u1db.remote import utils 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 ( EncryptionMethods, UnknownEncryptionMethod, @@ -96,7 +96,7 @@ class MacMethods(object): # -# Crypto utilities for a LeapDocument. +# Crypto utilities for a SoledadDocument. # ENC_JSON_KEY = '_enc_json' @@ -159,7 +159,7 @@ def encrypt_doc(crypto, doc): @param crypto: A SoledadCryto instance used to perform the encryption. @type crypto: leap.soledad.crypto.SoledadCrypto @param doc: The document with contents to be encrypted. - @type doc: LeapDocument + @type doc: SoledadDocument @return: The JSON serialization of the dict representing the encrypted content. @@ -213,7 +213,7 @@ def decrypt_doc(crypto, doc): @param crypto: A SoledadCryto instance to perform the encryption. @type crypto: leap.soledad.crypto.SoledadCrypto @param doc: The document to be decrypted. - @type doc: LeapDocument + @type doc: SoledadDocument @return: The JSON serialization of the decrypted content. @rtype: str @@ -252,92 +252,11 @@ def decrypt_doc(crypto, doc): return plainjson -class LeapDocument(Document): - """ - Encryptable and syncable document. - - LEAP Documents can be flagged as syncable or not, so the replicas - might not sync every document. - """ - - def __init__(self, doc_id=None, rev=None, json='{}', has_conflicts=False, - syncable=True): - """ - Container for handling an encryptable document. - - @param doc_id: The unique document identifier. - @type doc_id: str - @param rev: The revision identifier of the document. - @type rev: str - @param json: The JSON string for this document. - @type json: str - @param has_conflicts: Boolean indicating if this document has conflicts - @type has_conflicts: bool - @param syncable: Should this document be synced with remote replicas? - @type syncable: bool - """ - Document.__init__(self, doc_id, rev, json, has_conflicts) - self._syncable = syncable - - def _get_syncable(self): - """ - Return whether this document is syncable. - - @return: Is this document syncable? - @rtype: bool - """ - return self._syncable - - def _set_syncable(self, syncable=True): - """ - Determine if this document should be synced with remote replicas. - - @param syncable: Should this document be synced with remote replicas? - @type syncable: bool - """ - self._syncable = syncable - - syncable = property( - _get_syncable, - _set_syncable, - doc="Determine if document should be synced with server." - ) - - def _get_rev(self): - """ - Get the document revision. - - Returning the revision as string solves the following exception in - Twisted web: - exceptions.TypeError: Can only pass-through bytes on Python 2 - - @return: The document revision. - @rtype: str - """ - if self._rev is None: - return None - return str(self._rev) - - def _set_rev(self, rev): - """ - Set document revision. - - @param rev: The new document revision. - @type rev: bytes - """ - self._rev = rev - - rev = property( - _get_rev, - _set_rev, - doc="Wrapper to ensure `doc.rev` is always returned as bytes.") - - # -# LeapSyncTarget +# SoledadSyncTarget # -class LeapSyncTarget(HTTPSyncTarget, TokenBasedAuth): +class SoledadSyncTarget(HTTPSyncTarget, TokenBasedAuth): """ A SyncTarget that encrypts data before sending and decrypts data after receiving. @@ -380,11 +299,11 @@ class LeapSyncTarget(HTTPSyncTarget, TokenBasedAuth): @staticmethod def connect(url, crypto=None): - return LeapSyncTarget(url, crypto=crypto) + return SoledadSyncTarget(url, crypto=crypto) def __init__(self, url, creds=None, crypto=None): """ - Initialize the LeapSyncTarget. + Initialize the SoledadSyncTarget. @param url: The url of the target replica to sync with. @type url: str @@ -441,7 +360,7 @@ class LeapSyncTarget(HTTPSyncTarget, TokenBasedAuth): #------------------------------------------------------------- # if arriving content was symmetrically encrypted, we decrypt # it. - doc = LeapDocument(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: @@ -517,7 +436,7 @@ class LeapSyncTarget(HTTPSyncTarget, TokenBasedAuth): comma = ',' for doc, gen, trans_id in docs_by_generations: # skip non-syncable docs - if isinstance(doc, LeapDocument) and not doc.syncable: + if isinstance(doc, SoledadDocument) and not doc.syncable: continue #------------------------------------------------------------- # symmetric encryption of document's contents diff --git a/src/leap/soledad/tests/__init__.py b/soledad/src/leap/soledad/tests/__init__.py index 9fec5530..b33f866c 100644 --- a/src/leap/soledad/tests/__init__.py +++ b/soledad/src/leap/soledad/tests/__init__.py @@ -8,9 +8,9 @@ from mock import Mock from leap.soledad import Soledad +from leap.soledad.document import SoledadDocument from leap.soledad.crypto import SoledadCrypto -from leap.soledad.backends.leap_backend import ( - LeapDocument, +from leap.soledad.target import ( decrypt_doc, ENC_SCHEME_KEY, ) @@ -37,9 +37,9 @@ class BaseSoledadTest(BaseLeapTest): self.email = ADDRESS # open test dbs self._db1 = u1db.open(self.db1_file, create=True, - document_factory=LeapDocument) + document_factory=SoledadDocument) self._db2 = u1db.open(self.db2_file, create=True, - document_factory=LeapDocument) + document_factory=SoledadDocument) # initialize soledad by hand so we can control keys self._soledad = self._soledad_instance(user=self.email) @@ -257,11 +257,3 @@ RZXoH+FTg9UAW87eqU610npOkT6cRaBxaMK/mDtGNdc= =JTFu -----END PGP PRIVATE KEY BLOCK----- """ - -__all__ = [ - 'test_couch', - 'test_encrypted', - 'test_leap_backend', - 'test_sqlcipher', - 'u1db_tests', -] diff --git a/src/leap/soledad/tests/couchdb.ini.template b/soledad/src/leap/soledad/tests/couchdb.ini.template index 7d0316f0..7d0316f0 100644 --- a/src/leap/soledad/tests/couchdb.ini.template +++ b/soledad/src/leap/soledad/tests/couchdb.ini.template diff --git a/src/leap/soledad/tests/test_couch.py b/soledad/src/leap/soledad/tests/test_couch.py index 60a61b71..a84bb46c 100644 --- a/src/leap/soledad/tests/test_couch.py +++ b/soledad/src/leap/soledad/tests/test_couch.py @@ -27,14 +27,12 @@ from base64 import b64decode from leap.common.files import mkdir_p -from leap.soledad.backends import couch +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 import simplejson as json -from leap.soledad.backends.leap_backend import ( - LeapDocument, -) +from leap.soledad.document import SoledadDocument #----------------------------------------------------------------------------- @@ -185,7 +183,7 @@ def copy_couch_database_for_test(test, db): def make_document_for_test(test, doc_id, rev, content, has_conflicts=False): - return LeapDocument(doc_id, rev, content, has_conflicts=has_conflicts) + return SoledadDocument(doc_id, rev, content, has_conflicts=has_conflicts) COUCH_SCENARIOS = [ @@ -318,7 +316,6 @@ class CouchDatabaseSyncTests(test_sync.DatabaseSyncTests, CouchDBTestCase): self.db2 = None self.db3 = None test_sync.DatabaseSyncTests.setUp(self) - CouchDBTestCase.setUp(self) def tearDown(self): self.db and self.db.delete_database() diff --git a/src/leap/soledad/tests/test_crypto.py b/soledad/src/leap/soledad/tests/test_crypto.py index 59d20fa7..c727a2ff 100644 --- a/src/leap/soledad/tests/test_crypto.py +++ b/soledad/src/leap/soledad/tests/test_crypto.py @@ -33,10 +33,12 @@ from Crypto import Random from leap.common.testing.basetest import BaseLeapTest -from leap.soledad import Soledad -from leap.soledad import crypto -from leap.soledad.backends import leap_backend -from leap.soledad.backends.couch import CouchDatabase +from leap.soledad import ( + Soledad, + crypto, + target, +) +from leap.soledad.document import SoledadDocument from leap.soledad.tests import ( BaseSoledadTest, KEY_FINGERPRINT, @@ -49,12 +51,6 @@ from leap.soledad.tests.u1db_tests import ( ) -# These will be used in the future for EncryptedCouchSyncTestCase -#from leap.soledad.tests.test_couch import CouchDBTestCase -#from leap.soledad.tests.test_leap_backend import make_leap_document_for_test -#from leap.soledad.backends.couch import CouchServerState - - class EncryptedSyncTestCase(BaseSoledadTest): """ Tests that guarantee that data will always be encrypted when syncing. @@ -65,18 +61,18 @@ class EncryptedSyncTestCase(BaseSoledadTest): Test encrypting and decrypting documents. """ simpledoc = {'key': 'val'} - doc1 = leap_backend.LeapDocument(doc_id='id') + doc1 = SoledadDocument(doc_id='id') doc1.content = simpledoc # encrypt doc - doc1.set_json(leap_backend.encrypt_doc(self._soledad._crypto, doc1)) + doc1.set_json(target.encrypt_doc(self._soledad._crypto, doc1)) # assert content is different and includes keys self.assertNotEqual( simpledoc, doc1.content, 'incorrect document encryption') - self.assertTrue(leap_backend.ENC_JSON_KEY in doc1.content) - self.assertTrue(leap_backend.ENC_SCHEME_KEY in doc1.content) + self.assertTrue(target.ENC_JSON_KEY in doc1.content) + self.assertTrue(target.ENC_SCHEME_KEY in doc1.content) # decrypt doc - doc1.set_json(leap_backend.decrypt_doc(self._soledad._crypto, doc1)) + doc1.set_json(target.decrypt_doc(self._soledad._crypto, doc1)) self.assertEqual( simpledoc, doc1.content, 'incorrect document encryption') @@ -161,36 +157,36 @@ class MacAuthTestCase(BaseSoledadTest): Trying to decrypt a document with wrong MAC should raise. """ simpledoc = {'key': 'val'} - doc = leap_backend.LeapDocument(doc_id='id') + doc = SoledadDocument(doc_id='id') doc.content = simpledoc # encrypt doc - doc.set_json(leap_backend.encrypt_doc(self._soledad._crypto, doc)) - self.assertTrue(leap_backend.MAC_KEY in doc.content) - self.assertTrue(leap_backend.MAC_METHOD_KEY in doc.content) + doc.set_json(target.encrypt_doc(self._soledad._crypto, doc)) + self.assertTrue(target.MAC_KEY in doc.content) + self.assertTrue(target.MAC_METHOD_KEY in doc.content) # mess with MAC - doc.content[leap_backend.MAC_KEY] = '1234567890ABCDEF' + doc.content[target.MAC_KEY] = '1234567890ABCDEF' # try to decrypt doc self.assertRaises( - leap_backend.WrongMac, - leap_backend.decrypt_doc, self._soledad._crypto, doc) + target.WrongMac, + target.decrypt_doc, self._soledad._crypto, doc) def test_decrypt_with_unknown_mac_method_raises(self): """ Trying to decrypt a document with unknown MAC method should raise. """ simpledoc = {'key': 'val'} - doc = leap_backend.LeapDocument(doc_id='id') + doc = SoledadDocument(doc_id='id') doc.content = simpledoc # encrypt doc - doc.set_json(leap_backend.encrypt_doc(self._soledad._crypto, doc)) - self.assertTrue(leap_backend.MAC_KEY in doc.content) - self.assertTrue(leap_backend.MAC_METHOD_KEY in doc.content) + doc.set_json(target.encrypt_doc(self._soledad._crypto, doc)) + self.assertTrue(target.MAC_KEY in doc.content) + self.assertTrue(target.MAC_METHOD_KEY in doc.content) # mess with MAC method - doc.content[leap_backend.MAC_METHOD_KEY] = 'mymac' + doc.content[target.MAC_METHOD_KEY] = 'mymac' # try to decrypt doc self.assertRaises( - leap_backend.UnknownMacMethod, - leap_backend.decrypt_doc, self._soledad._crypto, doc) + target.UnknownMacMethod, + target.decrypt_doc, self._soledad._crypto, doc) class SoledadCryptoTestCase(BaseSoledadTest): diff --git a/src/leap/soledad/tests/test_server.py b/soledad/src/leap/soledad/tests/test_server.py index 02276d87..490d2fc8 100644 --- a/src/leap/soledad/tests/test_server.py +++ b/soledad/src/leap/soledad/tests/test_server.py @@ -29,16 +29,16 @@ import mock from leap.soledad import Soledad -from leap.soledad.server import ( +from leap.soledad_server import ( SoledadApp, SoledadAuthMiddleware, URLToAuth, ) -from leap.soledad.backends.couch import ( +from leap.soledad_server.couch import ( CouchServerState, CouchDatabase, ) -from leap.soledad.backends import leap_backend +from leap.soledad import target from leap.common.testing.basetest import BaseLeapTest @@ -49,7 +49,7 @@ from leap.soledad.tests.u1db_tests import ( nested_doc, ) from leap.soledad.tests.test_couch import CouchDBTestCase -from leap.soledad.tests.test_leap_backend import ( +from leap.soledad.tests.test_target import ( make_token_soledad_app, make_leap_document_for_test, token_leap_sync_target, @@ -336,7 +336,7 @@ class EncryptedSyncTestCase( self.startServer() # instantiate soledad and create a document sol1 = self._soledad_instance( - # token is verified in test_leap_backend.make_token_soledad_app + # token is verified in test_target.make_token_soledad_app auth_token='auth-token' ) _, doclist = sol1.get_all_docs() @@ -358,12 +358,12 @@ class EncryptedSyncTestCase( self.assertEqual(doc1.doc_id, couchdoc.doc_id) self.assertEqual(doc1.rev, couchdoc.rev) self.assertEqual(6, len(couchdoc.content)) - self.assertTrue(leap_backend.ENC_JSON_KEY in couchdoc.content) - self.assertTrue(leap_backend.ENC_SCHEME_KEY in couchdoc.content) - self.assertTrue(leap_backend.ENC_METHOD_KEY in couchdoc.content) - self.assertTrue(leap_backend.ENC_IV_KEY in couchdoc.content) - self.assertTrue(leap_backend.MAC_KEY in couchdoc.content) - self.assertTrue(leap_backend.MAC_METHOD_KEY in couchdoc.content) + self.assertTrue(target.ENC_JSON_KEY in couchdoc.content) + self.assertTrue(target.ENC_SCHEME_KEY in couchdoc.content) + self.assertTrue(target.ENC_METHOD_KEY in couchdoc.content) + self.assertTrue(target.ENC_IV_KEY in couchdoc.content) + self.assertTrue(target.MAC_KEY in couchdoc.content) + self.assertTrue(target.MAC_METHOD_KEY in couchdoc.content) # instantiate soledad with empty db, but with same secrets path sol2 = self._soledad_instance(prefix='x', auth_token='auth-token') _, doclist = sol2.get_all_docs() diff --git a/src/leap/soledad/tests/test_soledad.py b/soledad/src/leap/soledad/tests/test_soledad.py index 8062a337..281b7a0f 100644 --- a/src/leap/soledad/tests/test_soledad.py +++ b/soledad/src/leap/soledad/tests/test_soledad.py @@ -36,12 +36,10 @@ from leap.soledad.tests import ( ) 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.backends.leap_backend import ( - LeapDocument, - LeapSyncTarget, -) +from leap.soledad.target import SoledadSyncTarget class AuxMethodsTestCase(BaseSoledadTest): @@ -63,7 +61,7 @@ class AuxMethodsTestCase(BaseSoledadTest): sol._gen_secret() sol._load_secrets() sol._init_db() - from leap.soledad.backends.sqlcipher import SQLCipherDatabase + from leap.soledad.sqlcipher import SQLCipherDatabase self.assertIsInstance(sol._db, SQLCipherDatabase) def test__init_config_defaults(self): @@ -117,7 +115,7 @@ class SoledadSharedDBTestCase(BaseSoledadTest): def setUp(self): BaseSoledadTest.setUp(self) self._shared_db = SoledadSharedDatabase( - 'https://provider/', LeapDocument, None) + 'https://provider/', SoledadDocument, None) def test__get_secrets_from_shared_db(self): """ @@ -284,7 +282,8 @@ class SoledadSignalingTestCase(BaseSoledadTest): soledad.signal.reset_mock() sol = self._soledad_instance() # mock the sync target - LeapSyncTarget.get_sync_info = Mock(return_value=[0, 0, 0, 0, 2]) + old_get_sync_info = SoledadSyncTarget.get_sync_info + SoledadSyncTarget.get_sync_info = Mock(return_value=[0, 0, 0, 0, 2]) # mock our generation so soledad thinks there's new data to sync sol._db._get_generation = Mock(return_value=1) # check for new data to sync @@ -294,3 +293,4 @@ class SoledadSignalingTestCase(BaseSoledadTest): proto.SOLEDAD_NEW_DATA_TO_SYNC, ADDRESS, ) + SoledadSyncTarget.get_sync_info = old_get_sync_info diff --git a/src/leap/soledad/tests/test_sqlcipher.py b/soledad/src/leap/soledad/tests/test_sqlcipher.py index 9741bd4e..25d04861 100644 --- a/src/leap/soledad/tests/test_sqlcipher.py +++ b/soledad/src/leap/soledad/tests/test_sqlcipher.py @@ -43,13 +43,13 @@ from u1db.backends.sqlite_backend import SQLitePartialExpandDatabase # soledad stuff. -from leap.soledad.backends.sqlcipher import ( +from leap.soledad.document import SoledadDocument +from leap.soledad.sqlcipher import ( SQLCipherDatabase, DatabaseIsNotEncrypted, + open as u1db_open, ) -from leap.soledad.backends.sqlcipher import open as u1db_open -from leap.soledad.backends.leap_backend import ( - LeapDocument, +from leap.soledad.target import ( EncryptionSchemes, decrypt_doc, ENC_JSON_KEY, @@ -63,7 +63,7 @@ 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.backends.leap_backend import LeapSyncTarget +from leap.soledad.target import SoledadSyncTarget from leap.common.testing.basetest import BaseLeapTest PASSWORD = '123456' @@ -115,7 +115,7 @@ def copy_sqlcipher_database_for_test(test, db): def make_document_for_test(test, doc_id, rev, content, has_conflicts=False): - return LeapDocument(doc_id, rev, content, has_conflicts=has_conflicts) + return SoledadDocument(doc_id, rev, content, has_conflicts=has_conflicts) SQLCIPHER_SCENARIOS = [ @@ -205,7 +205,7 @@ class TestSQLCipherDatabase(test_sqlite_backend.TestSQLiteDatabase): self.assertTrue(db2._is_initialized(db1._get_sqlite_handle().cursor())) -class TestAlternativeDocument(LeapDocument): +class TestAlternativeDocument(SoledadDocument): """A (not very) alternative implementation of Document.""" @@ -272,7 +272,7 @@ class TestSQLCipherPartialExpandDatabase( path, PASSWORD, document_factory=TestAlternativeDocument) doc = db2.create_doc({}) - self.assertTrue(isinstance(doc, LeapDocument)) + self.assertTrue(isinstance(doc, SoledadDocument)) def test__open_database_non_existent(self): temp_dir = self.createTempDir(prefix='u1db-test-') @@ -341,7 +341,7 @@ class TestSQLCipherPartialExpandDatabase( path, PASSWORD, create=False, document_factory=TestAlternativeDocument) doc = db2.create_doc({}) - self.assertTrue(isinstance(doc, LeapDocument)) + self.assertTrue(isinstance(doc, SoledadDocument)) def test_open_database_create(self): temp_dir = self.createTempDir(prefix='u1db-test-') @@ -401,7 +401,7 @@ class SQLCipherOpen(test_open.TestU1DBOpen): document_factory=TestAlternativeDocument) self.addCleanup(db.close) doc = db.create_doc({}) - self.assertTrue(isinstance(doc, LeapDocument)) + self.assertTrue(isinstance(doc, SoledadDocument)) def test_open_existing(self): db = SQLCipherDatabase(self.db_path, PASSWORD) @@ -438,7 +438,7 @@ 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 = LeapSyncTarget.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) @@ -662,7 +662,7 @@ class SQLCipherDatabaseSyncTests( def _make_local_db_and_leap_target(test, path='test'): test.startServer() db = test.request_state._create_database(os.path.basename(path)) - st = LeapSyncTarget.connect(test.getURL(path), test._soledad._crypto) + st = SoledadSyncTarget.connect(test.getURL(path), test._soledad._crypto) st.set_token_credentials('user-uuid', 'auth-token') return db, st @@ -791,7 +791,7 @@ class SQLCipherEncryptionTest(BaseLeapTest): # trying to open an encrypted database with the regular u1db # backend should raise a DatabaseError exception. SQLitePartialExpandDatabase(self.DB_FILE, - document_factory=LeapDocument) + document_factory=SoledadDocument) raise DatabaseIsNotEncrypted() except dbapi2.DatabaseError: # at this point we know that the regular U1DB sqlcipher backend @@ -807,7 +807,7 @@ class SQLCipherEncryptionTest(BaseLeapTest): SQLCipher backend should not succeed to open unencrypted databases. """ db = SQLitePartialExpandDatabase(self.DB_FILE, - document_factory=LeapDocument) + document_factory=SoledadDocument) db.create_doc_from_json(tests.simple_doc) db.close() try: diff --git a/src/leap/soledad/tests/test_leap_backend.py b/soledad/src/leap/soledad/tests/test_target.py index 6914e869..73c9fe68 100644 --- a/src/leap/soledad/tests/test_leap_backend.py +++ b/soledad/src/leap/soledad/tests/test_target.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# test_leap_backend.py +# test_target.py # Copyright (C) 2013 LEAP # # This program is free software: you can redistribute it and/or modify @@ -33,15 +33,17 @@ from u1db.remote import ( http_database, http_target, ) -from routes.mapper import Mapper from leap import soledad -from leap.soledad.backends import leap_backend -from leap.soledad.server import ( +from leap.soledad import ( + target, + auth, +) +from leap.soledad.document import SoledadDocument +from leap.soledad_server import ( SoledadApp, SoledadAuthMiddleware, ) -from leap.soledad import auth from leap.soledad.tests import u1db_tests as tests @@ -61,7 +63,7 @@ from leap.soledad.tests.u1db_tests import test_sync def make_leap_document_for_test(test, doc_id, rev, content, has_conflicts=False): - return leap_backend.LeapDocument( + return SoledadDocument( doc_id, rev, content, has_conflicts=has_conflicts) @@ -126,7 +128,7 @@ def copy_token_http_database_for_test(test, db): return http_db -class LeapTests(test_backends.AllDatabaseTests, BaseSoledadTest): +class SoledadTests(test_backends.AllDatabaseTests, BaseSoledadTest): scenarios = LEAP_SCENARIOS + [ ('token_http', {'make_database_for_test': @@ -143,7 +145,7 @@ class LeapTests(test_backends.AllDatabaseTests, BaseSoledadTest): # The following tests come from `u1db.tests.test_http_client`. #----------------------------------------------------------------------------- -class TestLeapClientBase(test_http_client.TestHTTPClientBase): +class TestSoledadClientBase(test_http_client.TestHTTPClientBase): """ This class should be used to test Token auth. """ @@ -230,13 +232,13 @@ class TestLeapClientBase(test_http_client.TestHTTPClientBase): # The following tests come from `u1db.tests.test_document`. #----------------------------------------------------------------------------- -class TestLeapDocument(test_document.TestDocument, BaseSoledadTest): +class TestSoledadDocument(test_document.TestDocument, BaseSoledadTest): scenarios = ([( 'leap', {'make_document_for_test': make_leap_document_for_test})]) -class TestLeapPyDocument(test_document.TestPyDocument, BaseSoledadTest): +class TestSoledadPyDocument(test_document.TestPyDocument, BaseSoledadTest): scenarios = ([( 'leap', {'make_document_for_test': make_leap_document_for_test})]) @@ -246,7 +248,7 @@ class TestLeapPyDocument(test_document.TestPyDocument, BaseSoledadTest): # The following tests come from `u1db.tests.test_remote_sync_target`. #----------------------------------------------------------------------------- -class TestLeapSyncTargetBasics( +class TestSoledadSyncTargetBasics( test_remote_sync_target.TestHTTPSyncTargetBasics): """ Some tests had to be copied to this class so we can instantiate our own @@ -254,14 +256,14 @@ class TestLeapSyncTargetBasics( """ def test_parse_url(self): - remote_target = leap_backend.LeapSyncTarget('http://127.0.0.1:12345/') + remote_target = target.SoledadSyncTarget('http://127.0.0.1:12345/') self.assertEqual('http', remote_target._url.scheme) self.assertEqual('127.0.0.1', remote_target._url.hostname) self.assertEqual(12345, remote_target._url.port) self.assertEqual('/', remote_target._url.path) -class TestLeapParsingSyncStream( +class TestSoledadParsingSyncStream( test_remote_sync_target.TestParsingSyncStream, BaseSoledadTest): """ @@ -281,10 +283,10 @@ class TestLeapParsingSyncStream( """ Test adapted to use encrypted content. """ - doc = leap_backend.LeapDocument('i', rev='r') + doc = SoledadDocument('i', rev='r') doc.content = {} - enc_json = leap_backend.encrypt_doc(self._soledad._crypto, doc) - tgt = leap_backend.LeapSyncTarget( + enc_json = target.encrypt_doc(self._soledad._crypto, doc) + tgt = target.SoledadSyncTarget( "http://foo/foo", crypto=self._soledad._crypto) self.assertRaises(u1db.errors.BrokenSyncStream, @@ -297,7 +299,7 @@ class TestLeapParsingSyncStream( lambda doc, gen, trans_id: None) def test_wrong_start(self): - tgt = leap_backend.LeapSyncTarget("http://foo/foo") + tgt = target.SoledadSyncTarget("http://foo/foo") self.assertRaises(u1db.errors.BrokenSyncStream, tgt._parse_sync_stream, "{}\r\n]", None) @@ -309,7 +311,7 @@ class TestLeapParsingSyncStream( tgt._parse_sync_stream, "", None) def test_wrong_end(self): - tgt = leap_backend.LeapSyncTarget("http://foo/foo") + tgt = target.SoledadSyncTarget("http://foo/foo") self.assertRaises(u1db.errors.BrokenSyncStream, tgt._parse_sync_stream, "[\r\n{}", None) @@ -318,7 +320,7 @@ class TestLeapParsingSyncStream( tgt._parse_sync_stream, "[\r\n", None) def test_missing_comma(self): - tgt = leap_backend.LeapSyncTarget("http://foo/foo") + tgt = target.SoledadSyncTarget("http://foo/foo") self.assertRaises(u1db.errors.BrokenSyncStream, tgt._parse_sync_stream, @@ -326,13 +328,13 @@ class TestLeapParsingSyncStream( '"content": "c", "gen": 3}\r\n]', None) def test_no_entries(self): - tgt = leap_backend.LeapSyncTarget("http://foo/foo") + tgt = target.SoledadSyncTarget("http://foo/foo") self.assertRaises(u1db.errors.BrokenSyncStream, tgt._parse_sync_stream, "[\r\n]", None) def test_error_in_stream(self): - tgt = leap_backend.LeapSyncTarget("http://foo/foo") + tgt = target.SoledadSyncTarget("http://foo/foo") self.assertRaises(u1db.errors.Unavailable, tgt._parse_sync_stream, @@ -353,7 +355,7 @@ class TestLeapParsingSyncStream( # def leap_sync_target(test, path): - return leap_backend.LeapSyncTarget( + return target.SoledadSyncTarget( test.getURL(path), crypto=test._soledad._crypto) @@ -363,7 +365,7 @@ def token_leap_sync_target(test, path): return st -class TestLeapSyncTarget( +class TestSoledadSyncTarget( test_remote_sync_target.TestRemoteSyncTargets, BaseSoledadTest): scenarios = [ @@ -497,14 +499,14 @@ class TestLeapSyncTarget( def token_leap_https_sync_target(test, host, path): _, port = test.server.server_address - st = leap_backend.LeapSyncTarget( + st = target.SoledadSyncTarget( 'https://%s:%d/%s' % (host, port, path), crypto=test._soledad._crypto) st.set_token_credentials('user-uuid', 'auth-token') return st -class TestLeapSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSupport, +class TestSoledadSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSupport, BaseSoledadTest): scenarios = [ @@ -598,7 +600,7 @@ class TestHTTPDatabaseWithCreds( def _make_local_db_and_leap_target(test, path='test'): test.startServer() db = test.request_state._create_database(os.path.basename(path)) - st = leap_backend.LeapSyncTarget.connect( + st = target.SoledadSyncTarget.connect( test.getURL(path), crypto=test._soledad._crypto) return db, st @@ -616,7 +618,7 @@ target_scenarios = [ ] -class LeapDatabaseSyncTargetTests( +class SoledadDatabaseSyncTargetTests( test_sync.DatabaseSyncTargetTests, BaseSoledadTest): scenarios = ( @@ -702,7 +704,7 @@ class LeapDatabaseSyncTargetTests( [(doc.doc_id, doc.rev), (doc2.doc_id, doc2.rev)]}) -class TestLeapDbSync(test_sync.TestDbSync, BaseSoledadTest): +class TestSoledadDbSync(test_sync.TestDbSync, BaseSoledadTest): """Test db.sync remote sync shortcut""" scenarios = [ @@ -722,7 +724,7 @@ class TestLeapDbSync(test_sync.TestDbSync, BaseSoledadTest): def do_sync(self, target_name): """ - Perform sync using LeapSyncTarget and Token auth. + Perform sync using SoledadSyncTarget and Token auth. """ if self.token: extra = dict(creds={'token': { @@ -732,7 +734,7 @@ class TestLeapDbSync(test_sync.TestDbSync, BaseSoledadTest): target_url = self.getURL(target_name) return Synchronizer( self.db, - leap_backend.LeapSyncTarget( + target.SoledadSyncTarget( target_url, crypto=self._soledad._crypto, **extra)).sync(autocreate=True) diff --git a/src/leap/soledad/tests/u1db_tests/README b/soledad/src/leap/soledad/tests/u1db_tests/README index 605f01fa..605f01fa 100644 --- a/src/leap/soledad/tests/u1db_tests/README +++ b/soledad/src/leap/soledad/tests/u1db_tests/README diff --git a/src/leap/soledad/tests/u1db_tests/__init__.py b/soledad/src/leap/soledad/tests/u1db_tests/__init__.py index 43304b43..43304b43 100644 --- a/src/leap/soledad/tests/u1db_tests/__init__.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/__init__.py diff --git a/src/leap/soledad/tests/u1db_tests/test_backends.py b/soledad/src/leap/soledad/tests/u1db_tests/test_backends.py index a53b01ba..a53b01ba 100644 --- a/src/leap/soledad/tests/u1db_tests/test_backends.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_backends.py diff --git a/src/leap/soledad/tests/u1db_tests/test_document.py b/soledad/src/leap/soledad/tests/u1db_tests/test_document.py index e706e1a9..e706e1a9 100644 --- a/src/leap/soledad/tests/u1db_tests/test_document.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_document.py diff --git a/src/leap/soledad/tests/u1db_tests/test_http_app.py b/soledad/src/leap/soledad/tests/u1db_tests/test_http_app.py index e0729aa2..e0729aa2 100644 --- a/src/leap/soledad/tests/u1db_tests/test_http_app.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_http_app.py diff --git a/src/leap/soledad/tests/u1db_tests/test_http_client.py b/soledad/src/leap/soledad/tests/u1db_tests/test_http_client.py index 42e98461..42e98461 100644 --- a/src/leap/soledad/tests/u1db_tests/test_http_client.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_http_client.py diff --git a/src/leap/soledad/tests/u1db_tests/test_http_database.py b/soledad/src/leap/soledad/tests/u1db_tests/test_http_database.py index f21e6da1..f21e6da1 100644 --- a/src/leap/soledad/tests/u1db_tests/test_http_database.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_http_database.py diff --git a/src/leap/soledad/tests/u1db_tests/test_https.py b/soledad/src/leap/soledad/tests/u1db_tests/test_https.py index 62180f8c..62180f8c 100644 --- a/src/leap/soledad/tests/u1db_tests/test_https.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_https.py diff --git a/src/leap/soledad/tests/u1db_tests/test_open.py b/soledad/src/leap/soledad/tests/u1db_tests/test_open.py index 0ff307e8..0ff307e8 100644 --- a/src/leap/soledad/tests/u1db_tests/test_open.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_open.py diff --git a/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py b/soledad/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py index 66d404d2..66d404d2 100644 --- a/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_remote_sync_target.py diff --git a/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py b/soledad/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py index 1380e4b1..1380e4b1 100644 --- a/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_sqlite_backend.py diff --git a/src/leap/soledad/tests/u1db_tests/test_sync.py b/soledad/src/leap/soledad/tests/u1db_tests/test_sync.py index 96aa2736..96aa2736 100644 --- a/src/leap/soledad/tests/u1db_tests/test_sync.py +++ b/soledad/src/leap/soledad/tests/u1db_tests/test_sync.py diff --git a/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile index 2385e75b..2385e75b 100644 --- a/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile +++ b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/Makefile diff --git a/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem index c019a730..c019a730 100644 --- a/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem +++ b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/cacert.pem diff --git a/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert index 985684fb..985684fb 100644 --- a/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert +++ b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.cert diff --git a/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key index d83d4920..d83d4920 100644 --- a/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key +++ b/soledad/src/leap/soledad/tests/u1db_tests/testing-certs/testing.key diff --git a/pkg/soledad b/soledad_server/pkg/soledad index c640a94d..c640a94d 100644 --- a/pkg/soledad +++ b/soledad_server/pkg/soledad diff --git a/soledad_server/setup.py b/soledad_server/setup.py new file mode 100644 index 00000000..522c86ff --- /dev/null +++ b/soledad_server/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/>. + + +import os +from setuptools import ( + setup, + find_packages +) + + +install_requirements = [ + 'configparser', + 'couchdb', + 'simplejson', + 'twisted>=12.0.0', # TODO: maybe we just want twisted-web? + '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', +] + + +if os.environ.get('VIRTUAL_ENV', None): + data_files = None +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", + "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_server', + version='0.1.1', + 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"], + packages=find_packages('src'), + package_dir={'': 'src'}, + install_requires=install_requirements, + data_files=data_files, + classifiers=trove_classifiers, +) diff --git a/soledad_server/src/leap/__init__.py b/soledad_server/src/leap/__init__.py new file mode 100644 index 00000000..f48ad105 --- /dev/null +++ b/soledad_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/src/leap/soledad/server.py b/soledad_server/src/leap/soledad_server/__init__.py index 9c9e0ad7..bea5d5fd 100644 --- a/src/leap/soledad/server.py +++ b/soledad_server/src/leap/soledad_server/__init__.py @@ -20,7 +20,7 @@ A U1DB server that stores data using CouchDB as its persistence layer. This should be run with: - twistd -n web --wsgi=leap.soledad.server.application --port=2424 + twistd -n web --wsgi=leap.soledad_server.application --port=2424 """ import configparser @@ -53,7 +53,7 @@ if version.base() == "12.0.0": from couchdb.client import Server from leap.soledad import SECRETS_DOC_ID_HASH_PREFIX -from leap.soledad.backends.couch import CouchServerState +from leap.soledad_server.couch import CouchServerState #----------------------------------------------------------------------------- @@ -126,7 +126,7 @@ class URLToAuth(object): @return: The database name corresponding to C{uuid}. @rtype: str """ - return sha256('%s%s' % (SECRETS_DOC_ID_HASH_PREFIX, uuid)).hexdigest() + return '%s%s' % (SoledadApp.USER_DB_PREFIX, uuid) def _register_auth_info(self, dbname): """ @@ -329,6 +329,11 @@ class SoledadApp(http_app.HTTPApp): The name of the shared database that holds user's encrypted secrets. """ + USER_DB_PREFIX = 'uuid-' + """ + The string prefix of users' databases. + """ + def __call__(self, environ, start_response): """ Handle a WSGI call to the Soledad application. diff --git a/src/leap/soledad/backends/couch.py b/soledad_server/src/leap/soledad_server/couch.py index 57885012..ed5ad6b3 100644 --- a/src/leap/soledad/backends/couch.py +++ b/soledad_server/src/leap/soledad_server/couch.py @@ -34,11 +34,10 @@ from couchdb.client import Server, Document as CouchDocument from couchdb.http import ResourceNotFound -from leap.soledad.backends.objectstore import ( +from leap.soledad_server.objectstore import ( ObjectStoreDatabase, ObjectStoreSyncTarget, ) -from leap.soledad.backends.leap_backend import LeapDocument class InvalidURLError(Exception): @@ -120,10 +119,7 @@ class CouchDatabase(ObjectStoreDatabase): except ResourceNotFound: self._server.create(self._dbname) self._database = self._server[self._dbname] - ObjectStoreDatabase.__init__(self, replica_uid=replica_uid, - # TODO: move the factory choice - # away - document_factory=LeapDocument) + ObjectStoreDatabase.__init__(self, replica_uid=replica_uid) #------------------------------------------------------------------------- # methods from Database diff --git a/src/leap/soledad/backends/objectstore.py b/soledad_server/src/leap/soledad_server/objectstore.py index 8afac3ec..8afac3ec 100644 --- a/src/leap/soledad/backends/objectstore.py +++ b/soledad_server/src/leap/soledad_server/objectstore.py diff --git a/src/leap/soledad/backends/__init__.py b/src/leap/soledad/backends/__init__.py deleted file mode 100644 index 720a8118..00000000 --- a/src/leap/soledad/backends/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- 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/>. - - -""" -Backends that extend U1DB functionality. -""" - -from leap.soledad.backends import ( - objectstore, - couch, - sqlcipher, - leap_backend, -) - - -__all__ = [ - 'objectstore', - 'couch', - 'sqlcipher', - 'leap_backend', -] |