summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2016-12-18 14:21:54 -0200
committerKali Kaneko <kali@leap.se>2017-02-09 17:41:34 +0100
commite73d36621052a69aae327200c063ac1689bcf9e0 (patch)
treec53a1d4ab94827147ee0f599e2d344621936cf63
parenta39af0e003ba95c9b7ab554aa4a4c5ce316a43c7 (diff)
[feat] reuse the url mapper instead of creating it for every request
-rw-r--r--common/src/leap/soledad/common/__init__.py2
-rw-r--r--server/src/leap/soledad/server/auth.py82
-rw-r--r--testing/tests/server/test_server.py251
3 files changed, 136 insertions, 199 deletions
diff --git a/common/src/leap/soledad/common/__init__.py b/common/src/leap/soledad/common/__init__.py
index 1ba6ab89..4948ad20 100644
--- a/common/src/leap/soledad/common/__init__.py
+++ b/common/src/leap/soledad/common/__init__.py
@@ -30,8 +30,6 @@ Soledad routines common to client and server.
#
SHARED_DB_NAME = 'shared'
-SHARED_DB_LOCK_DOC_ID_PREFIX = 'lock-'
-USER_DB_PREFIX = 'user-'
#
diff --git a/server/src/leap/soledad/server/auth.py b/server/src/leap/soledad/server/auth.py
index f3d9c8a8..c026a282 100644
--- a/server/src/leap/soledad/server/auth.py
+++ b/server/src/leap/soledad/server/auth.py
@@ -26,67 +26,31 @@ from routes.mapper import Mapper
from leap.soledad.common.log import getLogger
from leap.soledad.common.l2db import DBNAME_CONSTRAINTS, errors as u1db_errors
from leap.soledad.common import SHARED_DB_NAME
-from leap.soledad.common import USER_DB_PREFIX
logger = getLogger(__name__)
-class URLToAuthorization(object):
+class URLMapper(object):
"""
- Verify if actions can be performed by a user.
+ Maps the URLs users can access.
"""
- HTTP_METHOD_GET = 'GET'
- HTTP_METHOD_PUT = 'PUT'
- HTTP_METHOD_DELETE = 'DELETE'
- HTTP_METHOD_POST = 'POST'
-
- def __init__(self, uuid):
- """
- Initialize the mapper.
-
- The C{uuid} is used to create the rules that will either allow or
- disallow the user to perform specific actions.
-
- @param uuid: The user uuid.
- @type uuid: str
- @param user_db_prefix: The string prefix of users' databases.
- @type user_db_prefix: str
- """
+ def __init__(self):
self._map = Mapper(controller_scan=None)
- self._user_db_name = "%s%s" % (USER_DB_PREFIX, uuid)
- self._uuid = uuid
- self._register_auth_info()
-
- def is_authorized(self, environ):
- """
- Return whether an HTTP request that produced the CGI C{environ}
- corresponds to an authorized action.
-
- @param environ: Dictionary containing CGI variables.
- @type environ: dict
-
- @return: Whether the action is authorized or not.
- @rtype: bool
- """
- return self._map.match(environ=environ) is not None
+ self._connect_urls()
+ self._map.create_regs()
- def _register(self, pattern, http_methods):
- """
- Register a C{pattern} in the mapper as valid for C{http_methods}.
+ def match(self, environ):
+ return self._map.match(environ=environ)
- @param pattern: The URL pattern that corresponds to the user action.
- @type pattern: str
- @param http_methods: A list of authorized HTTP methods.
- @type http_methods: list of str
- """
+ def _connect(self, pattern, http_methods):
self._map.connect(
None, pattern, http_methods=http_methods,
conditions=dict(method=http_methods),
requirements={'dbname': DBNAME_CONSTRAINTS})
- def _register_auth_info(self):
+ def _connect_urls(self):
"""
Register the authorization info in the mapper using C{SHARED_DB_NAME}
as the user's database name.
@@ -106,21 +70,15 @@ class URLToAuthorization(object):
/user-db/sync-from/{source} | GET, PUT, POST
"""
# auth info for global resource
- self._register('/', [self.HTTP_METHOD_GET])
+ self._connect('/', ['GET'])
# auth info for shared-db database resource
- self._register('/%s' % SHARED_DB_NAME, [self.HTTP_METHOD_GET])
+ self._connect('/%s' % SHARED_DB_NAME, ['GET'])
# auth info for shared-db doc resource
- self._register(
- '/%s/doc/{id:.*}' % SHARED_DB_NAME,
- [self.HTTP_METHOD_GET, self.HTTP_METHOD_PUT,
- self.HTTP_METHOD_DELETE])
+ self._connect('/%s/doc/{id:.*}' % SHARED_DB_NAME,
+ ['GET', 'PUT', 'DELETE'])
# auth info for user-db sync resource
- self._register(
- '/%s/sync-from/{source_replica_uid}' % self._user_db_name,
- [self.HTTP_METHOD_GET, self.HTTP_METHOD_PUT,
- self.HTTP_METHOD_POST])
- # generate the regular expressions
- self._map.create_regs()
+ self._connect('/user-{uuid}/sync-from/{source_replica_uid}',
+ ['GET', 'PUT', 'POST'])
class SoledadAuthMiddleware(object):
@@ -176,6 +134,7 @@ class SoledadAuthMiddleware(object):
@type prefix: str
"""
self._app = app
+ self._mapper = URLMapper()
def _error(self, start_response, status, description, message=None):
"""
@@ -310,14 +269,17 @@ class SoledadAuthMiddleware(object):
@param environ: Dictionary containing CGI variables.
@type environ: dict
- @param uuid: The user's uuid.
+ @param uuid: The user's uuid from the Authorization header.
@type uuid: str
- @return: Whether the user is authorize to perform the requested action
+ @return: Whether the user is authorized to perform the requested action
over the requested db.
@rtype: bool
"""
- return URLToAuthorization(uuid).is_authorized(environ)
+ match = self._mapper.match(environ)
+ if not match:
+ return False
+ return uuid == match.get('uuid')
@abstractmethod
def _get_auth_error_string(self):
diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py
index cae2e75c..09242736 100644
--- a/testing/tests/server/test_server.py
+++ b/testing/tests/server/test_server.py
@@ -45,7 +45,7 @@ from leap.soledad.client import _crypto
from leap.soledad.client import Soledad
from leap.soledad.server.config import load_configuration
from leap.soledad.server.config import CONFIG_DEFAULTS
-from leap.soledad.server.auth import URLToAuthorization
+from leap.soledad.server.auth import URLMapper
from leap.soledad.server.auth import SoledadTokenAuthMiddleware
@@ -116,175 +116,152 @@ class ServerAuthorizationTestCase(BaseSoledadTest):
/user-db/sync-from/{source} | GET, PUT, POST
"""
uuid = uuid4().hex
- authmap = URLToAuthorization(uuid,)
- dbname = authmap._user_db_name
+ urlmap = URLMapper()
+ dbname = 'user-%s' % uuid
+
# test global auth
- self.assertTrue(
- authmap.is_authorized(self._make_environ('/', 'GET')))
+ match = urlmap.match(self._make_environ('/', 'GET'))
+
# test shared-db database resource auth
- self.assertTrue(
- authmap.is_authorized(
- self._make_environ('/shared', 'GET')))
- self.assertFalse(
- authmap.is_authorized(
+ match = urlmap.match(
+ self._make_environ('/shared', 'GET'))
+ self.assertIsNotNone(match)
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared', 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared', 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared', 'POST')))
+
# test shared-db docs resource auth
- self.assertFalse(
- authmap.is_authorized(
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/docs', 'GET')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/docs', 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/docs', 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/docs', 'POST')))
+
# test shared-db doc resource auth
- self.assertTrue(
- authmap.is_authorized(
- self._make_environ('/shared/doc/x', 'GET')))
- self.assertTrue(
- authmap.is_authorized(
- self._make_environ('/shared/doc/x', 'PUT')))
- self.assertTrue(
- authmap.is_authorized(
- self._make_environ('/shared/doc/x', 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
+ match = urlmap.match(
+ self._make_environ('/shared/doc/x', 'GET'))
+ self.assertIsNotNone(match)
+ self.assertEqual('x', match.get('id'))
+
+ match = urlmap.match(
+ self._make_environ('/shared/doc/x', 'PUT'))
+ self.assertIsNotNone(match)
+ self.assertEqual('x', match.get('id'))
+
+ match = urlmap.match(
+ self._make_environ('/shared/doc/x', 'DELETE'))
+ self.assertEqual('x', match.get('id'))
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/doc/x', 'POST')))
+
# test shared-db sync resource auth
- self.assertFalse(
- authmap.is_authorized(
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/sync-from/x', 'GET')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/sync-from/x', 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/sync-from/x', 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/shared/sync-from/x', 'POST')))
+
# test user-db database resource auth
- self.assertFalse(
- authmap.is_authorized(
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s' % dbname, 'GET')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s' % dbname, 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s' % dbname, 'POST')))
+
# test user-db docs resource auth
- self.assertFalse(
- authmap.is_authorized(
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/docs' % dbname, 'GET')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/docs' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/docs' % dbname, 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/docs' % dbname, 'POST')))
+
# test user-db doc resource auth
- self.assertFalse(
- authmap.is_authorized(
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/doc/x' % dbname, 'GET')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/doc/x' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/doc/x' % dbname, 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
+
+ self.assertIsNone(
+ urlmap.match(
self._make_environ('/%s/doc/x' % dbname, 'POST')))
+
# test user-db sync resource auth
- self.assertTrue(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'GET')))
- self.assertTrue(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'DELETE')))
- self.assertTrue(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'POST')))
-
- def test_verify_action_with_wrong_dbnames(self):
- """
- Test if authorization fails for a wrong dbname.
- """
- uuid = uuid4().hex
- authmap = URLToAuthorization(uuid)
- dbname = 'somedb'
- # test wrong-db database resource auth
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s' % dbname, 'GET')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s' % dbname, 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s' % dbname, 'POST')))
- # test wrong-db docs resource auth
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/docs' % dbname, 'GET')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/docs' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/docs' % dbname, 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/docs' % dbname, 'POST')))
- # test wrong-db doc resource auth
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/doc/x' % dbname, 'GET')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/doc/x' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/doc/x' % dbname, 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/doc/x' % dbname, 'POST')))
- # test wrong-db sync resource auth
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'GET')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'PUT')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'DELETE')))
- self.assertFalse(
- authmap.is_authorized(
- self._make_environ('/%s/sync-from/x' % dbname, 'POST')))
+ match = urlmap.match(
+ self._make_environ('/%s/sync-from/x' % dbname, 'GET'))
+ self.assertEqual(uuid, match.get('uuid'))
+ self.assertEqual('x', match.get('source_replica_uid'))
+
+ match = urlmap.match(
+ self._make_environ('/%s/sync-from/x' % dbname, 'PUT'))
+ self.assertEqual(uuid, match.get('uuid'))
+ self.assertEqual('x', match.get('source_replica_uid'))
+
+ match = urlmap.match(
+ self._make_environ('/%s/sync-from/x' % dbname, 'DELETE'))
+ self.assertIsNone(match)
+
+ match = urlmap.match(
+ self._make_environ('/%s/sync-from/x' % dbname, 'POST'))
+ self.assertEqual(uuid, match.get('uuid'))
+ self.assertEqual('x', match.get('source_replica_uid'))
@pytest.mark.usefixtures("method_tmpdir")