summaryrefslogtreecommitdiff
path: root/common/src/leap/soledad
diff options
context:
space:
mode:
authorVictor Shyba <victor.shyba@gmail.com>2015-10-26 18:50:20 -0300
committerVictor Shyba <victor.shyba@gmail.com>2015-10-28 18:13:40 -0300
commitf8d38125098829fe50199725545365d6d2a889a6 (patch)
tree9edab8dc323606e675a31eb141d1d42ca1e72c99 /common/src/leap/soledad
parent55548cf947966bcbb9a496e523a3f802b0f0b55f (diff)
[feat] read security doc from configuration
LEAP Platform needs to granularly allow access on user database for other services, like mx. This is now possible by editing soledad-server.conf file. A new section 'database-security' was added and it is parsed during 'create-user-db' to be set on security design document, present on every per-user database.
Diffstat (limited to 'common/src/leap/soledad')
-rw-r--r--common/src/leap/soledad/common/couch.py23
-rw-r--r--common/src/leap/soledad/common/tests/fixture_soledad.conf10
-rw-r--r--common/src/leap/soledad/common/tests/test_couch.py24
-rw-r--r--common/src/leap/soledad/common/tests/test_server.py36
4 files changed, 85 insertions, 8 deletions
diff --git a/common/src/leap/soledad/common/couch.py b/common/src/leap/soledad/common/couch.py
index 3dee1473..a801a044 100644
--- a/common/src/leap/soledad/common/couch.py
+++ b/common/src/leap/soledad/common/couch.py
@@ -372,7 +372,8 @@ class CouchDatabase(CommonBackend):
"""
@classmethod
- def open_database(cls, url, create, replica_uid=None, ensure_ddocs=False):
+ def open_database(cls, url, create, replica_uid=None, ensure_ddocs=False,
+ database_security=None):
"""
Open a U1DB database using CouchDB as backend.
@@ -402,9 +403,11 @@ class CouchDatabase(CommonBackend):
raise DatabaseDoesNotExist()
server.create(dbname)
return cls(
- url, dbname, replica_uid=replica_uid, ensure_ddocs=ensure_ddocs)
+ url, dbname, replica_uid=replica_uid,
+ ensure_ddocs=ensure_ddocs, database_security=database_security)
- def __init__(self, url, dbname, replica_uid=None, ensure_ddocs=False):
+ def __init__(self, url, dbname, replica_uid=None, ensure_ddocs=False,
+ database_security=None):
"""
Create a new Couch data container.
@@ -435,7 +438,7 @@ class CouchDatabase(CommonBackend):
self._set_replica_uid(replica_uid)
if ensure_ddocs:
self.ensure_ddocs_on_db()
- self.ensure_security_ddoc()
+ self.ensure_security_ddoc(database_security)
self._cache = None
@property
@@ -469,7 +472,7 @@ class CouchDatabase(CommonBackend):
getattr(ddocs, ddoc_name)))
self._database.save(ddoc)
- def ensure_security_ddoc(self):
+ def ensure_security_ddoc(self, security_config=None):
"""
Make sure that only soledad user is able to access this database as
an unprivileged member, meaning that administration access will
@@ -478,10 +481,18 @@ class CouchDatabase(CommonBackend):
to the unprivileged CouchDB user set on the server process.
This is achieved by creating a _security design document, see:
http://docs.couchdb.org/en/latest/api/database/security.html
+
+ :param database_security: security configuration parsed from conf file
+ :type cache: dict
"""
+ security_config = security_config or {}
security = self._database.resource.get_json('_security')[2]
- security['members'] = {'names': ['soledad'], 'roles': []}
+ security['members'] = {'names': [], 'roles': []}
+ security['members']['names'] = security_config.get('members', ['soledad'])
+ security['members']['roles'] = security_config.get('members_roles', [])
security['admins'] = {'names': [], 'roles': []}
+ security['admins']['names'] = security_config.get('admins', [])
+ security['admins']['roles'] = security_config.get('admins_roles', [])
self._database.resource.put_json('_security', body=security)
def get_sync_target(self):
diff --git a/common/src/leap/soledad/common/tests/fixture_soledad.conf b/common/src/leap/soledad/common/tests/fixture_soledad.conf
new file mode 100644
index 00000000..c0ffacf6
--- /dev/null
+++ b/common/src/leap/soledad/common/tests/fixture_soledad.conf
@@ -0,0 +1,10 @@
+[soledad-server]
+couch_url = http://soledad:passwd@localhost:5984
+create_cmd = sudo -u soledad-admin /usr/bin/create-user-db
+admin_netrc = /etc/couchdb/couchdb-soledad-admin.netrc
+
+[database-security]
+members = user1, user2
+members_roles = role1, role2
+admins = user3, user4
+admins_roles = role3, role3
diff --git a/common/src/leap/soledad/common/tests/test_couch.py b/common/src/leap/soledad/common/tests/test_couch.py
index b4797f5e..668a7f64 100644
--- a/common/src/leap/soledad/common/tests/test_couch.py
+++ b/common/src/leap/soledad/common/tests/test_couch.py
@@ -534,9 +534,11 @@ class CouchDatabaseSyncTargetTests(
class IndexedCouchDatabase(couch.CouchDatabase):
- def __init__(self, url, dbname, replica_uid=None, ensure_ddocs=True):
+ def __init__(self, url, dbname, replica_uid=None, ensure_ddocs=True,
+ database_security=None):
old_class.__init__(self, url, dbname, replica_uid=replica_uid,
- ensure_ddocs=ensure_ddocs)
+ ensure_ddocs=ensure_ddocs,
+ database_security=database_security)
self._indexes = {}
def _put_doc(self, old_doc, doc):
@@ -1515,6 +1517,24 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
self.assertIn('members', security_ddoc)
self.assertIn('soledad', security_ddoc['members']['names'])
+ def test_ensure_security_from_configuration(self):
+ """
+ Given a configuration, follow it to create the security document
+ """
+ self.create_db(ensure=False)
+ configuration = {'members': ['user1', 'user2'],
+ 'members_roles': ['role1', 'role2'],
+ 'admins': ['admin'],
+ 'admins_roles': ['administrators']
+ }
+ self.db.ensure_security_ddoc(configuration)
+
+ security_ddoc = self.db._database.resource.get_json('_security')[2]
+ self.assertEquals(configuration['admins'], security_ddoc['admins']['names'])
+ self.assertEquals(configuration['admins_roles'], security_ddoc['admins']['roles'])
+ self.assertEquals(configuration['members'], security_ddoc['members']['names'])
+ self.assertEquals(configuration['members_roles'], security_ddoc['members']['roles'])
+
class DatabaseNameValidationTest(unittest.TestCase):
diff --git a/common/src/leap/soledad/common/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py
index 19d2907d..14e17e67 100644
--- a/common/src/leap/soledad/common/tests/test_server.py
+++ b/common/src/leap/soledad/common/tests/test_server.py
@@ -22,10 +22,12 @@ import tempfile
import mock
import time
import binascii
+import pkg_resources
from uuid import uuid4
from urlparse import urljoin
from twisted.internet import defer
+from twisted.trial import unittest
from leap.soledad.common.couch import (
CouchServerState,
@@ -43,6 +45,8 @@ from leap.soledad.common.tests.util import (
from leap.soledad.common import crypto
from leap.soledad.client import Soledad
from leap.soledad.server import LockResource
+from leap.soledad.server import load_configuration
+from leap.soledad.server import CONFIG_DEFAULTS
from leap.soledad.server.auth import URLToAuthorization
@@ -587,3 +591,35 @@ class LockResourceTestCase(
self.assertIsNotNone(lr._shared_db.get_doc('lock-' + lock_uuid))
responder.send_response_json.assert_called_with(
401, error='unlock unauthorized')
+
+
+class ConfigurationParsingTest(unittest.TestCase):
+
+ def setUp(self):
+ self.maxDiff = None
+
+ def test_use_defaults_on_failure(self):
+ config = load_configuration('this file will never exist')
+ expected = CONFIG_DEFAULTS
+ self.assertEquals(expected, config)
+
+ def test_security_values_configuration(self):
+ # given
+ config_path = pkg_resources.resource_filename('leap.soledad.common.tests',
+ 'fixture_soledad.conf')
+ # when
+ config = load_configuration(config_path)
+
+ # then
+ expected = {'soledad-server': {
+ 'couch_url': 'http://soledad:passwd@localhost:5984',
+ 'create_cmd': 'sudo -u soledad-admin /usr/bin/create-user-db',
+ 'admin_netrc': '/etc/couchdb/couchdb-soledad-admin.netrc',
+ },
+ 'database-security': {
+ 'members': ['user1', 'user2'],
+ 'members_roles': ['role1', 'role2'],
+ 'admins': ['user3', 'user4'],
+ 'admins_roles': ['role3', 'role3'],
+ }}
+ self.assertDictEqual(expected, config)