From a39af0e003ba95c9b7ab554aa4a4c5ce316a43c7 Mon Sep 17 00:00:00 2001 From: drebs Date: Sun, 18 Dec 2016 12:56:21 -0200 Subject: [bug] disallow all requests to "user-{uuid}/" --- testing/tests/server/test_server.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index 6710caaf..cae2e75c 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -110,7 +110,7 @@ class ServerAuthorizationTestCase(BaseSoledadTest): /shared-db/docs | - /shared-db/doc/{id} | GET, PUT, DELETE /shared-db/sync-from/{source} | - - /user-db | GET, PUT, DELETE + /user-db | - /user-db/docs | - /user-db/doc/{id} | - /user-db/sync-from/{source} | GET, PUT, POST @@ -174,13 +174,13 @@ class ServerAuthorizationTestCase(BaseSoledadTest): authmap.is_authorized( self._make_environ('/shared/sync-from/x', 'POST'))) # test user-db database resource auth - self.assertTrue( + self.assertFalse( authmap.is_authorized( self._make_environ('/%s' % dbname, 'GET'))) - self.assertTrue( + self.assertFalse( authmap.is_authorized( self._make_environ('/%s' % dbname, 'PUT'))) - self.assertTrue( + self.assertFalse( authmap.is_authorized( self._make_environ('/%s' % dbname, 'DELETE'))) self.assertFalse( -- cgit v1.2.3 From e73d36621052a69aae327200c063ac1689bcf9e0 Mon Sep 17 00:00:00 2001 From: drebs Date: Sun, 18 Dec 2016 14:21:54 -0200 Subject: [feat] reuse the url mapper instead of creating it for every request --- testing/tests/server/test_server.py | 251 ++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 137 deletions(-) (limited to 'testing/tests/server') 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") -- cgit v1.2.3 From 260805b9967184841c4499f94713a9a48c49a813 Mon Sep 17 00:00:00 2001 From: drebs Date: Sun, 18 Dec 2016 16:36:39 -0200 Subject: [feat] use twisted web http auth and creds --- testing/tests/server/test_server.py | 164 ++++++++---------------------------- 1 file changed, 37 insertions(+), 127 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index 09242736..12f6fb20 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -18,11 +18,9 @@ Tests for server-related functionality. """ import binascii -import mock import os import pytest -from hashlib import sha512 from pkg_resources import resource_filename from urlparse import urljoin from uuid import uuid4 @@ -46,36 +44,6 @@ 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 URLMapper -from leap.soledad.server.auth import SoledadTokenAuthMiddleware - - -class ServerAuthenticationMiddlewareTestCase(CouchDBTestCase): - - def setUp(self): - super(ServerAuthenticationMiddlewareTestCase, self).setUp() - app = mock.Mock() - self._state = CouchServerState(self.couch_url) - app.state = self._state - self.auth_middleware = SoledadTokenAuthMiddleware(app) - self._authorize('valid-uuid', 'valid-token') - - def _authorize(self, uuid, token): - token_doc = {} - token_doc['_id'] = sha512(token).hexdigest() - token_doc[self._state.TOKENS_USER_ID_KEY] = uuid - token_doc[self._state.TOKENS_TYPE_KEY] = \ - self._state.TOKENS_TYPE_DEF - dbname = self._state._tokens_dbname() - db = self.couch_server.create(dbname) - db.save(token_doc) - self.addCleanup(self.delete_db, db.name) - - def test_authorized_user(self): - is_authorized = self.auth_middleware._verify_authentication_data - self.assertTrue(is_authorized('valid-uuid', 'valid-token')) - self.assertFalse(is_authorized('valid-uuid', 'invalid-token')) - self.assertFalse(is_authorized('invalid-uuid', 'valid-token')) - self.assertFalse(is_authorized('eve', 'invalid-token')) class ServerAuthorizationTestCase(BaseSoledadTest): @@ -90,12 +58,6 @@ class ServerAuthorizationTestCase(BaseSoledadTest): def tearDown(self): pass - def _make_environ(self, path_info, request_method): - return { - 'PATH_INFO': path_info, - 'REQUEST_METHOD': request_method, - } - def test_verify_action_with_correct_dbnames(self): """ Test encrypting and decrypting documents. @@ -120,146 +82,94 @@ class ServerAuthorizationTestCase(BaseSoledadTest): dbname = 'user-%s' % uuid # test global auth - match = urlmap.match(self._make_environ('/', 'GET')) + match = urlmap.match('/', 'GET') + self.assertIsNotNone(match) # test shared-db database resource auth - match = urlmap.match( - self._make_environ('/shared', 'GET')) + match = urlmap.match('/shared', 'GET') self.assertIsNotNone(match) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared', 'PUT'))) + match = urlmap.match('/shared', 'PUT') + self.assertIsNone(match) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared', 'DELETE'))) + match = urlmap.match('/shared', 'DELETE') + self.assertIsNone(match) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared', 'POST'))) + match = urlmap.match('/shared', 'POST') + self.assertIsNone(match) # test shared-db docs resource auth - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/docs', 'GET'))) + self.assertIsNone(urlmap.match('/shared/docs', 'GET')) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/docs', 'PUT'))) + self.assertIsNone(urlmap.match('/shared/docs', 'PUT')) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/docs', 'DELETE'))) + self.assertIsNone(urlmap.match('/shared/docs', 'DELETE')) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/docs', 'POST'))) + self.assertIsNone(urlmap.match('/shared/docs', 'POST')) # test shared-db doc resource auth - match = urlmap.match( - self._make_environ('/shared/doc/x', 'GET')) + match = urlmap.match('/shared/doc/x', 'GET') self.assertIsNotNone(match) self.assertEqual('x', match.get('id')) - match = urlmap.match( - self._make_environ('/shared/doc/x', 'PUT')) + match = urlmap.match('/shared/doc/x', 'PUT') self.assertIsNotNone(match) self.assertEqual('x', match.get('id')) - match = urlmap.match( - self._make_environ('/shared/doc/x', 'DELETE')) + match = urlmap.match('/shared/doc/x', 'DELETE') self.assertEqual('x', match.get('id')) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/doc/x', 'POST'))) + self.assertIsNone(urlmap.match('/shared/doc/x', 'POST')) # test shared-db sync resource auth - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/sync-from/x', 'GET'))) + self.assertIsNone(urlmap.match('/shared/sync-from/x', 'GET')) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/sync-from/x', 'PUT'))) + self.assertIsNone(urlmap.match('/shared/sync-from/x', 'PUT')) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/sync-from/x', 'DELETE'))) + self.assertIsNone(urlmap.match('/shared/sync-from/x', 'DELETE')) - self.assertIsNone( - urlmap.match( - self._make_environ('/shared/sync-from/x', 'POST'))) + self.assertIsNone(urlmap.match('/shared/sync-from/x', 'POST')) # test user-db database resource auth - self.assertIsNone( - urlmap.match( - self._make_environ('/%s' % dbname, 'GET'))) + self.assertIsNone(urlmap.match('/%s' % dbname, 'GET')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s' % dbname, 'PUT'))) + self.assertIsNone(urlmap.match('/%s' % dbname, 'PUT')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s' % dbname, 'DELETE'))) + self.assertIsNone(urlmap.match('/%s' % dbname, 'DELETE')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s' % dbname, 'POST'))) + self.assertIsNone(urlmap.match('/%s' % dbname, 'POST')) # test user-db docs resource auth - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/docs' % dbname, 'GET'))) + self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'GET')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/docs' % dbname, 'PUT'))) + self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'PUT')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/docs' % dbname, 'DELETE'))) + self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'DELETE')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/docs' % dbname, 'POST'))) + self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'POST')) # test user-db doc resource auth - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/doc/x' % dbname, 'GET'))) + self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'GET')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/doc/x' % dbname, 'PUT'))) + self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'PUT')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/doc/x' % dbname, 'DELETE'))) + self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'DELETE')) - self.assertIsNone( - urlmap.match( - self._make_environ('/%s/doc/x' % dbname, 'POST'))) + self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'POST')) # test user-db sync resource auth - match = urlmap.match( - self._make_environ('/%s/sync-from/x' % dbname, 'GET')) + match = urlmap.match('/%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')) + match = urlmap.match('/%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')) + match = urlmap.match('/%s/sync-from/x' % dbname, 'DELETE') self.assertIsNone(match) - match = urlmap.match( - self._make_environ('/%s/sync-from/x' % dbname, 'POST')) + match = urlmap.match('/%s/sync-from/x' % dbname, 'POST') self.assertEqual(uuid, match.get('uuid')) self.assertEqual('x', match.get('source_replica_uid')) -- cgit v1.2.3 From 6043f7966b64d6922987bca9137a524fb06a3379 Mon Sep 17 00:00:00 2001 From: drebs Date: Mon, 19 Dec 2016 09:43:03 -0200 Subject: [refactor] separate url mapper, avoid hanging tests Because the wsgi resource has its own threadpool, tests might get confused when shutting down and the reactor may get clogged waiting for the threadpool to be stopped. By refactoring the URLMapper to its own module, server tests can avoid loading the resource module, where the wsgi threadpool resides, so the threapool will not be started. --- testing/tests/server/test_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index 12f6fb20..39d8e8c3 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -43,7 +43,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 URLMapper +from leap.soledad.server.url_mapper import URLMapper class ServerAuthorizationTestCase(BaseSoledadTest): -- cgit v1.2.3 From 5058cae83227d4ba1b6390aa52a63b22a1acb11d Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 22 Dec 2016 07:56:01 -0200 Subject: [test] split url mapper test in many smaller tests --- testing/tests/server/test_server.py | 180 ++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 101 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index 39d8e8c3..da69c423 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -36,7 +36,6 @@ from test_soledad.util import ( make_token_soledad_app, make_soledad_document_for_test, soledad_sync_target, - BaseSoledadTest, ) from leap.soledad.client import _crypto @@ -46,133 +45,112 @@ from leap.soledad.server.config import CONFIG_DEFAULTS from leap.soledad.server.url_mapper import URLMapper -class ServerAuthorizationTestCase(BaseSoledadTest): - +class URLMapperTestCase(unittest.TestCase): """ - Tests related to Soledad server authorization. + Test if the URLMapper behaves as expected. + + The following table lists the authorized actions among all possible + u1db remote actions: + + URL path | Authorized actions + -------------------------------------------------- + / | GET + /shared-db | GET + /shared-db/docs | - + /shared-db/doc/{id} | - + /shared-db/sync-from/{source} | - + /user-db | - + /user-db/docs | - + /user-db/doc/{id} | - + /user-db/sync-from/{source} | GET, PUT, POST """ def setUp(self): - pass - - def tearDown(self): - pass - - def test_verify_action_with_correct_dbnames(self): - """ - Test encrypting and decrypting documents. - - The following table lists the authorized actions among all possible - u1db remote actions: - - URL path | Authorized actions - -------------------------------------------------- - / | GET - /shared-db | GET - /shared-db/docs | - - /shared-db/doc/{id} | GET, PUT, DELETE - /shared-db/sync-from/{source} | - - /user-db | - - /user-db/docs | - - /user-db/doc/{id} | - - /user-db/sync-from/{source} | GET, PUT, POST - """ - uuid = uuid4().hex - urlmap = URLMapper() - dbname = 'user-%s' % uuid - - # test global auth - match = urlmap.match('/', 'GET') - self.assertIsNotNone(match) + self._uuid = uuid4().hex + self._urlmap = URLMapper() + self._dbname = 'user-%s' % self._uuid - # test shared-db database resource auth - match = urlmap.match('/shared', 'GET') + def test_root_authorized(self): + match = self._urlmap.match('/', 'GET') self.assertIsNotNone(match) - match = urlmap.match('/shared', 'PUT') - self.assertIsNone(match) + def test_shared_authorized(self): + self.assertIsNotNone(self._urlmap.match('/shared', 'GET')) - match = urlmap.match('/shared', 'DELETE') - self.assertIsNone(match) + def test_shared_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared', 'PUT')) + self.assertIsNone(self._urlmap.match('/shared', 'DELETE')) + self.assertIsNone(self._urlmap.match('/shared', 'POST')) - match = urlmap.match('/shared', 'POST') - self.assertIsNone(match) + def test_shared_docs_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared/docs', 'GET')) + self.assertIsNone(self._urlmap.match('/shared/docs', 'PUT')) + self.assertIsNone(self._urlmap.match('/shared/docs', 'DELETE')) + self.assertIsNone(self._urlmap.match('/shared/docs', 'POST')) - # test shared-db docs resource auth - self.assertIsNone(urlmap.match('/shared/docs', 'GET')) - - self.assertIsNone(urlmap.match('/shared/docs', 'PUT')) - - self.assertIsNone(urlmap.match('/shared/docs', 'DELETE')) - - self.assertIsNone(urlmap.match('/shared/docs', 'POST')) - - # test shared-db doc resource auth - match = urlmap.match('/shared/doc/x', 'GET') + def test_shared_doc_authorized(self): + match = self._urlmap.match('/shared/doc/x', 'GET') self.assertIsNotNone(match) self.assertEqual('x', match.get('id')) - match = urlmap.match('/shared/doc/x', 'PUT') + match = self._urlmap.match('/shared/doc/x', 'PUT') self.assertIsNotNone(match) self.assertEqual('x', match.get('id')) - match = urlmap.match('/shared/doc/x', 'DELETE') + match = self._urlmap.match('/shared/doc/x', 'DELETE') + self.assertIsNotNone(match) self.assertEqual('x', match.get('id')) - self.assertIsNone(urlmap.match('/shared/doc/x', 'POST')) - - # test shared-db sync resource auth - self.assertIsNone(urlmap.match('/shared/sync-from/x', 'GET')) - - self.assertIsNone(urlmap.match('/shared/sync-from/x', 'PUT')) - - self.assertIsNone(urlmap.match('/shared/sync-from/x', 'DELETE')) - - self.assertIsNone(urlmap.match('/shared/sync-from/x', 'POST')) - - # test user-db database resource auth - self.assertIsNone(urlmap.match('/%s' % dbname, 'GET')) - - self.assertIsNone(urlmap.match('/%s' % dbname, 'PUT')) - - self.assertIsNone(urlmap.match('/%s' % dbname, 'DELETE')) - - self.assertIsNone(urlmap.match('/%s' % dbname, 'POST')) - - # test user-db docs resource auth - self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'GET')) - - self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'PUT')) - - self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'DELETE')) - - self.assertIsNone(urlmap.match('/%s/docs' % dbname, 'POST')) - - # test user-db doc resource auth - self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'GET')) - - self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'PUT')) - - self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'DELETE')) - - self.assertIsNone(urlmap.match('/%s/doc/x' % dbname, 'POST')) - - # test user-db sync resource auth - match = urlmap.match('/%s/sync-from/x' % dbname, 'GET') + def test_shared_doc_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared/doc/x', 'POST')) + + def test_shared_sync_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'GET')) + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'PUT')) + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'DELETE')) + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'POST')) + + def test_user_db_unauthorized(self): + dbname = self._dbname + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'GET')) + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'PUT')) + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'DELETE')) + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'POST')) + + def test_user_db_docs_unauthorized(self): + dbname = self._dbname + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'GET')) + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'PUT')) + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'DELETE')) + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'POST')) + + def test_user_db_doc_unauthorized(self): + dbname = self._dbname + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'GET')) + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'PUT')) + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'DELETE')) + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'POST')) + + def test_user_db_sync_authorized(self): + uuid = self._uuid + dbname = self._dbname + match = self._urlmap.match('/%s/sync-from/x' % dbname, 'GET') self.assertEqual(uuid, match.get('uuid')) self.assertEqual('x', match.get('source_replica_uid')) - match = urlmap.match('/%s/sync-from/x' % dbname, 'PUT') + match = self._urlmap.match('/%s/sync-from/x' % dbname, 'PUT') self.assertEqual(uuid, match.get('uuid')) self.assertEqual('x', match.get('source_replica_uid')) - match = urlmap.match('/%s/sync-from/x' % dbname, 'DELETE') - self.assertIsNone(match) - - match = urlmap.match('/%s/sync-from/x' % dbname, 'POST') + match = self._urlmap.match('/%s/sync-from/x' % dbname, 'POST') self.assertEqual(uuid, match.get('uuid')) self.assertEqual('x', match.get('source_replica_uid')) + def test_user_db_sync_unauthorized(self): + dbname = self._dbname + self.assertIsNone( + self._urlmap.match('/%s/sync-from/x' % dbname, 'DELETE')) + @pytest.mark.usefixtures("method_tmpdir") class EncryptedSyncTestCase( -- cgit v1.2.3 From b3e0af399b81b65d6665865d1e1b18aa589f5824 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 31 Jan 2017 23:51:42 -0200 Subject: [test] add tests for server auth --- testing/tests/server/test_auth.py | 92 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 testing/tests/server/test_auth.py (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py new file mode 100644 index 00000000..3ff738f2 --- /dev/null +++ b/testing/tests/server/test_auth.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +# test_auth.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for auth pieces. +""" +import collections + +from twisted.cred.credentials import UsernamePassword +from twisted.cred.error import UnauthorizedLogin +from twisted.internet.defer import inlineCallbacks +from twisted.trial import unittest +from twisted.web.resource import IResource + +from leap.soledad.server.auth import SoledadRealm +from leap.soledad.server.auth import TokenChecker +from leap.soledad.server.auth import TokenCredentialFactory +from leap.soledad.server.resource import SoledadResource + + +class SoledadRealmTestCase(unittest.TestCase): + + def test_returned_resource(self): + realm = SoledadRealm() + iface, avatar, logout = realm.requestAvatar('any', None, IResource) + self.assertIsInstance(avatar, SoledadResource) + self.assertIsNone(logout()) + + +class DummyServer(object): + """ + I fake the `couchdb.client.Server` GET api and always return the token + given on my creation. + """ + + def __init__(self, token): + self._token = token + + def get(self, _): + return self._token + + +class TokenCheckerTestCase(unittest.TestCase): + + @inlineCallbacks + def test_good_creds(self): + # set up a dummy server which always return a *valid* token document + token = {'user_id': 'user', 'type': 'Token'} + server = collections.defaultdict(lambda: DummyServer(token)) + # setup the checker with the custom server + checker = TokenChecker(server=server) + # assert the checker *can* verify the creds + creds = UsernamePassword('user', 'pass') + avatarId = yield checker.requestAvatarId(creds) + self.assertEqual('user', avatarId) + + @inlineCallbacks + def test_bad_creds(self): + # set up a dummy server which always return an *invalid* token document + token = None + server = collections.defaultdict(lambda: DummyServer(token)) + # setup the checker with the custom server + checker = TokenChecker(server=server) + # assert the checker *cannot* verify the creds + creds = UsernamePassword('user', '') + with self.assertRaises(UnauthorizedLogin): + yield checker.requestAvatarId(creds) + + +from twisted.web.test import test_httpauth + + +class TokenCredentialFactoryTestcase( + test_httpauth.RequestMixin, test_httpauth.BasicAuthTestsMixin, + unittest.TestCase): + + def setUp(self): + test_httpauth.BasicAuthTestsMixin.setUp(self) + self.credentialFactory = TokenCredentialFactory() -- cgit v1.2.3 From 911695e59ab60d2abaef3013330a6d41283cc733 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 1 Feb 2017 18:16:43 -0200 Subject: [test] add tests for server auth session --- testing/tests/server/test_auth.py | 6 +- testing/tests/server/test_session.py | 209 +++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 testing/tests/server/test_session.py (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py index 3ff738f2..0e6baba3 100644 --- a/testing/tests/server/test_auth.py +++ b/testing/tests/server/test_auth.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # test_auth.py -# Copyright (C) 2013 LEAP +# Copyright (C) 2017 LEAP # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ from twisted.cred.error import UnauthorizedLogin from twisted.internet.defer import inlineCallbacks from twisted.trial import unittest from twisted.web.resource import IResource +from twisted.web.test import test_httpauth from leap.soledad.server.auth import SoledadRealm from leap.soledad.server.auth import TokenChecker @@ -80,9 +81,6 @@ class TokenCheckerTestCase(unittest.TestCase): yield checker.requestAvatarId(creds) -from twisted.web.test import test_httpauth - - class TokenCredentialFactoryTestcase( test_httpauth.RequestMixin, test_httpauth.BasicAuthTestsMixin, unittest.TestCase): diff --git a/testing/tests/server/test_session.py b/testing/tests/server/test_session.py new file mode 100644 index 00000000..7883ef4a --- /dev/null +++ b/testing/tests/server/test_session.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- +# test_session.py +# Copyright (C) 2017 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for server session entrypoint. +""" +from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse +from twisted.cred import portal +from twisted.web.test.test_httpauth import b64encode +from twisted.web.test.test_httpauth import Realm +from twisted.web.test.requesthelper import DummyRequest +from twisted.web.resource import getChildForRequest + +from twisted.web.resource import Resource + +from twisted.trial import unittest + +from leap.soledad.server.session import SoledadSession + +from twisted.web.static import Data +from twisted.web._auth.wrapper import UnauthorizedResource +from twisted.cred.credentials import IUsernamePassword +from twisted.cred.checkers import ANONYMOUS, AllowAnonymousAccess + + +class SoledadSessionTestCase(unittest.TestCase): + """ + Tests adapted from for + L{twisted.web.test.test_httpauth.HTTPAuthSessionWrapper}. + """ + + def makeRequest(self, *args, **kwargs): + request = DummyRequest(*args, **kwargs) + request.path = '/' + return request + + def setUp(self): + self.username = b'foo bar' + self.password = b'bar baz' + self.avatarContent = b"contents of the avatar resource itself" + self.childName = b"foo-child" + self.childContent = b"contents of the foo child of the avatar" + self.checker = InMemoryUsernamePasswordDatabaseDontUse() + self.checker.addUser(self.username, self.password) + self.avatar = Data(self.avatarContent, 'text/plain') + self.avatar.putChild( + self.childName, Data(self.childContent, 'text/plain')) + self.avatars = {self.username: self.avatar} + self.realm = Realm(self.avatars.get) + self.portal = portal.Portal(self.realm, [self.checker]) + self.wrapper = SoledadSession(self.portal) + + def _authorizedTokenLogin(self, request): + authorization = b64encode( + self.username + b':' + self.password) + request.requestHeaders.addRawHeader(b'authorization', + b'Token ' + authorization) + return getChildForRequest(self.wrapper, request) + + def test_getChildWithDefault(self): + request = self.makeRequest([self.childName]) + child = getChildForRequest(self.wrapper, request) + d = request.notifyFinish() + + def cbFinished(result): + self.assertEqual(request.responseCode, 401) + + d.addCallback(cbFinished) + request.render(child) + return d + + def _invalidAuthorizationTest(self, response): + request = self.makeRequest([self.childName]) + request.requestHeaders.addRawHeader(b'authorization', response) + child = getChildForRequest(self.wrapper, request) + d = request.notifyFinish() + + def cbFinished(result): + self.assertEqual(request.responseCode, 401) + + d.addCallback(cbFinished) + request.render(child) + return d + + def test_getChildWithDefaultUnauthorizedUser(self): + return self._invalidAuthorizationTest( + b'Basic ' + b64encode(b'foo:bar')) + + def test_getChildWithDefaultUnauthorizedPassword(self): + return self._invalidAuthorizationTest( + b'Basic ' + b64encode(self.username + b':bar')) + + def test_getChildWithDefaultUnrecognizedScheme(self): + return self._invalidAuthorizationTest(b'Quux foo bar baz') + + def test_getChildWithDefaultAuthorized(self): + request = self.makeRequest([self.childName]) + child = self._authorizedTokenLogin(request) + d = request.notifyFinish() + + def cbFinished(ignored): + self.assertEqual(request.written, [self.childContent]) + + d.addCallback(cbFinished) + request.render(child) + return d + + def test_renderAuthorized(self): + # Request it exactly, not any of its children. + request = self.makeRequest([]) + child = self._authorizedTokenLogin(request) + d = request.notifyFinish() + + def cbFinished(ignored): + self.assertEqual(request.written, [self.avatarContent]) + + d.addCallback(cbFinished) + request.render(child) + return d + + def test_decodeRaises(self): + request = self.makeRequest([self.childName]) + request.requestHeaders.addRawHeader(b'authorization', + b'Token decode should fail') + child = getChildForRequest(self.wrapper, request) + self.assertIsInstance(child, UnauthorizedResource) + + def test_parseResponse(self): + basicAuthorization = b'Basic abcdef123456' + self.assertEqual( + self.wrapper._parseHeader(basicAuthorization), + None) + tokenAuthorization = b'Token abcdef123456' + self.assertEqual( + self.wrapper._parseHeader(tokenAuthorization), + b'abcdef123456') + + def test_unexpectedDecodeError(self): + + class UnexpectedException(Exception): + pass + + class BadFactory(object): + scheme = b'bad' + + def getChallenge(self, client): + return {} + + def decode(self, response, request): + raise UnexpectedException() + + self.wrapper._credentialFactory = BadFactory() + request = self.makeRequest([self.childName]) + request.requestHeaders.addRawHeader(b'authorization', b'Bad abc') + child = getChildForRequest(self.wrapper, request) + request.render(child) + self.assertEqual(request.responseCode, 500) + + def test_unexpectedLoginError(self): + class UnexpectedException(Exception): + pass + + class BrokenChecker(object): + credentialInterfaces = (IUsernamePassword,) + + def requestAvatarId(self, credentials): + raise UnexpectedException() + + self.portal.registerChecker(BrokenChecker()) + request = self.makeRequest([self.childName]) + child = self._authorizedTokenLogin(request) + request.render(child) + self.assertEqual(request.responseCode, 500) + + def test_anonymousAccess(self): + """ + Anonymous requests are allowed if a L{Portal} has an anonymous checker + registered. + """ + unprotectedContents = b"contents of the unprotected child resource" + + self.avatars[ANONYMOUS] = Resource() + self.avatars[ANONYMOUS].putChild( + self.childName, Data(unprotectedContents, 'text/plain')) + self.portal.registerChecker(AllowAnonymousAccess()) + + request = self.makeRequest([self.childName]) + child = getChildForRequest(self.wrapper, request) + d = request.notifyFinish() + + def cbFinished(ignored): + self.assertEqual(request.written, [unprotectedContents]) + + d.addCallback(cbFinished) + request.render(child) + return d -- cgit v1.2.3 From c9cb1a814b6bfaa40de3c35a590f39d5fb0ce18e Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 1 Feb 2017 19:44:03 -0200 Subject: [test] fix session and auth tests --- testing/tests/server/test_auth.py | 11 +++++++-- testing/tests/server/test_session.py | 45 ++++++++---------------------------- 2 files changed, 19 insertions(+), 37 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py index 0e6baba3..5b215650 100644 --- a/testing/tests/server/test_auth.py +++ b/testing/tests/server/test_auth.py @@ -19,6 +19,8 @@ Tests for auth pieces. """ import collections +from contextlib import contextmanager + from twisted.cred.credentials import UsernamePassword from twisted.cred.error import UnauthorizedLogin from twisted.internet.defer import inlineCallbacks @@ -54,13 +56,18 @@ class DummyServer(object): return self._token +@contextmanager +def dummy_server(token): + yield collections.defaultdict(lambda: DummyServer(token)) + + class TokenCheckerTestCase(unittest.TestCase): @inlineCallbacks def test_good_creds(self): # set up a dummy server which always return a *valid* token document token = {'user_id': 'user', 'type': 'Token'} - server = collections.defaultdict(lambda: DummyServer(token)) + server = dummy_server(token) # setup the checker with the custom server checker = TokenChecker(server=server) # assert the checker *can* verify the creds @@ -72,7 +79,7 @@ class TokenCheckerTestCase(unittest.TestCase): def test_bad_creds(self): # set up a dummy server which always return an *invalid* token document token = None - server = collections.defaultdict(lambda: DummyServer(token)) + server = dummy_server(token) # setup the checker with the custom server checker = TokenChecker(server=server) # assert the checker *cannot* verify the creds diff --git a/testing/tests/server/test_session.py b/testing/tests/server/test_session.py index 7883ef4a..8131ddb3 100644 --- a/testing/tests/server/test_session.py +++ b/testing/tests/server/test_session.py @@ -17,24 +17,20 @@ """ Tests for server session entrypoint. """ -from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse +from twisted.trial import unittest + from twisted.cred import portal +from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse +from twisted.cred.credentials import IUsernamePassword +from twisted.web.resource import getChildForRequest +from twisted.web.static import Data +from twisted.web.test.requesthelper import DummyRequest from twisted.web.test.test_httpauth import b64encode from twisted.web.test.test_httpauth import Realm -from twisted.web.test.requesthelper import DummyRequest -from twisted.web.resource import getChildForRequest - -from twisted.web.resource import Resource - -from twisted.trial import unittest +from twisted.web._auth.wrapper import UnauthorizedResource from leap.soledad.server.session import SoledadSession -from twisted.web.static import Data -from twisted.web._auth.wrapper import UnauthorizedResource -from twisted.cred.credentials import IUsernamePassword -from twisted.cred.checkers import ANONYMOUS, AllowAnonymousAccess - class SoledadSessionTestCase(unittest.TestCase): """ @@ -168,6 +164,7 @@ class SoledadSessionTestCase(unittest.TestCase): child = getChildForRequest(self.wrapper, request) request.render(child) self.assertEqual(request.responseCode, 500) + self.assertEqual(len(self.flushLoggedErrors(UnexpectedException)), 1) def test_unexpectedLoginError(self): class UnexpectedException(Exception): @@ -184,26 +181,4 @@ class SoledadSessionTestCase(unittest.TestCase): child = self._authorizedTokenLogin(request) request.render(child) self.assertEqual(request.responseCode, 500) - - def test_anonymousAccess(self): - """ - Anonymous requests are allowed if a L{Portal} has an anonymous checker - registered. - """ - unprotectedContents = b"contents of the unprotected child resource" - - self.avatars[ANONYMOUS] = Resource() - self.avatars[ANONYMOUS].putChild( - self.childName, Data(unprotectedContents, 'text/plain')) - self.portal.registerChecker(AllowAnonymousAccess()) - - request = self.makeRequest([self.childName]) - child = getChildForRequest(self.wrapper, request) - d = request.notifyFinish() - - def cbFinished(ignored): - self.assertEqual(request.written, [unprotectedContents]) - - d.addCallback(cbFinished) - request.render(child) - return d + self.assertEqual(len(self.flushLoggedErrors(UnexpectedException)), 1) -- cgit v1.2.3 From 47c357213b4e39c6ced818de7eeb401e52bf92b9 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 26 Jan 2017 18:37:45 -0200 Subject: [refactor] move wsgi sync setup to its own module Conflicts: server/src/leap/soledad/server/_wsgi.py server/src/leap/soledad/server/entrypoint.py server/src/leap/soledad/server/resource.py testing/tests/server/test__resource.py --- testing/tests/server/test_auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py index 5b215650..8ef35177 100644 --- a/testing/tests/server/test_auth.py +++ b/testing/tests/server/test_auth.py @@ -31,7 +31,7 @@ from twisted.web.test import test_httpauth from leap.soledad.server.auth import SoledadRealm from leap.soledad.server.auth import TokenChecker from leap.soledad.server.auth import TokenCredentialFactory -from leap.soledad.server.resource import SoledadResource +from leap.soledad.server._resource import SoledadResource class SoledadRealmTestCase(unittest.TestCase): -- cgit v1.2.3 From 47858d88ca4ca10ac363c71550b1bafe50f8f4ce Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 26 Jan 2017 19:18:15 -0200 Subject: [refactor] allow passing threadpool pool for server sync resource Conflicts: server/src/leap/soledad/server/_resource.py testing/tests/server/test__resource.py --- testing/tests/server/test_auth.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py index 8ef35177..f7fe0f25 100644 --- a/testing/tests/server/test_auth.py +++ b/testing/tests/server/test_auth.py @@ -23,6 +23,7 @@ from contextlib import contextmanager from twisted.cred.credentials import UsernamePassword from twisted.cred.error import UnauthorizedLogin +from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks from twisted.trial import unittest from twisted.web.resource import IResource @@ -37,7 +38,9 @@ from leap.soledad.server._resource import SoledadResource class SoledadRealmTestCase(unittest.TestCase): def test_returned_resource(self): - realm = SoledadRealm() + # we have to pass a pool to the realm , otherwise tests will hang + pool = reactor.getThreadPool() + realm = SoledadRealm(sync_pool=pool) iface, avatar, logout = realm.requestAvatar('any', None, IResource) self.assertIsInstance(avatar, SoledadResource) self.assertIsNone(logout()) -- cgit v1.2.3 From 0e12cd3eb1e20bb867f34e0bf60f280d93b6182d Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 18 Jan 2017 17:28:01 -0200 Subject: [feature] add server config option for blobs --- testing/tests/server/test_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index da69c423..866d9eab 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -379,5 +379,6 @@ class ConfigurationParsingTest(unittest.TestCase): 'sudo -u soledad-admin /usr/bin/create-user-db', 'admin_netrc': '/etc/couchdb/couchdb-soledad-admin.netrc', - 'batching': False} + 'batching': False, + 'blobs': False} self.assertDictEqual(expected, config['soledad-server']) -- cgit v1.2.3 From e05e9566c55e20833a2c45fe2ca67e60df009aac Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 2 Feb 2017 12:15:45 -0200 Subject: [test] move server auth tests to its own file --- testing/tests/server/test_config.py | 68 +++++++++++++++++++++++++++++++++++++ testing/tests/server/test_server.py | 46 ------------------------- 2 files changed, 68 insertions(+), 46 deletions(-) create mode 100644 testing/tests/server/test_config.py (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_config.py b/testing/tests/server/test_config.py new file mode 100644 index 00000000..1241472b --- /dev/null +++ b/testing/tests/server/test_config.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# test_config.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for server configuration. +""" + +from twisted.trial import unittest +from pkg_resources import resource_filename + +from leap.soledad.server._config import _load_config +from leap.soledad.server._config import CONFIG_DEFAULTS + + +class ConfigurationParsingTest(unittest.TestCase): + + def setUp(self): + self.maxDiff = None + + def test_use_defaults_on_failure(self): + config = _load_config('this file will never exist') + expected = CONFIG_DEFAULTS + self.assertEquals(expected, config) + + def test_security_values_configuration(self): + # given + config_path = resource_filename('test_soledad', + 'fixture_soledad.conf') + # when + config = _load_config(config_path) + + # then + expected = {'members': ['user1', 'user2'], + 'members_roles': ['role1', 'role2'], + 'admins': ['user3', 'user4'], + 'admins_roles': ['role3', 'role3']} + self.assertDictEqual(expected, config['database-security']) + + def test_server_values_configuration(self): + # given + config_path = resource_filename('test_soledad', + 'fixture_soledad.conf') + # when + config = _load_config(config_path) + + # then + expected = {'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', + 'batching': False, + 'blobs': False} + self.assertDictEqual(expected, config['soledad-server']) diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index 866d9eab..ec0fc31d 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -21,7 +21,6 @@ import binascii import os import pytest -from pkg_resources import resource_filename from urlparse import urljoin from uuid import uuid4 @@ -40,8 +39,6 @@ from test_soledad.util import ( 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.url_mapper import URLMapper @@ -339,46 +336,3 @@ class EncryptedSyncTestCase( Test if Soledad can sync many smallfiles. """ return self._test_encrypted_sym_sync(doc_size=2, number_of_docs=100) - - -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 = resource_filename('test_soledad', - 'fixture_soledad.conf') - # when - config = load_configuration(config_path) - - # then - expected = {'members': ['user1', 'user2'], - 'members_roles': ['role1', 'role2'], - 'admins': ['user3', 'user4'], - 'admins_roles': ['role3', 'role3']} - self.assertDictEqual(expected, config['database-security']) - - def test_server_values_configuration(self): - # given - config_path = resource_filename('test_soledad', - 'fixture_soledad.conf') - # when - config = load_configuration(config_path) - - # then - expected = {'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', - 'batching': False, - 'blobs': False} - self.assertDictEqual(expected, config['soledad-server']) -- cgit v1.2.3 From 30c47eea2b10f10204b5d61ecb550edd24e59067 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 26 Jan 2017 08:24:39 -0200 Subject: [test] add tests for server resource and server info --- testing/tests/server/test__resource.py | 66 +++++++++++++++++++++++++++++++ testing/tests/server/test__server_info.py | 39 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 testing/tests/server/test__resource.py create mode 100644 testing/tests/server/test__server_info.py (limited to 'testing/tests/server') diff --git a/testing/tests/server/test__resource.py b/testing/tests/server/test__resource.py new file mode 100644 index 00000000..0ea46d6a --- /dev/null +++ b/testing/tests/server/test__resource.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# test__resource.py +# Copyright (C) 2017 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for Soledad server main resource. +""" +from twisted.trial import unittest +from twisted.web.test.test_web import DummyRequest +from twisted.web.error import Error +from twisted.web.wsgi import WSGIResource + +from leap.soledad.server._resource import SoledadResource +from leap.soledad.server._server_info import ServerInfo +from leap.soledad.server._blobs import BlobsResource + + +conf_blobs_false = {'soledad-server': {'blobs': False}} + + +class SoledadResourceTestCase(unittest.TestCase): + + def test_get_root(self): + conf = {'soledad-server': {'blobs': None}} # doesn't matter + resource = SoledadResource(conf) + path = '' + request = DummyRequest([]) + child = resource.getChild(path, request) + self.assertIsInstance(child, ServerInfo) + + def test_get_blobs_enabled(self): + conf = {'soledad-server': {'blobs': True}} + resource = SoledadResource(conf) + path = 'blobs' + request = DummyRequest([]) + child = resource.getChild(path, request) + self.assertIsInstance(child, BlobsResource) + + def test_get_blobs_disabled(self): + conf = {'soledad-server': {'blobs': False}} + resource = SoledadResource(conf) + path = 'blobs' + request = DummyRequest([]) + with self.assertRaises(Error): + resource.getChild(path, request) + + def test_get_sync(self): + conf = {'soledad-server': {'blobs': None}} # doesn't matter + resource = SoledadResource(conf) + path = 'sync' # if not 'blobs' or '', should be routed to sync + request = DummyRequest([]) + request.prepath = ['user-db'] + child = resource.getChild(path, request) + self.assertIsInstance(child, WSGIResource) diff --git a/testing/tests/server/test__server_info.py b/testing/tests/server/test__server_info.py new file mode 100644 index 00000000..80770721 --- /dev/null +++ b/testing/tests/server/test__server_info.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# test__server_info.py +# Copyright (C) 2017 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Tests for Soledad server information announcement. +""" +import json + +from twisted.trial import unittest +from twisted.web.test.test_web import DummyRequest + +from leap.soledad.server._server_info import ServerInfo + + +class ServerInfoTestCase(unittest.TestCase): + + def test_blobs_enabled(self): + resource = ServerInfo(True) + response = resource.render(DummyRequest([''])) + self.assertEquals(json.loads(response), {'blobs': True}) + + def test_blobs_disabled(self): + resource = ServerInfo(False) + response = resource.render(DummyRequest([''])) + self.assertEquals(json.loads(response), {'blobs': False}) -- cgit v1.2.3 From 7432b72471fa3de03d13b9d35f6004d14deae63d Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 26 Jan 2017 18:37:45 -0200 Subject: [refactor] move wsgi sync setup to its own module --- testing/tests/server/test__resource.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test__resource.py b/testing/tests/server/test__resource.py index 0ea46d6a..b5285195 100644 --- a/testing/tests/server/test__resource.py +++ b/testing/tests/server/test__resource.py @@ -25,6 +25,7 @@ from twisted.web.wsgi import WSGIResource from leap.soledad.server._resource import SoledadResource from leap.soledad.server._server_info import ServerInfo from leap.soledad.server._blobs import BlobsResource +from leap.soledad.server.gzip_middleware import GzipMiddleware conf_blobs_false = {'soledad-server': {'blobs': False}} @@ -64,3 +65,4 @@ class SoledadResourceTestCase(unittest.TestCase): request.prepath = ['user-db'] child = resource.getChild(path, request) self.assertIsInstance(child, WSGIResource) + self.assertIsInstance(child._application, GzipMiddleware) -- cgit v1.2.3 From 1f1a9847117d23a767e99fe80484baf232375d36 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 26 Jan 2017 19:18:15 -0200 Subject: [refactor] allow passing threadpool pool for server sync resource --- testing/tests/server/test__resource.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test__resource.py b/testing/tests/server/test__resource.py index b5285195..2a387416 100644 --- a/testing/tests/server/test__resource.py +++ b/testing/tests/server/test__resource.py @@ -21,6 +21,7 @@ from twisted.trial import unittest from twisted.web.test.test_web import DummyRequest from twisted.web.error import Error from twisted.web.wsgi import WSGIResource +from twisted.internet import reactor from leap.soledad.server._resource import SoledadResource from leap.soledad.server._server_info import ServerInfo @@ -28,14 +29,14 @@ from leap.soledad.server._blobs import BlobsResource from leap.soledad.server.gzip_middleware import GzipMiddleware -conf_blobs_false = {'soledad-server': {'blobs': False}} +_pool = reactor.getThreadPool() class SoledadResourceTestCase(unittest.TestCase): def test_get_root(self): conf = {'soledad-server': {'blobs': None}} # doesn't matter - resource = SoledadResource(conf) + resource = SoledadResource(conf, sync_pool=_pool) path = '' request = DummyRequest([]) child = resource.getChild(path, request) @@ -43,7 +44,7 @@ class SoledadResourceTestCase(unittest.TestCase): def test_get_blobs_enabled(self): conf = {'soledad-server': {'blobs': True}} - resource = SoledadResource(conf) + resource = SoledadResource(conf, sync_pool=_pool) path = 'blobs' request = DummyRequest([]) child = resource.getChild(path, request) @@ -51,7 +52,7 @@ class SoledadResourceTestCase(unittest.TestCase): def test_get_blobs_disabled(self): conf = {'soledad-server': {'blobs': False}} - resource = SoledadResource(conf) + resource = SoledadResource(conf, sync_pool=_pool) path = 'blobs' request = DummyRequest([]) with self.assertRaises(Error): @@ -59,7 +60,7 @@ class SoledadResourceTestCase(unittest.TestCase): def test_get_sync(self): conf = {'soledad-server': {'blobs': None}} # doesn't matter - resource = SoledadResource(conf) + resource = SoledadResource(conf, sync_pool=_pool) path = 'sync' # if not 'blobs' or '', should be routed to sync request = DummyRequest([]) request.prepath = ['user-db'] -- cgit v1.2.3 From 53b5a6788ad8416f78b24cc9880d02da73c52d70 Mon Sep 17 00:00:00 2001 From: drebs Date: Fri, 27 Jan 2017 20:30:02 -0200 Subject: [refacor] make proper use of twisted web dyamic resources in server --- testing/tests/server/test__resource.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test__resource.py b/testing/tests/server/test__resource.py index 2a387416..30ef782d 100644 --- a/testing/tests/server/test__resource.py +++ b/testing/tests/server/test__resource.py @@ -19,8 +19,8 @@ Tests for Soledad server main resource. """ from twisted.trial import unittest from twisted.web.test.test_web import DummyRequest -from twisted.web.error import Error from twisted.web.wsgi import WSGIResource +from twisted.web.resource import getChildForRequest from twisted.internet import reactor from leap.soledad.server._resource import SoledadResource @@ -37,33 +37,30 @@ class SoledadResourceTestCase(unittest.TestCase): def test_get_root(self): conf = {'soledad-server': {'blobs': None}} # doesn't matter resource = SoledadResource(conf, sync_pool=_pool) - path = '' - request = DummyRequest([]) - child = resource.getChild(path, request) + request = DummyRequest(['']) + child = getChildForRequest(resource, request) self.assertIsInstance(child, ServerInfo) def test_get_blobs_enabled(self): conf = {'soledad-server': {'blobs': True}} resource = SoledadResource(conf, sync_pool=_pool) - path = 'blobs' - request = DummyRequest([]) - child = resource.getChild(path, request) + request = DummyRequest(['blobs']) + child = getChildForRequest(resource, request) self.assertIsInstance(child, BlobsResource) def test_get_blobs_disabled(self): conf = {'soledad-server': {'blobs': False}} resource = SoledadResource(conf, sync_pool=_pool) - path = 'blobs' - request = DummyRequest([]) - with self.assertRaises(Error): - resource.getChild(path, request) + request = DummyRequest(['blobs']) + child = getChildForRequest(resource, request) + # if blobs is disabled, the request should be routed to sync + self.assertIsInstance(child, WSGIResource) + self.assertIsInstance(child._application, GzipMiddleware) def test_get_sync(self): conf = {'soledad-server': {'blobs': None}} # doesn't matter resource = SoledadResource(conf, sync_pool=_pool) - path = 'sync' # if not 'blobs' or '', should be routed to sync - request = DummyRequest([]) - request.prepath = ['user-db'] - child = resource.getChild(path, request) + request = DummyRequest(['user-db', 'sync-from', 'source-id']) + child = getChildForRequest(resource, request) self.assertIsInstance(child, WSGIResource) self.assertIsInstance(child._application, GzipMiddleware) -- cgit v1.2.3 From a2f041de7f1ea653f078ac9cd532e2d2b774248f Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 2 Feb 2017 11:45:57 -0200 Subject: [refactor] parametrize blobs toggling in soledad server resource --- testing/tests/server/test__resource.py | 16 ++++++++-------- testing/tests/server/test_auth.py | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test__resource.py b/testing/tests/server/test__resource.py index 30ef782d..c066435e 100644 --- a/testing/tests/server/test__resource.py +++ b/testing/tests/server/test__resource.py @@ -35,22 +35,22 @@ _pool = reactor.getThreadPool() class SoledadResourceTestCase(unittest.TestCase): def test_get_root(self): - conf = {'soledad-server': {'blobs': None}} # doesn't matter - resource = SoledadResource(conf, sync_pool=_pool) + enable_blobs = None # doesn't matter + resource = SoledadResource(enable_blobs=enable_blobs, sync_pool=_pool) request = DummyRequest(['']) child = getChildForRequest(resource, request) self.assertIsInstance(child, ServerInfo) def test_get_blobs_enabled(self): - conf = {'soledad-server': {'blobs': True}} - resource = SoledadResource(conf, sync_pool=_pool) + enable_blobs = True + resource = SoledadResource(enable_blobs=enable_blobs, sync_pool=_pool) request = DummyRequest(['blobs']) child = getChildForRequest(resource, request) self.assertIsInstance(child, BlobsResource) def test_get_blobs_disabled(self): - conf = {'soledad-server': {'blobs': False}} - resource = SoledadResource(conf, sync_pool=_pool) + enable_blobs = False + resource = SoledadResource(enable_blobs=enable_blobs, sync_pool=_pool) request = DummyRequest(['blobs']) child = getChildForRequest(resource, request) # if blobs is disabled, the request should be routed to sync @@ -58,8 +58,8 @@ class SoledadResourceTestCase(unittest.TestCase): self.assertIsInstance(child._application, GzipMiddleware) def test_get_sync(self): - conf = {'soledad-server': {'blobs': None}} # doesn't matter - resource = SoledadResource(conf, sync_pool=_pool) + enable_blobs = None # doesn't matter + resource = SoledadResource(enable_blobs=enable_blobs, sync_pool=_pool) request = DummyRequest(['user-db', 'sync-from', 'source-id']) child = getChildForRequest(resource, request) self.assertIsInstance(child, WSGIResource) diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py index f7fe0f25..00d416d5 100644 --- a/testing/tests/server/test_auth.py +++ b/testing/tests/server/test_auth.py @@ -39,8 +39,9 @@ class SoledadRealmTestCase(unittest.TestCase): def test_returned_resource(self): # we have to pass a pool to the realm , otherwise tests will hang + conf = {'soledad-server': {'blobs': False}} pool = reactor.getThreadPool() - realm = SoledadRealm(sync_pool=pool) + realm = SoledadRealm(conf=conf, sync_pool=pool) iface, avatar, logout = realm.requestAvatar('any', None, IResource) self.assertIsInstance(avatar, SoledadResource) self.assertIsNone(logout()) -- cgit v1.2.3 From 4ae57257fa2d40ceeba1558d995d3514e6f6d6fa Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 2 Feb 2017 12:28:19 -0200 Subject: [test] move server url mapper tests to its own file --- testing/tests/server/test_config.py | 2 +- testing/tests/server/test_server.py | 109 -------------------------- testing/tests/server/test_url_mapper.py | 131 ++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 110 deletions(-) create mode 100644 testing/tests/server/test_url_mapper.py (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_config.py b/testing/tests/server/test_config.py index 1241472b..133057f5 100644 --- a/testing/tests/server/test_config.py +++ b/testing/tests/server/test_config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # test_config.py -# Copyright (C) 2013 LEAP +# Copyright (C) 2017 LEAP # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index ec0fc31d..647ef5a8 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -25,7 +25,6 @@ from urlparse import urljoin from uuid import uuid4 from twisted.internet import defer -from twisted.trial import unittest from leap.soledad.common.couch.state import CouchServerState from leap.soledad.common.couch import CouchDatabase @@ -39,114 +38,6 @@ from test_soledad.util import ( from leap.soledad.client import _crypto from leap.soledad.client import Soledad -from leap.soledad.server.url_mapper import URLMapper - - -class URLMapperTestCase(unittest.TestCase): - """ - Test if the URLMapper behaves as expected. - - The following table lists the authorized actions among all possible - u1db remote actions: - - URL path | Authorized actions - -------------------------------------------------- - / | GET - /shared-db | GET - /shared-db/docs | - - /shared-db/doc/{id} | - - /shared-db/sync-from/{source} | - - /user-db | - - /user-db/docs | - - /user-db/doc/{id} | - - /user-db/sync-from/{source} | GET, PUT, POST - """ - - def setUp(self): - self._uuid = uuid4().hex - self._urlmap = URLMapper() - self._dbname = 'user-%s' % self._uuid - - def test_root_authorized(self): - match = self._urlmap.match('/', 'GET') - self.assertIsNotNone(match) - - def test_shared_authorized(self): - self.assertIsNotNone(self._urlmap.match('/shared', 'GET')) - - def test_shared_unauthorized(self): - self.assertIsNone(self._urlmap.match('/shared', 'PUT')) - self.assertIsNone(self._urlmap.match('/shared', 'DELETE')) - self.assertIsNone(self._urlmap.match('/shared', 'POST')) - - def test_shared_docs_unauthorized(self): - self.assertIsNone(self._urlmap.match('/shared/docs', 'GET')) - self.assertIsNone(self._urlmap.match('/shared/docs', 'PUT')) - self.assertIsNone(self._urlmap.match('/shared/docs', 'DELETE')) - self.assertIsNone(self._urlmap.match('/shared/docs', 'POST')) - - def test_shared_doc_authorized(self): - match = self._urlmap.match('/shared/doc/x', 'GET') - self.assertIsNotNone(match) - self.assertEqual('x', match.get('id')) - - match = self._urlmap.match('/shared/doc/x', 'PUT') - self.assertIsNotNone(match) - self.assertEqual('x', match.get('id')) - - match = self._urlmap.match('/shared/doc/x', 'DELETE') - self.assertIsNotNone(match) - self.assertEqual('x', match.get('id')) - - def test_shared_doc_unauthorized(self): - self.assertIsNone(self._urlmap.match('/shared/doc/x', 'POST')) - - def test_shared_sync_unauthorized(self): - self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'GET')) - self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'PUT')) - self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'DELETE')) - self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'POST')) - - def test_user_db_unauthorized(self): - dbname = self._dbname - self.assertIsNone(self._urlmap.match('/%s' % dbname, 'GET')) - self.assertIsNone(self._urlmap.match('/%s' % dbname, 'PUT')) - self.assertIsNone(self._urlmap.match('/%s' % dbname, 'DELETE')) - self.assertIsNone(self._urlmap.match('/%s' % dbname, 'POST')) - - def test_user_db_docs_unauthorized(self): - dbname = self._dbname - self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'GET')) - self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'PUT')) - self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'DELETE')) - self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'POST')) - - def test_user_db_doc_unauthorized(self): - dbname = self._dbname - self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'GET')) - self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'PUT')) - self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'DELETE')) - self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'POST')) - - def test_user_db_sync_authorized(self): - uuid = self._uuid - dbname = self._dbname - match = self._urlmap.match('/%s/sync-from/x' % dbname, 'GET') - self.assertEqual(uuid, match.get('uuid')) - self.assertEqual('x', match.get('source_replica_uid')) - - match = self._urlmap.match('/%s/sync-from/x' % dbname, 'PUT') - self.assertEqual(uuid, match.get('uuid')) - self.assertEqual('x', match.get('source_replica_uid')) - - match = self._urlmap.match('/%s/sync-from/x' % dbname, 'POST') - self.assertEqual(uuid, match.get('uuid')) - self.assertEqual('x', match.get('source_replica_uid')) - - def test_user_db_sync_unauthorized(self): - dbname = self._dbname - self.assertIsNone( - self._urlmap.match('/%s/sync-from/x' % dbname, 'DELETE')) @pytest.mark.usefixtures("method_tmpdir") diff --git a/testing/tests/server/test_url_mapper.py b/testing/tests/server/test_url_mapper.py new file mode 100644 index 00000000..fa99cae7 --- /dev/null +++ b/testing/tests/server/test_url_mapper.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# test_url_mapper.py +# Copyright (C) 2017 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for server-related functionality. +""" + +from twisted.trial import unittest +from uuid import uuid4 + +from leap.soledad.server.url_mapper import URLMapper + + +class URLMapperTestCase(unittest.TestCase): + """ + Test if the URLMapper behaves as expected. + + The following table lists the authorized actions among all possible + u1db remote actions: + + URL path | Authorized actions + -------------------------------------------------- + / | GET + /shared-db | GET + /shared-db/docs | - + /shared-db/doc/{id} | - + /shared-db/sync-from/{source} | - + /user-db | - + /user-db/docs | - + /user-db/doc/{id} | - + /user-db/sync-from/{source} | GET, PUT, POST + """ + + def setUp(self): + self._uuid = uuid4().hex + self._urlmap = URLMapper() + self._dbname = 'user-%s' % self._uuid + + def test_root_authorized(self): + match = self._urlmap.match('/', 'GET') + self.assertIsNotNone(match) + + def test_shared_authorized(self): + self.assertIsNotNone(self._urlmap.match('/shared', 'GET')) + + def test_shared_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared', 'PUT')) + self.assertIsNone(self._urlmap.match('/shared', 'DELETE')) + self.assertIsNone(self._urlmap.match('/shared', 'POST')) + + def test_shared_docs_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared/docs', 'GET')) + self.assertIsNone(self._urlmap.match('/shared/docs', 'PUT')) + self.assertIsNone(self._urlmap.match('/shared/docs', 'DELETE')) + self.assertIsNone(self._urlmap.match('/shared/docs', 'POST')) + + def test_shared_doc_authorized(self): + match = self._urlmap.match('/shared/doc/x', 'GET') + self.assertIsNotNone(match) + self.assertEqual('x', match.get('id')) + + match = self._urlmap.match('/shared/doc/x', 'PUT') + self.assertIsNotNone(match) + self.assertEqual('x', match.get('id')) + + match = self._urlmap.match('/shared/doc/x', 'DELETE') + self.assertIsNotNone(match) + self.assertEqual('x', match.get('id')) + + def test_shared_doc_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared/doc/x', 'POST')) + + def test_shared_sync_unauthorized(self): + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'GET')) + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'PUT')) + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'DELETE')) + self.assertIsNone(self._urlmap.match('/shared/sync-from/x', 'POST')) + + def test_user_db_unauthorized(self): + dbname = self._dbname + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'GET')) + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'PUT')) + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'DELETE')) + self.assertIsNone(self._urlmap.match('/%s' % dbname, 'POST')) + + def test_user_db_docs_unauthorized(self): + dbname = self._dbname + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'GET')) + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'PUT')) + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'DELETE')) + self.assertIsNone(self._urlmap.match('/%s/docs' % dbname, 'POST')) + + def test_user_db_doc_unauthorized(self): + dbname = self._dbname + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'GET')) + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'PUT')) + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'DELETE')) + self.assertIsNone(self._urlmap.match('/%s/doc/x' % dbname, 'POST')) + + def test_user_db_sync_authorized(self): + uuid = self._uuid + dbname = self._dbname + match = self._urlmap.match('/%s/sync-from/x' % dbname, 'GET') + self.assertEqual(uuid, match.get('uuid')) + self.assertEqual('x', match.get('source_replica_uid')) + + match = self._urlmap.match('/%s/sync-from/x' % dbname, 'PUT') + self.assertEqual(uuid, match.get('uuid')) + self.assertEqual('x', match.get('source_replica_uid')) + + match = self._urlmap.match('/%s/sync-from/x' % dbname, 'POST') + self.assertEqual(uuid, match.get('uuid')) + self.assertEqual('x', match.get('source_replica_uid')) + + def test_user_db_sync_unauthorized(self): + dbname = self._dbname + self.assertIsNone( + self._urlmap.match('/%s/sync-from/x' % dbname, 'DELETE')) -- cgit v1.2.3 From 3fb32423b75898f4eb0812c8927dd7f06839e756 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Sun, 12 Feb 2017 23:14:55 -0300 Subject: [tests] server parameter is gone, patch the module --- testing/tests/server/test_auth.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py index 00d416d5..c9b941f9 100644 --- a/testing/tests/server/test_auth.py +++ b/testing/tests/server/test_auth.py @@ -29,6 +29,7 @@ from twisted.trial import unittest from twisted.web.resource import IResource from twisted.web.test import test_httpauth +import leap.soledad.server.auth as auth_module from leap.soledad.server.auth import SoledadRealm from leap.soledad.server.auth import TokenChecker from leap.soledad.server.auth import TokenCredentialFactory @@ -73,7 +74,8 @@ class TokenCheckerTestCase(unittest.TestCase): token = {'user_id': 'user', 'type': 'Token'} server = dummy_server(token) # setup the checker with the custom server - checker = TokenChecker(server=server) + checker = TokenChecker() + auth_module.couch_server = lambda url: server # assert the checker *can* verify the creds creds = UsernamePassword('user', 'pass') avatarId = yield checker.requestAvatarId(creds) @@ -85,7 +87,8 @@ class TokenCheckerTestCase(unittest.TestCase): token = None server = dummy_server(token) # setup the checker with the custom server - checker = TokenChecker(server=server) + checker = TokenChecker() + auth_module.couch_server = lambda url: server # assert the checker *cannot* verify the creds creds = UsernamePassword('user', '') with self.assertRaises(UnauthorizedLogin): -- cgit v1.2.3 From 45c58edc99009fc0f2ac044a08a74fd742326a58 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Sun, 12 Feb 2017 23:15:30 -0300 Subject: [tests] conf format changed, no more nesting --- testing/tests/server/test_auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_auth.py b/testing/tests/server/test_auth.py index c9b941f9..6eb647ee 100644 --- a/testing/tests/server/test_auth.py +++ b/testing/tests/server/test_auth.py @@ -40,7 +40,7 @@ class SoledadRealmTestCase(unittest.TestCase): def test_returned_resource(self): # we have to pass a pool to the realm , otherwise tests will hang - conf = {'soledad-server': {'blobs': False}} + conf = {'blobs': False} pool = reactor.getThreadPool() realm = SoledadRealm(conf=conf, sync_pool=pool) iface, avatar, logout = realm.requestAvatar('any', None, IResource) -- cgit v1.2.3 From 6d7dd39fb3d4f138595f885d19315008d13f8907 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 15 Feb 2017 01:53:01 +0100 Subject: [tests] fix tests --- testing/tests/server/test__server_info.py | 8 ++++++-- testing/tests/server/test_session.py | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test__server_info.py b/testing/tests/server/test__server_info.py index 80770721..40567ef1 100644 --- a/testing/tests/server/test__server_info.py +++ b/testing/tests/server/test__server_info.py @@ -31,9 +31,13 @@ class ServerInfoTestCase(unittest.TestCase): def test_blobs_enabled(self): resource = ServerInfo(True) response = resource.render(DummyRequest([''])) - self.assertEquals(json.loads(response), {'blobs': True}) + _info = json.loads(response) + self.assertEquals(_info['blobs'], True) + self.assertTrue(isinstance(_info['version'], basestring)) def test_blobs_disabled(self): resource = ServerInfo(False) response = resource.render(DummyRequest([''])) - self.assertEquals(json.loads(response), {'blobs': False}) + _info = json.loads(response) + self.assertEquals(_info['blobs'], False) + self.assertTrue(isinstance(_info['version'], basestring)) diff --git a/testing/tests/server/test_session.py b/testing/tests/server/test_session.py index 8131ddb3..b00ab858 100644 --- a/testing/tests/server/test_session.py +++ b/testing/tests/server/test_session.py @@ -156,6 +156,7 @@ class SoledadSessionTestCase(unittest.TestCase): return {} def decode(self, response, request): + print "decode raised" raise UnexpectedException() self.wrapper._credentialFactory = BadFactory() @@ -164,7 +165,9 @@ class SoledadSessionTestCase(unittest.TestCase): child = getChildForRequest(self.wrapper, request) request.render(child) self.assertEqual(request.responseCode, 500) - self.assertEqual(len(self.flushLoggedErrors(UnexpectedException)), 1) + #self.assertEqual(len(self.flushLoggedErrors(UnexpectedException)), 1) + errors = self.flushLoggedErrors(UnexpectedException) + self.assertEqual(len(errors), 1) def test_unexpectedLoginError(self): class UnexpectedException(Exception): -- cgit v1.2.3 From 13fdd28cd7448b11a35c794e69e5c64e1c9cd154 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Wed, 15 Feb 2017 05:17:43 -0300 Subject: [style] pep8 --- testing/tests/server/test_session.py | 1 - 1 file changed, 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_session.py b/testing/tests/server/test_session.py index b00ab858..ebb94476 100644 --- a/testing/tests/server/test_session.py +++ b/testing/tests/server/test_session.py @@ -165,7 +165,6 @@ class SoledadSessionTestCase(unittest.TestCase): child = getChildForRequest(self.wrapper, request) request.render(child) self.assertEqual(request.responseCode, 500) - #self.assertEqual(len(self.flushLoggedErrors(UnexpectedException)), 1) errors = self.flushLoggedErrors(UnexpectedException) self.assertEqual(len(errors), 1) -- cgit v1.2.3 From b433c1ed736f5d4c19da4cdb21108a02459ca7fd Mon Sep 17 00:00:00 2001 From: drebs Date: Sat, 25 Feb 2017 08:53:38 -0300 Subject: [refactor] pass soledad object to client secrets api In order to be able to change passphrase, token and offline status of soledad from the bitmask client api, the secrets api also has to be able to use up-to-date values when encrypting/decrypting secrets and uploading/downloading them to the server. This commit makes public some soledad attributes that were previously "private" (i.e. used to start with "_" and were not meant to be accessed from outside), and passes the whole soledad object to the client secrets api. This makes the code cleaner and also allows for always getting newest values of soledad attributes. --- testing/tests/server/test_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_server.py b/testing/tests/server/test_server.py index 647ef5a8..4a5ec43f 100644 --- a/testing/tests/server/test_server.py +++ b/testing/tests/server/test_server.py @@ -135,7 +135,7 @@ class EncryptedSyncTestCase( user=user, prefix='x', auth_token='auth-token', - secrets_path=sol1._secrets_path, + secrets_path=sol1.secrets_path, passphrase=passphrase) # ensure remote db exists before syncing -- cgit v1.2.3 From 7eb1ffa1d49a7e0016c5980da71151e715abc77a Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 21 Feb 2017 14:16:26 -0300 Subject: [feat] add configurable blobs path in server - Resolves: #8777 --- testing/tests/server/test_config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'testing/tests/server') diff --git a/testing/tests/server/test_config.py b/testing/tests/server/test_config.py index 133057f5..d2a8a9de 100644 --- a/testing/tests/server/test_config.py +++ b/testing/tests/server/test_config.py @@ -64,5 +64,6 @@ class ConfigurationParsingTest(unittest.TestCase): 'admin_netrc': '/etc/couchdb/couchdb-soledad-admin.netrc', 'batching': False, - 'blobs': False} + 'blobs': False, + 'blobs_path': '/srv/leap/soledad/blobs'} self.assertDictEqual(expected, config['soledad-server']) -- cgit v1.2.3