summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--client/changes/VERSION_COMPAT2
-rw-r--r--client/changes/bug_fix-sync-enc-close-queue-error1
-rw-r--r--client/changes/feature_6996-post-sync-hooks1
-rw-r--r--client/pkg/requirements.pip2
-rw-r--r--client/src/leap/soledad/client/adbapi.py3
-rw-r--r--client/src/leap/soledad/client/api.py27
-rw-r--r--client/src/leap/soledad/client/encdecpool.py42
-rw-r--r--client/src/leap/soledad/client/http_target.py7
-rw-r--r--client/src/leap/soledad/client/interfaces.py31
-rw-r--r--client/src/leap/soledad/client/sqlcipher.py13
-rw-r--r--client/src/leap/soledad/client/sync.py10
-rw-r--r--common/pkg/requirements-testing.pip6
-rw-r--r--common/pkg/requirements.pip2
-rw-r--r--common/src/leap/soledad/common/couch.py6
-rw-r--r--common/src/leap/soledad/common/tests/test_https.py66
-rw-r--r--common/src/leap/soledad/common/tests/test_server.py46
-rw-r--r--common/src/leap/soledad/common/tests/u1db_tests/test_https.py4
-rw-r--r--common/src/leap/soledad/common/tests/util.py7
-rw-r--r--server/pkg/requirements.pip2
20 files changed, 210 insertions, 69 deletions
diff --git a/.gitignore b/.gitignore
index c502541e..6a0003cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+dropin.cache
*.log
*.pyc
dist/
diff --git a/client/changes/VERSION_COMPAT b/client/changes/VERSION_COMPAT
index cc00ecf7..a24751f9 100644
--- a/client/changes/VERSION_COMPAT
+++ b/client/changes/VERSION_COMPAT
@@ -8,3 +8,5 @@
#
# BEGIN DEPENDENCY LIST -------------------------
# leap.foo.bar>=x.y.z
+#
+leap.common >= 0.4.1 # collect_plugins
diff --git a/client/changes/bug_fix-sync-enc-close-queue-error b/client/changes/bug_fix-sync-enc-close-queue-error
new file mode 100644
index 00000000..71af7c67
--- /dev/null
+++ b/client/changes/bug_fix-sync-enc-close-queue-error
@@ -0,0 +1 @@
+ o Fix sync encrypter pool close queue error. Closes #7088.
diff --git a/client/changes/feature_6996-post-sync-hooks b/client/changes/feature_6996-post-sync-hooks
new file mode 100644
index 00000000..027c0b2a
--- /dev/null
+++ b/client/changes/feature_6996-post-sync-hooks
@@ -0,0 +1 @@
+o Expose post-sync hooks via plugin system. Related: #6996
diff --git a/client/pkg/requirements.pip b/client/pkg/requirements.pip
index 9fffdbe3..26f7c979 100644
--- a/client/pkg/requirements.pip
+++ b/client/pkg/requirements.pip
@@ -8,7 +8,7 @@ zope.proxy
twisted
# leap deps -- bump me!
-leap.common>=0.4
+leap.common>=0.4.0
leap.soledad.common>=0.6.5
# XXX -- fix me!
diff --git a/client/src/leap/soledad/client/adbapi.py b/client/src/leap/soledad/client/adbapi.py
index 5b882bbe..bc0ab7a5 100644
--- a/client/src/leap/soledad/client/adbapi.py
+++ b/client/src/leap/soledad/client/adbapi.py
@@ -217,6 +217,9 @@ class U1DBConnectionPool(adbapi.ConnectionPool):
"""
meth = getattr(trans, meth)
return meth(*args, **kw)
+ # XXX should return a fetchall?
+
+ # XXX add _runOperation too
def _runInteraction(self, interaction, *args, **kw):
"""
diff --git a/client/src/leap/soledad/client/api.py b/client/src/leap/soledad/client/api.py
index 76d6acc3..6c2b3673 100644
--- a/client/src/leap/soledad/client/api.py
+++ b/client/src/leap/soledad/client/api.py
@@ -38,13 +38,16 @@ try:
import cchardet as chardet
except ImportError:
import chardet
+from itertools import chain
from StringIO import StringIO
from u1db.remote import http_client
from u1db.remote.ssl_match_hostname import match_hostname
+from twisted.plugin import getPlugins
from zope.interface import implements
from leap.common.config import get_path_prefix
+from leap.common.plugins import collect_plugins
from leap.soledad.common import SHARED_DB_NAME
from leap.soledad.common import soledad_assert
@@ -656,6 +659,20 @@ class Soledad(object):
defer_decryption=defer_decryption)
def _sync_callback(local_gen):
+ self._last_received_docs = docs = self._dbsyncer.received_docs
+
+ # Post-Sync Hooks
+ if docs:
+ iface = soledad_interfaces.ISoledadPostSyncPlugin
+ suitable_plugins = collect_plugins(iface)
+ for plugin in suitable_plugins:
+ watched = plugin.watched_doc_types
+ r = [filter(
+ lambda s: s.startswith(preffix),
+ docs) for preffix in watched]
+ filtered = list(chain(*r))
+ plugin.process_received_docs(filtered)
+
soledad_events.emit(
soledad_events.SOLEDAD_DONE_DATA_SYNC, self.uuid)
return local_gen
@@ -788,10 +805,18 @@ class Soledad(object):
def raw_sqlcipher_query(self, *args, **kw):
"""
- Run a raw sqlcipher query in the local database.
+ Run a raw sqlcipher query in the local database, and return a deferred
+ that will be fired with the result.
"""
return self._dbpool.runQuery(*args, **kw)
+ def raw_sqlcipher_operation(self, *args, **kw):
+ """
+ Run a raw sqlcipher operation in the local database, and return a
+ deferred that will be fired with None.
+ """
+ return self._dbpool.runOperation(*args, **kw)
+
def _convert_to_unicode(content):
"""
diff --git a/client/src/leap/soledad/client/encdecpool.py b/client/src/leap/soledad/client/encdecpool.py
index d9a72b25..f81cd2d1 100644
--- a/client/src/leap/soledad/client/encdecpool.py
+++ b/client/src/leap/soledad/client/encdecpool.py
@@ -29,7 +29,7 @@ import logging
from twisted.internet import reactor
from twisted.internet import defer
-from twisted.internet.threads import deferToThread
+from twisted.python import log
from leap.soledad.common.document import SoledadDocument
from leap.soledad.common import soledad_assert
@@ -147,7 +147,7 @@ class SyncEncrypterPool(SyncEncryptDecryptPool):
TABLE_NAME = "docs_tosync"
FIELD_NAMES = "doc_id PRIMARY KEY, rev, content"
- ENCRYPT_LOOP_PERIOD = 0.5
+ ENCRYPT_LOOP_PERIOD = 2
def __init__(self, *args, **kwargs):
"""
@@ -159,9 +159,8 @@ class SyncEncrypterPool(SyncEncryptDecryptPool):
self._sync_queue = multiprocessing.Queue()
# start the encryption loop
- self._deferred_loop = deferToThread(self._encrypt_docs_loop)
- self._deferred_loop.addCallback(
- lambda _: logger.debug("Finished encrypter thread."))
+ logger.debug("Starting the encryption loop...")
+ reactor.callWhenRunning(self._maybe_encrypt_and_recurse)
def enqueue_doc_for_encryption(self, doc):
"""
@@ -171,24 +170,28 @@ class SyncEncrypterPool(SyncEncryptDecryptPool):
:type doc: SoledadDocument
"""
try:
- self.sync_queue.put_nowait(doc)
- except multiprocessing.Queue.Full:
+ self._sync_queue.put_nowait(doc)
+ except Queue.Full:
# do not asynchronously encrypt this file if the queue is full
pass
- def _encrypt_docs_loop(self):
+ def _maybe_encrypt_and_recurse(self):
"""
Process the syncing queue and send the documents there to be encrypted
in the sync db. They will be read by the SoledadSyncTarget during the
sync_exchange.
"""
- logger.debug("Starting encrypter thread.")
- while not self._stopped:
+ if not self._stopped:
try:
- doc = self._sync_queue.get(True, self.ENCRYPT_LOOP_PERIOD)
+ doc = self._sync_queue.get(False)
self._encrypt_doc(doc)
except Queue.Empty:
pass
+ reactor.callLater(
+ self.ENCRYPT_LOOP_PERIOD,
+ self._maybe_encrypt_and_recurse)
+ else:
+ logger.debug("Finished encrypter thread.")
def _encrypt_doc(self, doc):
"""
@@ -374,9 +377,9 @@ class SyncDecrypterPool(SyncEncryptDecryptPool):
self.source_replica_uid = kwargs.pop("source_replica_uid")
SyncEncryptDecryptPool.__init__(self, *args, **kwargs)
- self._last_inserted_idx = 0
self._docs_to_process = None
self._processed_docs = 0
+ self._last_inserted_idx = 0
self._async_results = []
self._failure = None
@@ -386,6 +389,7 @@ class SyncDecrypterPool(SyncEncryptDecryptPool):
# asynchronous call, so we have to somehow make sure that it is
# executed before any other call to the database, without
# blocking.
+ # XXX in mail and keymanager we have a pattern for that -- kali.
self._empty()
def _launch_decrypt_and_process(self):
@@ -402,6 +406,7 @@ class SyncDecrypterPool(SyncEncryptDecryptPool):
return self._failure
def _set_failure(self, failure):
+ log.err(failure)
self._failure = failure
self._finished = True
@@ -419,6 +424,7 @@ class SyncDecrypterPool(SyncEncryptDecryptPool):
:type docs_to_process: int
"""
self._docs_to_process = docs_to_process
+ self._finished = False
self._schedule_decrypt_and_process()
def insert_encrypted_received_doc(
@@ -729,7 +735,10 @@ class SyncDecrypterPool(SyncEncryptDecryptPool):
:rtype: twisted.internet.defer.Deferred
"""
if not self.failed():
- if self._processed_docs < self._docs_to_process:
+ processed = self._processed_docs
+ pending = self._docs_to_process
+
+ if not self.has_finished() and processed < pending:
yield self._async_decrypt_received_docs()
yield self._collect_async_decryption_results()
docs = yield self._process_decrypted_docs()
@@ -737,7 +746,12 @@ class SyncDecrypterPool(SyncEncryptDecryptPool):
# recurse
self._schedule_decrypt_and_process()
else:
- self._finished = True
+ self._mark_finished()
+
+ def _mark_finished(self):
+ self._finished = True
+ self._processed_docs = 0
+ self._last_inserted_idx = 0
def has_finished(self):
"""
diff --git a/client/src/leap/soledad/client/http_target.py b/client/src/leap/soledad/client/http_target.py
index 30590ae1..ac078f39 100644
--- a/client/src/leap/soledad/client/http_target.py
+++ b/client/src/leap/soledad/client/http_target.py
@@ -75,8 +75,6 @@ class SoledadHTTPSyncTarget(SyncTarget):
:param source_replica_uid: The source replica uid which we use when
deferring decryption.
:type source_replica_uid: str
- :param url: The url of the target replica to sync with.
- :type url: str
:param creds: A dictionary containing the uuid and token.
:type creds: creds
:param crypto: An instance of SoledadCrypto so we can encrypt/decrypt
@@ -98,7 +96,7 @@ class SoledadHTTPSyncTarget(SyncTarget):
"""
if url.endswith("/"):
url = url[:-1]
- self._url = str(url) + "/sync-from/" + source_replica_uid
+ self._url = str(url) + "/sync-from/" + str(source_replica_uid)
self.source_replica_uid = source_replica_uid
self._auth_header = None
self.set_creds(creds)
@@ -111,6 +109,9 @@ class SoledadHTTPSyncTarget(SyncTarget):
self._sync_decr_pool = None
self._http = HTTPClient(cert_file)
+ def close(self):
+ self._http.close()
+
def set_creds(self, creds):
"""
Update credentials.
diff --git a/client/src/leap/soledad/client/interfaces.py b/client/src/leap/soledad/client/interfaces.py
index 4f7b0779..14b34d24 100644
--- a/client/src/leap/soledad/client/interfaces.py
+++ b/client/src/leap/soledad/client/interfaces.py
@@ -19,6 +19,37 @@ Interfaces used by the Soledad Client.
"""
from zope.interface import Interface, Attribute
+#
+# Plugins
+#
+
+
+class ISoledadPostSyncPlugin(Interface):
+ """
+ I implement the minimal methods and attributes for a plugin that can be
+ called after a soledad synchronization has ended.
+ """
+
+ def process_received_docs(self, doc_id_list):
+ """
+ Do something with the passed list of doc_ids received after the last
+ sync.
+
+ :param doc_id_list: a list of strings for the received doc_ids
+ """
+
+ watched_doc_types = Attribute("""
+ a tuple of the watched doc types for this plugin. So far, the
+ `doc-types` convention is just the preffix of the doc_id, which is
+ basically its first character, followed by a dash. So, for instance,
+ `M-` is used for meta-docs in mail, and `F-` is used for flag-docs in
+ mail. For now there's no central register of all the doc-types
+ used.""")
+
+
+#
+# Soledad storage
+#
class ILocalStorage(Interface):
"""
diff --git a/client/src/leap/soledad/client/sqlcipher.py b/client/src/leap/soledad/client/sqlcipher.py
index b2025130..75d786a6 100644
--- a/client/src/leap/soledad/client/sqlcipher.py
+++ b/client/src/leap/soledad/client/sqlcipher.py
@@ -456,6 +456,9 @@ class SQLCipherU1DBSync(SQLCipherDatabase):
self._syncers = {}
+ # Storage for the documents received during a sync
+ self.received_docs = []
+
self.running = False
self._sync_threadpool = None
self._initialize_sync_threadpool()
@@ -587,8 +590,16 @@ class SQLCipherU1DBSync(SQLCipherDatabase):
# the following context manager blocks until the syncing lock can be
# acquired.
with self._syncer(url, creds=creds) as syncer:
+
+ def _record_received_docs(result):
+ # beware, closure. syncer is in scope.
+ self.received_docs = syncer.received_docs
+ return result
+
# XXX could mark the critical section here...
- return syncer.sync(defer_decryption=defer_decryption)
+ d = syncer.sync(defer_decryption=defer_decryption)
+ d.addCallback(_record_received_docs)
+ return d
@contextmanager
def _syncer(self, url, creds=None):
diff --git a/client/src/leap/soledad/client/sync.py b/client/src/leap/soledad/client/sync.py
index 53172f31..14c547cf 100644
--- a/client/src/leap/soledad/client/sync.py
+++ b/client/src/leap/soledad/client/sync.py
@@ -39,6 +39,7 @@ class SoledadSynchronizer(Synchronizer):
Also modified to allow for interrupting the synchronization process.
"""
+ received_docs = []
@defer.inlineCallbacks
def sync(self, defer_decryption=True):
@@ -62,6 +63,7 @@ class SoledadSynchronizer(Synchronizer):
:rtype: twisted.internet.defer.Deferred
"""
sync_target = self.sync_target
+ self.received_docs = []
# get target identifier, its current generation,
# and its last-seen database generation for this source
@@ -123,12 +125,14 @@ class SoledadSynchronizer(Synchronizer):
changed_doc_ids = [doc_id for doc_id, _, _ in changes]
docs_to_send = self.source.get_docs(
changed_doc_ids, check_for_conflicts=False, include_deleted=True)
+ ids_sent = []
docs_by_generation = []
idx = 0
for doc in docs_to_send:
_, gen, trans = changes[idx]
docs_by_generation.append((doc, gen, trans))
idx += 1
+ ids_sent.append(doc.doc_id)
# exchange documents and try to insert the returned ones with
# the target, return target synced-up-to gen.
@@ -151,6 +155,12 @@ class SoledadSynchronizer(Synchronizer):
self._syncing_info = info
yield self.complete_sync()
+ _, _, changes = self.source.whats_changed(target_my_gen)
+ changed_doc_ids = [doc_id for doc_id, _, _ in changes]
+
+ just_received = list(set(changed_doc_ids) - set(ids_sent))
+ self.received_docs = just_received
+
defer.returnValue(my_gen)
def complete_sync(self):
diff --git a/common/pkg/requirements-testing.pip b/common/pkg/requirements-testing.pip
index c72c9fc4..e23135ac 100644
--- a/common/pkg/requirements-testing.pip
+++ b/common/pkg/requirements-testing.pip
@@ -1,6 +1,6 @@
mock
testscenarios
-leap.common
-leap.soledad.server
-leap.soledad.client
+leap.common>=0.4.0
+leap.soledad.server>=0.7.0
+leap.soledad.client>=0.7.0
setuptools-trial
diff --git a/common/pkg/requirements.pip b/common/pkg/requirements.pip
index 005d6884..b91186e7 100644
--- a/common/pkg/requirements.pip
+++ b/common/pkg/requirements.pip
@@ -2,7 +2,7 @@ simplejson
u1db
# leap deps -- bump me!
-leap.common>=0.7.0
+leap.common>=0.4.0
# XXX -- fix me!
# oauth is not strictly needed by us, but we need it until u1db adds it to its
diff --git a/common/src/leap/soledad/common/couch.py b/common/src/leap/soledad/common/couch.py
index 8d262ccd..ae9e7d2a 100644
--- a/common/src/leap/soledad/common/couch.py
+++ b/common/src/leap/soledad/common/couch.py
@@ -240,8 +240,12 @@ def raise_server_error(exc, ddoc_path):
document for an yet unknown reason.
"""
path = "".join(ddoc_path)
- if exc.message[1][0] == 'unnamed_error':
+ msg = exc.message[1][0]
+ if msg == 'unnamed_error':
raise errors.MissingDesignDocListFunctionError(path)
+ elif msg == 'TypeError':
+ if 'point is undefined' in exc.message[1][1]:
+ raise errors.MissingDesignDocListFunctionError
# other errors are unknown for now
raise errors.DesignDocUnknownError(path)
diff --git a/common/src/leap/soledad/common/tests/test_https.py b/common/src/leap/soledad/common/tests/test_https.py
index 4dd55754..6907e3ed 100644
--- a/common/src/leap/soledad/common/tests/test_https.py
+++ b/common/src/leap/soledad/common/tests/test_https.py
@@ -50,16 +50,22 @@ LEAP_SCENARIOS = [
# The following tests come from `u1db.tests.test_https`.
#-----------------------------------------------------------------------------
-def token_leap_https_sync_target(test, host, path):
+def token_leap_https_sync_target(test, host, path, cert_file=None):
_, port = test.server.server_address
- st = client.target.SoledadSyncTarget(
+ #source_replica_uid = test._soledad._dbpool.replica_uid
+ creds = {'token': {'uuid': 'user-uuid', 'token': 'auth-token'}}
+ if not cert_file:
+ cert_file = test.cacert_pem
+ st = client.http_target.SoledadHTTPSyncTarget(
'https://%s:%d/%s' % (host, port, path),
- crypto=test._soledad._crypto)
- st.set_token_credentials('user-uuid', 'auth-token')
+ source_replica_uid='other-id',
+ creds=creds,
+ crypto=test._soledad._crypto,
+ cert_file=cert_file)
return st
-class TestSoledadSyncTargetHttpsSupport(
+class TestSoledadHTTPSyncTargetHttpsSupport(
TestWithScenarios,
test_https.TestHttpSyncTargetHttpsSupport,
BaseSoledadTest):
@@ -80,6 +86,29 @@ class TestSoledadSyncTargetHttpsSupport(
http_client._VerifiedHTTPSConnection = client.api.VerifiedHTTPSConnection
client.api.SOLEDAD_CERT = http_client.CA_CERTS
+ def test_cannot_verify_cert(self):
+ self.startServer()
+ # don't print expected traceback server-side
+ self.server.handle_error = lambda req, cli_addr: None
+ self.request_state._create_database('test')
+ remote_target = self.getSyncTarget(
+ 'localhost', 'test', cert_file=http_client.CA_CERTS)
+ d = remote_target.record_sync_info('other-id', 2, 'T-id')
+
+ def _assert_raises(result):
+ from twisted.python.failure import Failure
+ if isinstance(result, Failure):
+ from OpenSSL.SSL import Error
+ error = result.value.message[0].value
+ if isinstance(error, Error):
+ msg = error.message[0][2]
+ self.assertEqual("certificate verify failed", msg)
+ return
+ self.fail("certificate verification should have failed.")
+
+ d.addCallbacks(_assert_raises, _assert_raises)
+ return d
+
def test_working(self):
"""
Test that SSL connections work well.
@@ -89,24 +118,19 @@ class TestSoledadSyncTargetHttpsSupport(
"""
self.startServer()
db = self.request_state._create_database('test')
- self.patch(client.api, 'SOLEDAD_CERT', self.cacert_pem)
remote_target = self.getSyncTarget('localhost', 'test')
- remote_target.record_sync_info('other-id', 2, 'T-id')
- self.assertEqual(
- (2, 'T-id'), db._get_replica_gen_and_trans_id('other-id'))
+ d = remote_target.record_sync_info('other-id', 2, 'T-id')
+ d.addCallback(lambda _:
+ self.assertEqual(
+ (2, 'T-id'), db._get_replica_gen_and_trans_id('other-id')))
+ d.addCallback(lambda _:
+ remote_target.close())
+ return d
def test_host_mismatch(self):
"""
- Test that SSL connections to a hostname different than the one in the
- certificate raise CertificateError.
-
- This test was adapted to patch Soledad's HTTPS connection custom class
- with the intended CA certificates.
+ This test is disabled because soledad's twisted-based http agent uses
+ pyOpenSSL, which will complain if we try to use an IP to connect to
+ the remote host (see the original test in u1db_tests/test_https.py).
"""
- self.startServer()
- self.request_state._create_database('test')
- self.patch(client.api, 'SOLEDAD_CERT', self.cacert_pem)
- remote_target = self.getSyncTarget('127.0.0.1', 'test')
- self.assertRaises(
- http_client.CertificateError, remote_target.record_sync_info,
- 'other-id', 2, 'T-id')
+ pass
diff --git a/common/src/leap/soledad/common/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py
index 2b653a1c..a8012e08 100644
--- a/common/src/leap/soledad/common/tests/test_server.py
+++ b/common/src/leap/soledad/common/tests/test_server.py
@@ -22,6 +22,7 @@ import tempfile
import mock
import time
import binascii
+from uuid import uuid4
from urlparse import urljoin
from twisted.internet import defer
@@ -93,7 +94,7 @@ class ServerAuthorizationTestCase(BaseSoledadTest):
/user-db/doc/{id} | -
/user-db/sync-from/{source} | GET, PUT, POST
"""
- uuid = 'myuuid'
+ uuid = uuid4().hex
authmap = URLToAuthorization(uuid,)
dbname = authmap._user_db_name
# test global auth
@@ -208,7 +209,7 @@ class ServerAuthorizationTestCase(BaseSoledadTest):
"""
Test if authorization fails for a wrong dbname.
"""
- uuid = 'myuuid'
+ uuid = uuid4().hex
authmap = URLToAuthorization(uuid)
dbname = 'somedb'
# test wrong-db database resource auth
@@ -283,7 +284,7 @@ class EncryptedSyncTestCase(
sync_target = token_soledad_sync_target
- def _soledad_instance(self, user='user-uuid', passphrase=u'123',
+ def _soledad_instance(self, user=None, passphrase=u'123',
prefix='',
secrets_path='secrets.json',
local_db_path='soledad.u1db',
@@ -336,15 +337,17 @@ class EncryptedSyncTestCase(
TestCaseWithServer.tearDown(self)
def _test_encrypted_sym_sync(self, passphrase=u'123', doc_size=2,
- number_of_docs=1):
+ number_of_docs=1):
"""
Test the complete syncing chain between two soledad dbs using a
Soledad server backed by a couch database.
"""
self.startServer()
+ user = 'user-' + uuid4().hex
# instantiate soledad and create a document
sol1 = self._soledad_instance(
+ user=user,
# token is verified in test_target.make_token_soledad_app
auth_token='auth-token',
passphrase=passphrase)
@@ -352,6 +355,7 @@ class EncryptedSyncTestCase(
# instantiate another soledad using the same secret as the previous
# one (so we can correctly verify the mac of the synced document)
sol2 = self._soledad_instance(
+ user=user,
prefix='x',
auth_token='auth-token',
secrets_path=sol1._secrets_path,
@@ -359,7 +363,7 @@ class EncryptedSyncTestCase(
# ensure remote db exists before syncing
db = CouchDatabase.open_database(
- urljoin(self._couch_url, 'user-user-uuid'),
+ urljoin(self._couch_url, 'user-' + user),
create=True,
ensure_ddocs=True)
@@ -370,7 +374,7 @@ class EncryptedSyncTestCase(
def _db1CreateDocs(results):
deferreds = []
for i in xrange(number_of_docs):
- content = binascii.hexlify(os.urandom(doc_size/2))
+ content = binascii.hexlify(os.urandom(doc_size/2))
deferreds.append(sol1.create_doc({'data': content}))
return defer.DeferredList(deferreds)
@@ -461,6 +465,7 @@ class EncryptedSyncTestCase(
"""
return self._test_encrypted_sym_sync(doc_size=2, number_of_docs=100)
+
class LockResourceTestCase(
CouchDBTestCase, TestCaseWithServer):
"""
@@ -506,7 +511,8 @@ class LockResourceTestCase(
def test__try_obtain_filesystem_lock(self):
responder = mock.Mock()
- lr = LockResource('uuid', self._state, responder)
+ lock_uuid = uuid4().hex
+ lr = LockResource(lock_uuid, self._state, responder)
self.assertFalse(lr._lock.locked)
self.assertTrue(lr._try_obtain_filesystem_lock())
self.assertTrue(lr._lock.locked)
@@ -514,7 +520,8 @@ class LockResourceTestCase(
def test__try_release_filesystem_lock(self):
responder = mock.Mock()
- lr = LockResource('uuid', self._state, responder)
+ lock_uuid = uuid4().hex
+ lr = LockResource(lock_uuid, self._state, responder)
lr._try_obtain_filesystem_lock()
self.assertTrue(lr._lock.locked)
lr._try_release_filesystem_lock()
@@ -522,11 +529,12 @@ class LockResourceTestCase(
def test_put(self):
responder = mock.Mock()
- lr = LockResource('uuid', self._state, responder)
+ lock_uuid = uuid4().hex
+ lr = LockResource(lock_uuid, self._state, responder)
# lock!
lr.put({}, None)
# assert lock document was correctly written
- lock_doc = lr._shared_db.get_doc('lock-uuid')
+ lock_doc = lr._shared_db.get_doc('lock-' + lock_uuid)
self.assertIsNotNone(lock_doc)
self.assertTrue(LockResource.TIMESTAMP_KEY in lock_doc.content)
self.assertTrue(LockResource.LOCK_TOKEN_KEY in lock_doc.content)
@@ -541,20 +549,22 @@ class LockResourceTestCase(
def test_delete(self):
responder = mock.Mock()
- lr = LockResource('uuid', self._state, responder)
+ lock_uuid = uuid4().hex
+ lr = LockResource(lock_uuid, self._state, responder)
# lock!
lr.put({}, None)
- lock_doc = lr._shared_db.get_doc('lock-uuid')
+ lock_doc = lr._shared_db.get_doc('lock-' + lock_uuid)
token = lock_doc.content[LockResource.LOCK_TOKEN_KEY]
# unlock!
lr.delete({'token': token}, None)
self.assertFalse(lr._lock.locked)
- self.assertIsNone(lr._shared_db.get_doc('lock-uuid'))
+ self.assertIsNone(lr._shared_db.get_doc('lock-' + lock_uuid))
responder.send_response_json.assert_called_with(200)
def test_put_while_locked_fails(self):
responder = mock.Mock()
- lr = LockResource('uuid', self._state, responder)
+ lock_uuid = uuid4().hex
+ lr = LockResource(lock_uuid, self._state, responder)
# lock!
lr.put({}, None)
# try to lock again!
@@ -572,7 +582,8 @@ class LockResourceTestCase(
def test_unlock_unexisting_lock_fails(self):
responder = mock.Mock()
- lr = LockResource('uuid', self._state, responder)
+ lock_uuid = uuid4().hex
+ lr = LockResource(lock_uuid, self._state, responder)
# unlock!
lr.delete({'token': 'anything'}, None)
responder.send_response_json.assert_called_with(
@@ -580,11 +591,12 @@ class LockResourceTestCase(
def test_unlock_with_wrong_token_fails(self):
responder = mock.Mock()
- lr = LockResource('uuid', self._state, responder)
+ lock_uuid = uuid4().hex
+ lr = LockResource(lock_uuid, self._state, responder)
# lock!
lr.put({}, None)
# unlock!
lr.delete({'token': 'wrongtoken'}, None)
- self.assertIsNotNone(lr._shared_db.get_doc('lock-uuid'))
+ self.assertIsNotNone(lr._shared_db.get_doc('lock-' + lock_uuid))
responder.send_response_json.assert_called_with(
401, error='unlock unauthorized')
diff --git a/common/src/leap/soledad/common/tests/u1db_tests/test_https.py b/common/src/leap/soledad/common/tests/u1db_tests/test_https.py
index cea175d6..f22ce51e 100644
--- a/common/src/leap/soledad/common/tests/u1db_tests/test_https.py
+++ b/common/src/leap/soledad/common/tests/u1db_tests/test_https.py
@@ -80,10 +80,10 @@ class TestHttpSyncTargetHttpsSupport(tests.TestCaseWithServer):
soledad.client.api.old__VerifiedHTTPSConnection
super(TestHttpSyncTargetHttpsSupport, self).setUp()
- def getSyncTarget(self, host, path=None):
+ def getSyncTarget(self, host, path=None, cert_file=None):
if self.server is None:
self.startServer()
- return self.sync_target(self, host, path)
+ return self.sync_target(self, host, path, cert_file=cert_file)
def test_working(self):
self.startServer()
diff --git a/common/src/leap/soledad/common/tests/util.py b/common/src/leap/soledad/common/tests/util.py
index 17ed3855..60bab81c 100644
--- a/common/src/leap/soledad/common/tests/util.py
+++ b/common/src/leap/soledad/common/tests/util.py
@@ -50,7 +50,7 @@ from leap.soledad.common.couch import CouchDatabase, CouchServerState
from leap.soledad.common.crypto import ENC_SCHEME_KEY
from leap.soledad.client import Soledad
-from leap.soledad.client import target
+from leap.soledad.client import http_target
from leap.soledad.client import auth
from leap.soledad.client.crypto import decrypt_doc_dict
@@ -102,7 +102,7 @@ def make_token_soledad_app(state):
app = SoledadApp(state)
def _verify_authentication_data(uuid, auth_data):
- if uuid == 'user-uuid' and auth_data == 'auth-token':
+ if uuid.startswith('user-') and auth_data == 'auth-token':
return True
return False
@@ -165,6 +165,7 @@ class MockedSharedDBTest(object):
lock = Mock(return_value=('atoken', 300))
unlock = Mock(return_value=True)
open = Mock(return_value=None)
+ close = Mock(return_value=None)
syncable = True
def __call__(self):
@@ -173,7 +174,7 @@ class MockedSharedDBTest(object):
def soledad_sync_target(test, path):
- return target.SoledadSyncTarget(
+ return http_target.SoledadSyncTarget(
test.getURL(path), crypto=test._soledad._crypto)
diff --git a/server/pkg/requirements.pip b/server/pkg/requirements.pip
index df6ad95d..43088222 100644
--- a/server/pkg/requirements.pip
+++ b/server/pkg/requirements.pip
@@ -3,7 +3,7 @@ couchdb
simplejson
u1db
routes
-PyOpenSSL<0.14
+PyOpenSSL
twisted
# leap deps -- bump me!