summaryrefslogtreecommitdiff
path: root/client/src/leap/soledad/client/api.py
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/leap/soledad/client/api.py')
-rw-r--r--client/src/leap/soledad/client/api.py78
1 files changed, 71 insertions, 7 deletions
diff --git a/client/src/leap/soledad/client/api.py b/client/src/leap/soledad/client/api.py
index 8c5f7f1b..e657c939 100644
--- a/client/src/leap/soledad/client/api.py
+++ b/client/src/leap/soledad/client/api.py
@@ -32,6 +32,7 @@ import logging
import os
import socket
import ssl
+import uuid
import urlparse
try:
@@ -44,11 +45,11 @@ from StringIO import StringIO
from collections import defaultdict
from u1db.remote import http_client
from u1db.remote.ssl_match_hostname import match_hostname
+from twisted.internet.defer import DeferredLock, returnValue, inlineCallbacks
from zope.interface import implements
from leap.common.config import get_path_prefix
from leap.common.plugins import collect_plugins
-from twisted.internet.defer import DeferredLock
from leap.soledad.common import SHARED_DB_NAME
from leap.soledad.common import soledad_assert
@@ -102,11 +103,11 @@ class Soledad(object):
soledad starts to retrieve keys from server.
SOLEDAD_DONE_DOWNLOADING_KEYS: emitted during bootstrap sequence when
soledad finishes downloading keys from server.
- SOLEDAD_NEW_DATA_TO_SYNC: emitted upon call to C{need_sync()} when
- there's indeed new data to be synchronized between local database
- replica and server's replica.
SOLEDAD_DONE_DATA_SYNC: emitted inside C{sync()} method when it has
finished synchronizing with remote replica.
+ SOLEDAD_NEW_DATA_TO_SYNC: emitted upon call to C{need_sync()} when
+ there's indeed new data to be synchronized between local database
+ replica and server's replica. --- not used right now.
"""
implements(soledad_interfaces.ILocalStorage,
soledad_interfaces.ISyncableStorage,
@@ -125,7 +126,8 @@ class Soledad(object):
def __init__(self, uuid, passphrase, secrets_path, local_db_path,
server_url, cert_file, shared_db=None,
- auth_token=None, defer_encryption=False, syncable=True):
+ auth_token=None, defer_encryption=False, syncable=True,
+ userid=None):
"""
Initialize configuration, cryptographic keys and dbs.
@@ -179,12 +181,14 @@ class Soledad(object):
"""
# store config params
self._uuid = uuid
+ self._userid = userid
self._passphrase = passphrase
self._local_db_path = local_db_path
self._server_url = server_url
self._defer_encryption = defer_encryption
self._secrets_path = None
self._sync_enc_pool = None
+ self._dbsyncer = None
self.shared_db = shared_db
@@ -215,6 +219,7 @@ class Soledad(object):
#
# initialization/destruction methods
#
+
def _init_config_with_defaults(self):
"""
Initialize configuration using default values for missing params.
@@ -250,7 +255,7 @@ class Soledad(object):
"""
self._secrets = SoledadSecrets(
self.uuid, self._passphrase, self._secrets_path,
- self.shared_db)
+ self.shared_db, userid=self._userid)
self._secrets.bootstrap()
def _init_u1db_sqlcipher_backend(self):
@@ -648,10 +653,29 @@ class Soledad(object):
def uuid(self):
return self._uuid
+ @property
+ def userid(self):
+ return self._userid
+
#
# ISyncableStorage
#
+ def set_syncable(self, syncable):
+ """
+ Toggle the syncable state for this database.
+
+ This can be used to start a database with offline state and switch it
+ online afterwards. Or the opposite: stop syncs when connection is lost.
+
+ :param syncable: new status for syncable.
+ :type syncable: bool
+ """
+ # TODO should check that we've got a token!
+ self.shared_db.syncable = syncable
+ if syncable and not self._dbsyncer:
+ self._init_u1db_syncer()
+
def sync(self, defer_decryption=True):
"""
Synchronize documents with the server replica.
@@ -718,8 +742,9 @@ class Soledad(object):
return failure
def _emit_done_data_sync(passthrough):
+ user_data = {'uuid': self.uuid, 'userid': self.userid}
soledad_events.emit_async(
- soledad_events.SOLEDAD_DONE_DATA_SYNC, self.uuid)
+ soledad_events.SOLEDAD_DONE_DATA_SYNC, user_data)
return passthrough
d.addCallbacks(_sync_callback, _sync_errback)
@@ -747,6 +772,13 @@ class Soledad(object):
"""
return self.sync_lock.locked
+ @property
+ def syncable(self):
+ if self.shared_db:
+ return self.shared_db.syncable
+ else:
+ return False
+
def _set_token(self, token):
"""
Set the authentication token for remote database access.
@@ -911,6 +943,38 @@ class Soledad(object):
"""
return self._dbpool.runOperation(*args, **kw)
+ #
+ # Service authentication
+ #
+
+ @inlineCallbacks
+ def get_or_create_service_token(self, service):
+ """
+ Return the stored token for a given service, or generates and stores a
+ random one if it does not exist.
+
+ These tokens can be used to authenticate services.
+ """
+ # FIXME this could use the local sqlcipher database, to avoid
+ # problems with different replicas creating different tokens.
+
+ yield self.create_index('by-servicetoken', 'type', 'service')
+ docs = yield self._get_token_for_service(service)
+ if docs:
+ doc = docs[0]
+ returnValue(doc.content['token'])
+ else:
+ token = str(uuid.uuid4()).replace('-', '')[-24:]
+ yield self._set_token_for_service(service, token)
+ returnValue(token)
+
+ def _get_token_for_service(self, service):
+ return self.get_from_index('by-servicetoken', 'servicetoken', service)
+
+ def _set_token_for_service(self, service, token):
+ doc = {'type': 'servicetoken', 'service': service, 'token': token}
+ return self.create_doc(doc)
+
def _convert_to_unicode(content):
"""