From c51e243917c9cfd5859de9f42fcb9943a94ab096 Mon Sep 17 00:00:00 2001 From: drebs Date: Sat, 31 Aug 2013 15:47:40 -0300 Subject: Add couch permission check. --- ...ure_3501-add-verification-for-couch-permissions | 1 + common/src/leap/soledad/common/couch.py | 93 +++++++++++++++++++++- .../src/leap/soledad/common/tests/test_server.py | 3 +- 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 common/changes/feature_3501-add-verification-for-couch-permissions (limited to 'common') 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-' 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- 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): -- cgit v1.2.3