summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2013-08-31 15:47:40 -0300
committerdrebs <drebs@leap.se>2013-09-18 11:24:55 -0300
commitc51e243917c9cfd5859de9f42fcb9943a94ab096 (patch)
treefd2113fbcc70b27db2f4558d2cafa2cdd6dd51fe
parent710130799a2a843309be0306862780f0f108b5dd (diff)
Add couch permission check.
-rw-r--r--common/changes/feature_3501-add-verification-for-couch-permissions1
-rw-r--r--common/src/leap/soledad/common/couch.py93
-rw-r--r--common/src/leap/soledad/common/tests/test_server.py3
-rw-r--r--server/changes/feature_3501-add-verification-for-couch-permissions1
-rw-r--r--server/src/leap/soledad/server/__init__.py28
5 files changed, 115 insertions, 11 deletions
diff --git a/common/changes/feature_3501-add-verification-for-couch-permissions b/common/changes/feature_3501-add-verification-for-couch-permissions
new file mode 100644
index 00000000..3c2687d5
--- /dev/null
+++ b/common/changes/feature_3501-add-verification-for-couch-permissions
@@ -0,0 +1 @@
+ o Add verification for couch permissions. Closes #3501.
diff --git a/common/src/leap/soledad/common/couch.py b/common/src/leap/soledad/common/couch.py
index 9642e8f3..973f8b49 100644
--- a/common/src/leap/soledad/common/couch.py
+++ b/common/src/leap/soledad/common/couch.py
@@ -21,6 +21,8 @@
import uuid
import re
import simplejson as json
+import socket
+import logging
from base64 import b64encode, b64decode
@@ -30,7 +32,7 @@ from u1db.backends.inmemory import InMemoryIndex
from u1db.remote.server_state import ServerState
from u1db.errors import DatabaseDoesNotExist
from couchdb.client import Server, Document as CouchDocument
-from couchdb.http import ResourceNotFound
+from couchdb.http import ResourceNotFound, Unauthorized
from leap.soledad.common.objectstore import (
@@ -39,6 +41,9 @@ from leap.soledad.common.objectstore import (
)
+logger = logging.getLogger(__name__)
+
+
class InvalidURLError(Exception):
"""
Exception raised when Soledad encounters a malformed URL.
@@ -410,6 +415,15 @@ class CouchSyncTarget(ObjectStoreSyncTarget):
"""
Functionality for using a CouchDatabase as a synchronization target.
"""
+ pass
+
+
+class NotEnoughCouchPermissions(Exception):
+ """
+ Raised when failing to assert for enough permissions on underlying Couch
+ Database.
+ """
+ pass
class CouchServerState(ServerState):
@@ -417,8 +431,83 @@ class CouchServerState(ServerState):
Inteface of the WSGI server with the CouchDB backend.
"""
- def __init__(self, couch_url):
+ def __init__(self, couch_url, shared_db_name, tokens_db_name,
+ user_db_prefix):
+ """
+ Initialize the couch server state.
+
+ @param couch_url: The URL for the couch database.
+ @type couch_url: str
+ @param shared_db_name: The name of the shared database.
+ @type shared_db_name: str
+ @param tokens_db_name: The name of the tokens database.
+ @type tokens_db_name: str
+ @param user_db_prefix: The prefix for user database names.
+ @type user_db_prefix: str
+ """
self._couch_url = couch_url
+ self._shared_db_name = shared_db_name
+ self._tokens_db_name = tokens_db_name
+ self._user_db_prefix = user_db_prefix
+ try:
+ self._check_couch_permissions()
+ except NotEnoughCouchPermissions:
+ logger.error("Not enough permissions on underlying couch "
+ "database (%s)." % self._couch_url)
+ except (socket.error, socket.gaierror, socket.herror,
+ socket.timeout), e:
+ logger.error("Socket problem while trying to reach underlying "
+ "couch database: (%s, %s)." %
+ (self._couch_url, e))
+
+ def _check_couch_permissions(self):
+ """
+ Assert that Soledad Server has enough permissions on the underlying couch
+ database.
+
+ Soledad Server has to be able to do the following in the couch server:
+
+ * Create, read and write from/to 'shared' db.
+ * Create, read and write from/to 'user-<anything>' dbs.
+ * Read from 'tokens' db.
+
+ This function tries to perform the actions above using the "low level"
+ couch library to ensure that Soledad Server can do everything it needs on
+ the underlying couch database.
+
+ @param couch_url: The URL of the couch database.
+ @type couch_url: str
+
+ @raise NotEnoughCouchPermissions: Raised in case there are not enough
+ permissions to read/write/create the needed couch databases.
+ @rtype: bool
+ """
+
+ def _open_couch_db(dbname):
+ server = Server(url=self._couch_url)
+ try:
+ server[dbname]
+ except ResourceNotFound:
+ server.create(dbname)
+ return server[dbname]
+
+ def _create_delete_test_doc(db):
+ doc_id, _ = db.save({'test': 'document'})
+ doc = db[doc_id]
+ db.delete(doc)
+
+ try:
+ # test read/write auth for shared db
+ _create_delete_test_doc(
+ _open_couch_db(self._shared_db_name))
+ # test read/write auth for user-<something> db
+ _create_delete_test_doc(
+ _open_couch_db('%stest-db' % self._user_db_prefix))
+ # test read auth for tokens db
+ tokensdb = _open_couch_db(self._tokens_db_name)
+ tokensdb.info()
+ except Unauthorized:
+ raise NotEnoughCouchPermissions(self._couch_url)
def open_database(self, dbname):
"""
diff --git a/common/src/leap/soledad/common/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py
index beb7e04d..1ea4d615 100644
--- a/common/src/leap/soledad/common/tests/test_server.py
+++ b/common/src/leap/soledad/common/tests/test_server.py
@@ -310,7 +310,8 @@ class EncryptedSyncTestCase(
secret_id=secret_id)
def make_app(self):
- self.request_state = CouchServerState(self._couch_url)
+ self.request_state = CouchServerState(
+ self._couch_url, 'shared', 'tokens', 'user-')
return self.make_app_with_state(self.request_state)
def setUp(self):
diff --git a/server/changes/feature_3501-add-verification-for-couch-permissions b/server/changes/feature_3501-add-verification-for-couch-permissions
new file mode 100644
index 00000000..9206c708
--- /dev/null
+++ b/server/changes/feature_3501-add-verification-for-couch-permissions
@@ -0,0 +1 @@
+ o Verify for couch permissions when starting server. Closes #3501.
diff --git a/server/src/leap/soledad/server/__init__.py b/server/src/leap/soledad/server/__init__.py
index 67b0611d..b4b715e2 100644
--- a/server/src/leap/soledad/server/__init__.py
+++ b/server/src/leap/soledad/server/__init__.py
@@ -19,12 +19,19 @@
"""
A U1DB server that stores data using CouchDB as its persistence layer.
-This should be run with:
- twistd -n web --wsgi=leap.soledad.server.application --port=2424
+This is written as a Twisted application and intended to be run using the
+twistd command. To start the soledad server, run:
+
+ twistd -n web --wsgi=leap.soledad.server.application --port=X
+
+An initscript is included and will be installed system wide to make it
+feasible to start and stop the Soledad server service using a standard
+interface.
"""
import configparser
+
from u1db.remote import http_app
@@ -116,13 +123,18 @@ def load_configuration(file_path):
# Run as Twisted WSGI Resource
#-----------------------------------------------------------------------------
-conf = load_configuration('/etc/leap/soledad-server.conf')
-state = CouchServerState(conf['couch_url'])
-
-# WSGI application that may be used by `twistd -web`
-application = SoledadTokenAuthMiddleware(SoledadApp(state))
+def application(environ, start_response):
+ conf = load_configuration('/etc/leap/soledad-server.conf')
+ state = CouchServerState(
+ conf['couch_url'],
+ SoledadApp.SHARED_DB_NAME,
+ SoledadTokenAuthMiddleware.TOKENS_DB,
+ SoledadApp.USER_DB_PREFIX)
+ # WSGI application that may be used by `twistd -web`
+ application = SoledadTokenAuthMiddleware(SoledadApp(state))
+ resource = WSGIResource(reactor, reactor.getThreadPool(), application)
+ return application(environ, start_response)
-resource = WSGIResource(reactor, reactor.getThreadPool(), application)
from ._version import get_versions
__version__ = get_versions()['version']