summaryrefslogtreecommitdiff
path: root/src/leap/soledad/backends
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/soledad/backends')
-rw-r--r--src/leap/soledad/backends/couch.py49
-rw-r--r--src/leap/soledad/backends/leap_backend.py8
-rw-r--r--src/leap/soledad/backends/objectstore.py22
-rw-r--r--src/leap/soledad/backends/openstack.py14
-rw-r--r--src/leap/soledad/backends/sqlcipher.py16
5 files changed, 52 insertions, 57 deletions
diff --git a/src/leap/soledad/backends/couch.py b/src/leap/soledad/backends/couch.py
index 78026af8..8ba42d78 100644
--- a/src/leap/soledad/backends/couch.py
+++ b/src/leap/soledad/backends/couch.py
@@ -1,7 +1,5 @@
-import sys
import uuid
from base64 import b64encode, b64decode
-from u1db import errors
from u1db.sync import LocalSyncTarget
from couchdb.client import Server, Document as CouchDocument
from couchdb.http import ResourceNotFound
@@ -17,7 +15,8 @@ except ImportError:
class CouchDatabase(ObjectStore):
"""A U1DB implementation that uses Couch as its persistence layer."""
- def __init__(self, url, database, replica_uid=None, full_commit=True, session=None):
+ def __init__(self, url, database, replica_uid=None, full_commit=True,
+ session=None):
"""Create a new Couch data container."""
self._url = url
self._full_commit = full_commit
@@ -54,8 +53,9 @@ class CouchDatabase(ObjectStore):
doc_id=doc_id,
rev=cdoc['u1db_rev'],
has_conflicts=has_conflicts)
- if cdoc['u1db_json'] is not None:
- doc.content = json.loads(cdoc['u1db_json'])
+ contents = self._database.get_attachment(cdoc, 'u1db_json')
+ if contents:
+ doc.content = json.loads(contents.getvalue())
else:
doc.make_tombstone()
return doc
@@ -83,13 +83,14 @@ class CouchDatabase(ObjectStore):
cdoc['_rev'] = old_cdoc['_rev']
# store u1db's rev
cdoc['u1db_rev'] = doc.rev
+ # save doc in db
+ self._database.save(cdoc)
# store u1db's content as json string
if not doc.is_tombstone():
- cdoc['u1db_json'] = doc.get_json()
+ self._database.put_attachment(cdoc, doc.get_json(),
+ filename='u1db_json')
else:
- cdoc['u1db_json'] = None
- # save doc in db
- self._database.save(cdoc)
+ self._database.delete_attachment(cdoc, 'u1db_json')
def get_sync_target(self):
return CouchSyncTarget(self)
@@ -103,7 +104,6 @@ class CouchDatabase(ObjectStore):
#self._server = None
self._database = None
return True
-
def sync(self, url, creds=None, autocreate=True):
from u1db.sync import Synchronizer
@@ -114,15 +114,16 @@ class CouchDatabase(ObjectStore):
if self._replica_uid is None:
self._replica_uid = uuid.uuid4().hex
doc = self._factory(doc_id=self.U1DB_DATA_DOC_ID)
- doc.content = { 'sync_log' : [],
- 'transaction_log' : [],
- 'conflict_log' : b64encode(json.dumps([])),
- 'replica_uid' : self._replica_uid }
+ doc.content = {'sync_log': [],
+ 'transaction_log': [],
+ 'conflict_log': b64encode(json.dumps([])),
+ 'replica_uid': self._replica_uid}
self._put_doc(doc)
def _get_u1db_data(self):
cdoc = self._database.get(self.U1DB_DATA_DOC_ID)
- content = json.loads(cdoc['u1db_json'])
+ jsonstr = self._database.get_attachment(cdoc, 'u1db_json').getvalue()
+ content = json.loads(jsonstr)
self._sync_log.log = content['sync_log']
self._transaction_log.log = content['transaction_log']
self._conflict_log.log = json.loads(b64decode(content['conflict_log']))
@@ -131,14 +132,15 @@ class CouchDatabase(ObjectStore):
def _set_u1db_data(self):
doc = self._factory(doc_id=self.U1DB_DATA_DOC_ID)
- doc.content = { 'sync_log' : self._sync_log.log,
- 'transaction_log' : self._transaction_log.log,
- # Here, the b64 encode ensures that document content
- # does not cause strange behaviour in couchdb because
- # of encoding.
- 'conflict_log' : b64encode(json.dumps(self._conflict_log.log)),
- 'replica_uid' : self._replica_uid,
- '_rev' : self._couch_rev}
+ doc.content = {
+ 'sync_log': self._sync_log.log,
+ 'transaction_log': self._transaction_log.log,
+ # Here, the b64 encode ensures that document content
+ # does not cause strange behaviour in couchdb because
+ # of encoding.
+ 'conflict_log': b64encode(json.dumps(self._conflict_log.log)),
+ 'replica_uid': self._replica_uid,
+ '_rev': self._couch_rev}
self._put_doc(doc)
#-------------------------------------------------------------------------
@@ -166,4 +168,3 @@ class CouchSyncTarget(LocalSyncTarget):
self._db._set_replica_gen_and_trans_id(
source_replica_uid, source_replica_generation,
source_replica_transaction_id)
-
diff --git a/src/leap/soledad/backends/leap_backend.py b/src/leap/soledad/backends/leap_backend.py
index 3e859f7c..7e98dd45 100644
--- a/src/leap/soledad/backends/leap_backend.py
+++ b/src/leap/soledad/backends/leap_backend.py
@@ -41,8 +41,9 @@ class LeapDocument(Document):
"""
if not self._soledad:
raise NoSoledadInstance()
- ciphertext = self._soledad.encrypt_symmetric(self.doc_id, self.get_json())
- return json.dumps({'_encrypted_json' : ciphertext})
+ ciphertext = self._soledad.encrypt_symmetric(self.doc_id,
+ self.get_json())
+ return json.dumps({'_encrypted_json': ciphertext})
def set_encrypted_json(self, encrypted_json):
"""
@@ -89,7 +90,8 @@ class LeapDatabase(HTTPDatabase):
doc_id = self._allocate_doc_id()
res, headers = self._request_json('PUT', ['doc', doc_id], {},
content, 'application/json')
- new_doc = self._factory(doc_id, res['rev'], content, soledad=self._soledad)
+ new_doc = self._factory(doc_id, res['rev'], content,
+ soledad=self._soledad)
return new_doc
diff --git a/src/leap/soledad/backends/objectstore.py b/src/leap/soledad/backends/objectstore.py
index b6523336..d72a2ecc 100644
--- a/src/leap/soledad/backends/objectstore.py
+++ b/src/leap/soledad/backends/objectstore.py
@@ -1,6 +1,7 @@
from u1db.backends import CommonBackend
from u1db import errors, Document, vectorclock
+
class ObjectStore(CommonBackend):
"""
A backend for storing u1db data in an object store.
@@ -139,12 +140,13 @@ class ObjectStore(CommonBackend):
def _set_replica_gen_and_trans_id(self, other_replica_uid,
other_generation, other_transaction_id):
return self._do_set_replica_gen_and_trans_id(
- other_replica_uid,
- other_generation,
- other_transaction_id)
+ other_replica_uid,
+ other_generation,
+ other_transaction_id)
def _do_set_replica_gen_and_trans_id(self, other_replica_uid,
- other_generation, other_transaction_id):
+ other_generation,
+ other_transaction_id):
self._sync_log.set_replica_gen_and_trans_id(other_replica_uid,
other_generation,
other_transaction_id)
@@ -201,7 +203,6 @@ class ObjectStore(CommonBackend):
"""
Verify if u1db data exists in store.
"""
- doc = self._get_doc(self.U1DB_DATA_DOC_ID)
if not self._get_doc(self.U1DB_DATA_DOC_ID):
return False
return True
@@ -234,7 +235,6 @@ class ObjectStore(CommonBackend):
replica_uid = property(
_get_replica_uid, _set_replica_uid, doc="Replica UID of the database")
-
#-------------------------------------------------------------------------
# The methods below were cloned from u1db sqlite backend. They should at
# least exist and raise a NotImplementedError exception in CommonBackend
@@ -387,12 +387,12 @@ class TransactionLog(SimpleLog):
return cur_gen, newest_trans_id, changes
-
def get_transaction_log(self):
"""
Return only a list of (doc_id, transaction_id)
"""
- return map(lambda x: (x[1], x[2]), sorted(self._get_log(reverse=False)))
+ return map(lambda x: (x[1], x[2]),
+ sorted(self._get_log(reverse=False)))
class SyncLog(SimpleLog):
@@ -416,7 +416,7 @@ class SyncLog(SimpleLog):
return (info[1], info[2])
def set_replica_gen_and_trans_id(self, other_replica_uid,
- other_generation, other_transaction_id):
+ other_generation, other_transaction_id):
"""
Set the last-known generation and transaction id for the other
database replica.
@@ -425,6 +425,7 @@ class SyncLog(SimpleLog):
self.append((other_replica_uid, other_generation,
other_transaction_id))
+
class ConflictLog(SimpleLog):
"""
A list of (doc_id, my_doc_rev, my_content) tuples.
@@ -433,7 +434,7 @@ class ConflictLog(SimpleLog):
def __init__(self, factory):
super(ConflictLog, self).__init__()
self._factory = factory
-
+
def delete_conflicts(self, conflicts):
for conflict in conflicts:
self._set_log(self.filter(lambda x:
@@ -448,4 +449,3 @@ class ConflictLog(SimpleLog):
def has_conflicts(self, doc_id):
return bool(self.filter(lambda x: x[0] == doc_id))
-
diff --git a/src/leap/soledad/backends/openstack.py b/src/leap/soledad/backends/openstack.py
index c027231c..a9615736 100644
--- a/src/leap/soledad/backends/openstack.py
+++ b/src/leap/soledad/backends/openstack.py
@@ -1,6 +1,6 @@
-from u1db import errors
+# TODO: this backend is not tested yet.
from u1db.remote.http_target import HTTPSyncTarget
-from swiftclient import client
+import swiftclient
from soledad.backends.objectstore import ObjectStore
@@ -25,12 +25,13 @@ class OpenStackDatabase(ObjectStore):
def _get_doc(self, doc_id, check_for_conflicts=False):
"""Get just the document content, without fancy handling.
-
+
Conflicts do not happen on server side, so there's no need to check
for them.
"""
try:
- response, contents = self._connection.get_object(self._container, doc_id)
+ response, contents = self._connection.get_object(self._container,
+ doc_id)
# TODO: change revision to be a dictionary element?
rev = response['x-object-meta-rev']
return self._factory(doc_id, rev, contents)
@@ -53,7 +54,7 @@ class OpenStackDatabase(ObjectStore):
def _put_doc(self, doc, new_rev):
new_rev = self._allocate_doc_rev(doc.rev)
# TODO: change revision to be a dictionary element?
- headers = { 'X-Object-Meta-Rev' : new_rev }
+ headers = {'X-Object-Meta-Rev': new_rev}
self._connection.put_object(self._container, doc_id, doc.get_json(),
headers=headers)
@@ -77,6 +78,7 @@ class OpenStackDatabase(ObjectStore):
self._url, self._auth_token = self._connection.get_auth()
return self._url, self.auth_token
+
class OpenStackSyncTarget(HTTPSyncTarget):
def get_sync_info(self, source_replica_uid):
@@ -94,5 +96,3 @@ class OpenStackSyncTarget(HTTPSyncTarget):
self._db._set_replica_gen_and_trans_id(
source_replica_uid, source_replica_generation,
source_replica_transaction_id)
-
-
diff --git a/src/leap/soledad/backends/sqlcipher.py b/src/leap/soledad/backends/sqlcipher.py
index 3d03449e..08b4df43 100644
--- a/src/leap/soledad/backends/sqlcipher.py
+++ b/src/leap/soledad/backends/sqlcipher.py
@@ -59,11 +59,9 @@ class SQLCipherDatabase(SQLitePartialExpandDatabase):
_index_storage_value = 'expand referenced encrypted'
-
@classmethod
def set_pragma_key(cls, db_handle, key):
- db_handle.cursor().execute("PRAGMA key = '%s'" % key)
-
+ db_handle.cursor().execute("PRAGMA key = '%s'" % key)
def __init__(self, sqlite_file, password, document_factory=None):
"""Create a new sqlcipher file."""
@@ -74,20 +72,18 @@ class SQLCipherDatabase(SQLitePartialExpandDatabase):
self._ensure_schema()
self._factory = document_factory or Document
-
def _check_if_db_is_encrypted(self, sqlite_file):
if not os.path.exists(sqlite_file):
return
else:
try:
- # try to open an encrypted database with the regular u1db backend
- # should raise a DatabaseError exception.
+ # try to open an encrypted database with the regular u1db
+ # backend should raise a DatabaseError exception.
SQLitePartialExpandDatabase(sqlite_file)
raise DatabaseIsNotEncrypted()
except DatabaseError:
pass
-
@classmethod
def _open_database(cls, sqlite_file, password, document_factory=None):
if not os.path.isfile(sqlite_file):
@@ -113,7 +109,6 @@ class SQLCipherDatabase(SQLitePartialExpandDatabase):
return SQLCipherDatabase._sqlite_registry[v](
sqlite_file, password, document_factory=document_factory)
-
@classmethod
def open_database(cls, sqlite_file, password, create, backend_cls=None,
document_factory=None):
@@ -129,7 +124,6 @@ class SQLCipherDatabase(SQLitePartialExpandDatabase):
return backend_cls(sqlite_file, password,
document_factory=document_factory)
-
def sync(self, url, creds=None, autocreate=True, soledad=None):
"""
Synchronize encrypted documents with remote replica exposed at url.
@@ -137,9 +131,7 @@ class SQLCipherDatabase(SQLitePartialExpandDatabase):
from u1db.sync import Synchronizer
from leap.soledad.backends.leap_backend import LeapSyncTarget
return Synchronizer(self, LeapSyncTarget(url, creds=creds),
- soledad=self._soledad).sync(
- autocreate=autocreate)
+ soledad=self._soledad).sync(autocreate=autocreate)
SQLiteDatabase.register_implementation(SQLCipherDatabase)
-