summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2015-07-22 17:52:15 -0300
committerKali Kaneko <kali@leap.se>2015-07-23 14:03:01 -0400
commite35341d57ac5fa87be5de64d62e219a105389c4c (patch)
treef8c67b61ee0461aef29afa68bc0472617cac9f45 /client
parent1aa96859a5751adc10e32d0143c02482fd210b50 (diff)
[bug] move sync db and encpool creation to api
Deferred encryption was disabled because the soledad u1db wrapper for adbapi did not correctly udated the parameter that controls it. Also, it did not contain the encrypter pool. This commit moves the sync db and encrypt pool to the main api, so they can be passed to the wrapper and deferred encryption can work.
Diffstat (limited to 'client')
-rw-r--r--client/src/leap/soledad/client/adbapi.py26
-rw-r--r--client/src/leap/soledad/client/api.py70
-rw-r--r--client/src/leap/soledad/client/encdecpool.py2
-rw-r--r--client/src/leap/soledad/client/sqlcipher.py67
4 files changed, 91 insertions, 74 deletions
diff --git a/client/src/leap/soledad/client/adbapi.py b/client/src/leap/soledad/client/adbapi.py
index bc0ab7a5..9675c048 100644
--- a/client/src/leap/soledad/client/adbapi.py
+++ b/client/src/leap/soledad/client/adbapi.py
@@ -54,7 +54,8 @@ How many times a SQLCipher query should be retried in case of timeout.
SQLCIPHER_MAX_RETRIES = 10
-def getConnectionPool(opts, openfun=None, driver="pysqlcipher"):
+def getConnectionPool(opts, openfun=None, driver="pysqlcipher",
+ sync_enc_pool=None):
"""
Return a connection pool.
@@ -75,8 +76,8 @@ def getConnectionPool(opts, openfun=None, driver="pysqlcipher"):
if openfun is None and driver == "pysqlcipher":
openfun = partial(set_init_pragmas, opts=opts)
return U1DBConnectionPool(
- "%s.dbapi2" % driver, database=opts.path,
- check_same_thread=False, cp_openfun=openfun,
+ "%s.dbapi2" % driver, opts=opts, sync_enc_pool=sync_enc_pool,
+ database=opts.path, check_same_thread=False, cp_openfun=openfun,
timeout=SQLCIPHER_CONNECTION_TIMEOUT)
@@ -90,7 +91,7 @@ class U1DBConnection(adbapi.Connection):
The U1DB wrapper to use.
"""
- def __init__(self, pool, init_u1db=False):
+ def __init__(self, pool, sync_enc_pool, init_u1db=False):
"""
:param pool: The pool of connections to that owns this connection.
:type pool: adbapi.ConnectionPool
@@ -98,6 +99,7 @@ class U1DBConnection(adbapi.Connection):
:type init_u1db: bool
"""
self.init_u1db = init_u1db
+ self._sync_enc_pool = sync_enc_pool
adbapi.Connection.__init__(self, pool)
def reconnect(self):
@@ -109,7 +111,10 @@ class U1DBConnection(adbapi.Connection):
self._connection = self._pool.connect()
if self.init_u1db:
- self._u1db = self.u1db_wrapper(self._connection)
+ self._u1db = self.u1db_wrapper(
+ self._connection,
+ self._pool.opts,
+ self._sync_enc_pool)
def __getattr__(self, name):
"""
@@ -158,14 +163,20 @@ class U1DBConnectionPool(adbapi.ConnectionPool):
"""
Initialize the connection pool.
"""
+ # extract soledad-specific objects from keyword arguments
+ self.opts = kwargs.pop("opts")
+ self._sync_enc_pool = kwargs.pop("sync_enc_pool")
+
adbapi.ConnectionPool.__init__(self, *args, **kwargs)
+
# all u1db connections, hashed by thread-id
self._u1dbconnections = {}
# The replica uid, primed by the connections on init.
self.replica_uid = ProxyBase(None)
- conn = self.connectionFactory(self, init_u1db=True)
+ conn = self.connectionFactory(
+ self, self._sync_enc_pool, init_u1db=True)
replica_uid = conn._u1db._real_replica_uid
setProxiedObject(self.replica_uid, replica_uid)
@@ -235,7 +246,8 @@ class U1DBConnectionPool(adbapi.ConnectionPool):
"""
tid = self.threadID()
u1db = self._u1dbconnections.get(tid)
- conn = self.connectionFactory(self, init_u1db=not bool(u1db))
+ conn = self.connectionFactory(
+ self, self._sync_enc_pool, init_u1db=not bool(u1db))
if self.replica_uid is None:
replica_uid = conn._u1db._real_replica_uid
diff --git a/client/src/leap/soledad/client/api.py b/client/src/leap/soledad/client/api.py
index 6c2b3673..2bc524c3 100644
--- a/client/src/leap/soledad/client/api.py
+++ b/client/src/leap/soledad/client/api.py
@@ -43,7 +43,6 @@ 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
@@ -59,7 +58,8 @@ from leap.soledad.client import interfaces as soledad_interfaces
from leap.soledad.client.crypto import SoledadCrypto
from leap.soledad.client.secrets import SoledadSecrets
from leap.soledad.client.shared_db import SoledadSharedDatabase
-from leap.soledad.client.sqlcipher import SQLCipherOptions, SQLCipherU1DBSync
+from leap.soledad.client import sqlcipher
+from leap.soledad.client import encdecpool
logger = logging.getLogger(name=__name__)
@@ -175,6 +175,7 @@ class Soledad(object):
self._server_url = server_url
self._defer_encryption = defer_encryption
self._secrets_path = None
+ self._sync_enc_pool = None
self.shared_db = shared_db
@@ -259,24 +260,32 @@ class Soledad(object):
key = tohex(self._secrets.get_local_storage_key())
sync_db_key = tohex(self._secrets.get_sync_db_key())
- opts = SQLCipherOptions(
+ opts = sqlcipher.SQLCipherOptions(
self._local_db_path, key,
is_raw_key=True, create=True,
defer_encryption=self._defer_encryption,
sync_db_key=sync_db_key,
)
self._sqlcipher_opts = opts
- self._dbpool = adbapi.getConnectionPool(opts)
+
+ # the sync_db is used both for deferred encryption and decryption, so
+ # we want to initialize it anyway to allow for all combinations of
+ # deferred encryption and decryption configurations.
+ self._initialize_sync_db(opts)
+ self._dbpool = adbapi.getConnectionPool(
+ opts, sync_enc_pool=self._sync_enc_pool)
def _init_u1db_syncer(self):
"""
Initialize the U1DB synchronizer.
"""
replica_uid = self._dbpool.replica_uid
- self._dbsyncer = SQLCipherU1DBSync(
+ self._dbsyncer = sqlcipher.SQLCipherU1DBSync(
self._sqlcipher_opts, self._crypto, replica_uid,
SOLEDAD_CERT,
- defer_encryption=self._defer_encryption)
+ defer_encryption=self._defer_encryption,
+ sync_db=self._sync_db,
+ sync_enc_pool=self._sync_enc_pool)
#
# Closing methods
@@ -290,6 +299,9 @@ class Soledad(object):
self._dbpool.close()
if getattr(self, '_dbsyncer', None):
self._dbsyncer.close()
+ # close the sync database
+ self._sync_db.close()
+ self._sync_db = None
#
# ILocalStorage
@@ -729,6 +741,52 @@ class Soledad(object):
token = property(_get_token, _set_token, doc='The authentication Token.')
+ def _initialize_sync_db(self, opts):
+ """
+ Initialize the Symmetrically-Encrypted document to be synced database,
+ and the queue to communicate with subprocess workers.
+
+ :param opts:
+ :type opts: SQLCipherOptions
+ """
+ soledad_assert(opts.sync_db_key is not None)
+ sync_db_path = None
+ if opts.path != ":memory:":
+ sync_db_path = "%s-sync" % opts.path
+ else:
+ sync_db_path = ":memory:"
+
+ # we copy incoming options because the opts object might be used
+ # somewhere else
+ sync_opts = sqlcipher.SQLCipherOptions.copy(
+ opts, path=sync_db_path, create=True)
+ self._sync_db = sqlcipher.getConnectionPool(
+ sync_opts, extra_queries=self._sync_db_extra_init)
+ if self._defer_encryption:
+ # initialize syncing queue encryption pool
+ self._sync_enc_pool = encdecpool.SyncEncrypterPool(
+ self._crypto, self._sync_db)
+
+
+ @property
+ def _sync_db_extra_init(self):
+ """
+ Queries for creating tables for the local sync documents db if needed.
+ They are passed as extra initialization to initialize_sqlciphjer_db
+
+ :rtype: tuple of strings
+ """
+ maybe_create = "CREATE TABLE IF NOT EXISTS %s (%s)"
+ encr = encdecpool.SyncEncrypterPool
+ decr = encdecpool.SyncDecrypterPool
+ sql_encr_table_query = (maybe_create % (
+ encr.TABLE_NAME, encr.FIELD_NAMES))
+ sql_decr_table_query = (maybe_create % (
+ decr.TABLE_NAME, decr.FIELD_NAMES))
+ return (sql_encr_table_query, sql_decr_table_query)
+
+
+
#
# ISecretsStorage
#
diff --git a/client/src/leap/soledad/client/encdecpool.py b/client/src/leap/soledad/client/encdecpool.py
index 7923bf70..0b3e7eb8 100644
--- a/client/src/leap/soledad/client/encdecpool.py
+++ b/client/src/leap/soledad/client/encdecpool.py
@@ -261,8 +261,10 @@ class SyncEncrypterPool(SyncEncryptDecryptPool):
% self.TABLE_NAME
result = yield self._runQuery(query, (doc_id, doc_rev))
if result:
+ logger.debug("Found doc on sync db: %s" % doc_id)
val = result.pop()
defer.returnValue(val[0])
+ logger.debug("Did not find doc on sync db: %s" % doc_id)
defer.returnValue(None)
def delete_encrypted_doc(self, doc_id, doc_rev):
diff --git a/client/src/leap/soledad/client/sqlcipher.py b/client/src/leap/soledad/client/sqlcipher.py
index 75d786a6..68802dd6 100644
--- a/client/src/leap/soledad/client/sqlcipher.py
+++ b/client/src/leap/soledad/client/sqlcipher.py
@@ -414,8 +414,6 @@ class SQLCipherU1DBSync(SQLCipherDatabase):
Soledad syncer implementation.
"""
- _sync_enc_pool = None
-
"""
The name of the local symmetrically encrypted documents to
sync database file.
@@ -435,17 +433,16 @@ class SQLCipherU1DBSync(SQLCipherDatabase):
syncing_lock = defaultdict(threading.Lock)
def __init__(self, opts, soledad_crypto, replica_uid, cert_file,
- defer_encryption=False):
+ defer_encryption=False, sync_db=None, sync_enc_pool=None):
self._opts = opts
self._path = opts.path
self._crypto = soledad_crypto
self.__replica_uid = replica_uid
self._cert_file = cert_file
+ self._sync_enc_pool = sync_enc_pool
- self._sync_db_key = opts.sync_db_key
- self._sync_db = None
- self._sync_enc_pool = None
+ self._sync_db = sync_db
# we store syncers in a dictionary indexed by the target URL. We also
# store a hash of the auth info in case auth info expires and we need
@@ -469,16 +466,6 @@ class SQLCipherU1DBSync(SQLCipherDatabase):
self._db_handle = None
self._initialize_main_db()
- # the sync_db is used both for deferred encryption and decryption, so
- # we want to initialize it anyway to allow for all combinations of
- # deferred encryption and decryption configurations.
- self._initialize_sync_db(opts)
-
- if defer_encryption:
- # initialize syncing queue encryption pool
- self._sync_enc_pool = encdecpool.SyncEncrypterPool(
- self._crypto, self._sync_db)
-
self.shutdownID = None
@property
@@ -521,45 +508,6 @@ class SQLCipherU1DBSync(SQLCipherDatabase):
# before the database has been initialized.
self._sync_threadpool = ThreadPool(0, 1)
- def _initialize_sync_db(self, opts):
- """
- Initialize the Symmetrically-Encrypted document to be synced database,
- and the queue to communicate with subprocess workers.
-
- :param opts:
- :type opts: SQLCipherOptions
- """
- soledad_assert(opts.sync_db_key is not None)
- sync_db_path = None
- if opts.path != ":memory:":
- sync_db_path = "%s-sync" % opts.path
- else:
- sync_db_path = ":memory:"
-
- # we copy incoming options because the opts object might be used
- # somewhere else
- sync_opts = SQLCipherOptions.copy(
- opts, path=sync_db_path, create=True)
- self._sync_db = getConnectionPool(
- sync_opts, extra_queries=self._sync_db_extra_init)
-
- @property
- def _sync_db_extra_init(self):
- """
- Queries for creating tables for the local sync documents db if needed.
- They are passed as extra initialization to initialize_sqlciphjer_db
-
- :rtype: tuple of strings
- """
- maybe_create = "CREATE TABLE IF NOT EXISTS %s (%s)"
- encr = encdecpool.SyncEncrypterPool
- decr = encdecpool.SyncDecrypterPool
- sql_encr_table_query = (maybe_create % (
- encr.TABLE_NAME, encr.FIELD_NAMES))
- sql_decr_table_query = (maybe_create % (
- decr.TABLE_NAME, decr.FIELD_NAMES))
- return (sql_encr_table_query, sql_decr_table_query)
-
def sync(self, url, creds=None, defer_decryption=True):
"""
Synchronize documents with remote replica exposed at url.
@@ -695,11 +643,6 @@ class SQLCipherU1DBSync(SQLCipherDatabase):
self._sync_enc_pool.close()
self._sync_enc_pool = None
- # close the sync database
- if self._sync_db is not None:
- self._sync_db.close()
- self._sync_db = None
-
class U1DBSQLiteBackend(sqlite_backend.SQLitePartialExpandDatabase):
"""
@@ -729,12 +672,14 @@ class SoledadSQLCipherWrapper(SQLCipherDatabase):
It can be used from adbapi to initialize a soledad database after
getting a regular connection to a sqlcipher database.
"""
- def __init__(self, conn):
+ def __init__(self, conn, opts, sync_enc_pool):
self._db_handle = conn
self._real_replica_uid = None
self._ensure_schema()
self.set_document_factory(soledad_doc_factory)
self._prime_replica_uid()
+ self.defer_encryption = opts.defer_encryption
+ self._sync_enc_pool = sync_enc_pool
def _assert_db_is_encrypted(opts):