summaryrefslogtreecommitdiff
path: root/client/src/leap/soledad
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/leap/soledad')
-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):