summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/feature_add-action-validation1
-rw-r--r--setup.py1
-rw-r--r--src/leap/soledad/__init__.py28
-rw-r--r--src/leap/soledad/auth.py5
-rw-r--r--src/leap/soledad/backends/couch.py5
-rw-r--r--src/leap/soledad/backends/leap_backend.py5
-rw-r--r--src/leap/soledad/server.py162
-rw-r--r--src/leap/soledad/shared_db.py5
-rw-r--r--src/leap/soledad/tests/__init__.py15
-rw-r--r--src/leap/soledad/tests/test_couch.py5
-rw-r--r--src/leap/soledad/tests/test_crypto.py11
-rw-r--r--src/leap/soledad/tests/test_leap_backend.py36
-rw-r--r--src/leap/soledad/tests/test_server.py239
-rw-r--r--src/leap/soledad/tests/test_soledad.py15
-rw-r--r--src/leap/soledad/tests/test_sqlcipher.py11
15 files changed, 438 insertions, 106 deletions
diff --git a/changes/feature_add-action-validation b/changes/feature_add-action-validation
new file mode 100644
index 00000000..57d5b90c
--- /dev/null
+++ b/changes/feature_add-action-validation
@@ -0,0 +1 @@
+ o Add validation and authorization of actions upon interaction with server.
diff --git a/setup.py b/setup.py
index 1f972746..92c51482 100644
--- a/setup.py
+++ b/setup.py
@@ -37,6 +37,7 @@ install_requirements = [
'six==1.1.0',
'pysqlite',
'scrypt',
+ 'routes',
]
diff --git a/src/leap/soledad/__init__.py b/src/leap/soledad/__init__.py
index 6bb88094..b847364f 100644
--- a/src/leap/soledad/__init__.py
+++ b/src/leap/soledad/__init__.py
@@ -71,27 +71,7 @@ Path to the certificate file used to certify the SSL connection between
Soledad client and server.
"""
-
-#
-# Exceptions
-#
-
-class KeyDoesNotExist(Exception):
- """
- Soledad attempted to find a key that does not exist locally.
- """
-
-
-class KeyAlreadyExists(Exception):
- """
- Soledad attempted to create a key that already exists locally.
- """
-
-
-class NotADirectory(Exception):
- """
- Expected a path for a directory but got some other thing.
- """
+SECRETS_DOC_ID_HASH_PREFIX = 'uuid-'
#
@@ -374,7 +354,6 @@ class Soledad(object):
# recover the initial value and ciphertext
iv, ciphertext = self._secrets[self._secret_id][self.SECRET_KEY].split(
self.IV_SEPARATOR, 1)
- iv = int(iv)
ciphertext = binascii.a2b_base64(ciphertext)
return self._crypto.decrypt_sym(ciphertext, key, iv=iv)
@@ -523,7 +502,10 @@ class Soledad(object):
@return: the hash
@rtype: str
"""
- return sha256('uuid-%s' % self._uuid).hexdigest()
+ return sha256(
+ '%s%s' % (
+ SECRETS_DOC_ID_HASH_PREFIX,
+ self._uuid)).hexdigest()
def _shared_db(self):
"""
diff --git a/src/leap/soledad/auth.py b/src/leap/soledad/auth.py
index 562a8263..8c093099 100644
--- a/src/leap/soledad/auth.py
+++ b/src/leap/soledad/auth.py
@@ -25,6 +25,7 @@ they can do token-based auth requests to the Soledad server.
from u1db.remote.http_client import HTTPClientBase
+from u1db import errors
class TokenBasedAuth(object):
@@ -66,5 +67,5 @@ class TokenBasedAuth(object):
auth = '%s:%s' % (uuid, token)
return [('Authorization', 'Token %s' % auth.encode('base64')[:-1])]
else:
- return HTTPClientBase._sign_request(
- self, method, url_query, params)
+ raise errors.UnknownAuthMethod(
+ 'Wrong credentials: %s' % self._creds)
diff --git a/src/leap/soledad/backends/couch.py b/src/leap/soledad/backends/couch.py
index 93279e2e..0b9bff01 100644
--- a/src/leap/soledad/backends/couch.py
+++ b/src/leap/soledad/backends/couch.py
@@ -21,10 +21,7 @@
# general imports
import uuid
import re
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
from base64 import b64encode, b64decode
diff --git a/src/leap/soledad/backends/leap_backend.py b/src/leap/soledad/backends/leap_backend.py
index bb8ee548..4af2489b 100644
--- a/src/leap/soledad/backends/leap_backend.py
+++ b/src/leap/soledad/backends/leap_backend.py
@@ -21,10 +21,7 @@ A U1DB backend for encrypting data before sending to server and decrypting
after receiving.
"""
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
import hashlib
import hmac
import binascii
diff --git a/src/leap/soledad/server.py b/src/leap/soledad/server.py
index 7aa253a3..9c9e0ad7 100644
--- a/src/leap/soledad/server.py
+++ b/src/leap/soledad/server.py
@@ -25,19 +25,34 @@ This should be run with:
import configparser
import httplib
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
+
+from hashlib import sha256
+from routes.mapper import Mapper
+from u1db import DBNAME_CONSTRAINTS
from u1db.remote import http_app
+
+# Keep OpenSSL's tsafe before importing Twisted submodules so we can put
+# it back if Twisted==12.0.0 messes with it.
+from OpenSSL import tsafe
+old_tsafe = tsafe
+
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
from twisted.python import log
+from twisted import version
+if version.base() == "12.0.0":
+ # Put OpenSSL's tsafe back into place. This can probably be removed if we
+ # come to use Twisted>=12.3.0.
+ import sys
+ sys.modules['OpenSSL.tsafe'] = old_tsafe
+
from couchdb.client import Server
+from leap.soledad import SECRETS_DOC_ID_HASH_PREFIX
from leap.soledad.backends.couch import CouchServerState
@@ -51,6 +66,115 @@ class Unauthorized(Exception):
"""
+class URLToAuth(object):
+ """
+ Verify if actions can be performed by a user.
+ """
+
+ 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
+ """
+ self._map = Mapper(controller_scan=None)
+ self._register_auth_info(self._uuid_dbname(uuid))
+
+ 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
+
+ def _register(self, pattern, http_methods):
+ """
+ Register a C{pattern} in the mapper as valid for C{http_methods}.
+
+ @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
+ """
+ self._map.connect(
+ None, pattern, http_methods=http_methods,
+ conditions=dict(method=http_methods),
+ requirements={'dbname': DBNAME_CONSTRAINTS})
+
+ def _uuid_dbname(self, uuid):
+ """
+ Return the database name corresponding to C{uuid}.
+
+ @param uuid: The user uid.
+ @type uuid: str
+
+ @return: The database name corresponding to C{uuid}.
+ @rtype: str
+ """
+ return sha256('%s%s' % (SECRETS_DOC_ID_HASH_PREFIX, uuid)).hexdigest()
+
+ def _register_auth_info(self, dbname):
+ """
+ Register the authorization info in the mapper using C{dbname} as the
+ user's database name.
+
+ This method sets up the following authorization rules:
+
+ 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 | GET, PUT, DELETE
+ /user-db/docs | -
+ /user-db/doc/{id} | -
+ /user-db/sync-from/{source} | GET, PUT, POST
+
+ @param dbname: The name of the user's database.
+ @type dbname: str
+ """
+ # auth info for global resource
+ self._register('/', [self.HTTP_METHOD_GET])
+ # auth info for shared-db database resource
+ self._register(
+ '/%s' % SoledadApp.SHARED_DB_NAME,
+ [self.HTTP_METHOD_GET])
+ # auth info for shared-db doc resource
+ self._register(
+ '/%s/doc/{id:.*}' % SoledadApp.SHARED_DB_NAME,
+ [self.HTTP_METHOD_GET, self.HTTP_METHOD_PUT,
+ self.HTTP_METHOD_DELETE])
+ # auth info for user-db database resource
+ self._register(
+ '/%s' % dbname,
+ [self.HTTP_METHOD_GET, self.HTTP_METHOD_PUT,
+ self.HTTP_METHOD_DELETE])
+ # auth info for user-db sync resource
+ self._register(
+ '/%s/sync-from/{source_replica_uid}' % dbname,
+ [self.HTTP_METHOD_GET, self.HTTP_METHOD_PUT,
+ self.HTTP_METHOD_POST])
+ # generate the regular expressions
+ self._map.create_regs()
+
+
class SoledadAuthMiddleware(object):
"""
Soledad Authentication WSGI middleware.
@@ -138,6 +262,9 @@ class SoledadAuthMiddleware(object):
if not self.verify_token(environ, uuid, token):
return unauth_err("Incorrect address or token.")
+ if not self.verify_action(uuid, environ):
+ return unauth_err("Unauthorized action.")
+
del environ[self.HTTP_AUTH_KEY]
return self._app(environ, start_response)
@@ -171,20 +298,21 @@ class SoledadAuthMiddleware(object):
return False
return True
- def need_auth(self, environ):
+ def verify_action(self, uuid, environ):
"""
- Check if action can be performed on database without authentication.
-
- For now, just allow access to /shared/*.
+ Verify if the user is authorized to perform the requested action over
+ the requested database.
+ @param uuid: The user's uuid.
+ @type uuid: str
@param environ: Dictionary containing CGI variables.
@type environ: dict
- @return: Whether the requests needs authentication.
+ @return: Whether the user is authorize to perform the requested action
+ over the requested db.
@rtype: bool
"""
- # TODO: design unauth verification.
- return not environ.get(self.PATH_INFO_KEY).startswith('/shared/')
+ return URLToAuth(uuid).is_authorized(environ)
#-----------------------------------------------------------------------------
@@ -196,6 +324,11 @@ class SoledadApp(http_app.HTTPApp):
Soledad WSGI application
"""
+ SHARED_DB_NAME = 'shared'
+ """
+ The name of the shared database that holds user's encrypted secrets.
+ """
+
def __call__(self, environ, start_response):
"""
Handle a WSGI call to the Soledad application.
@@ -209,6 +342,8 @@ class SoledadApp(http_app.HTTPApp):
@return: HTTP application results.
@rtype: list
"""
+ # ensure the shared database exists
+ self.state.ensure_database(self.SHARED_DB_NAME)
return http_app.HTTPApp.__call__(self, environ, start_response)
@@ -244,11 +379,10 @@ def load_configuration(file_path):
# Run as Twisted WSGI Resource
#-----------------------------------------------------------------------------
-# TODO: create command-line option for choosing config file.
conf = load_configuration('/etc/leap/soledad-server.conf')
state = CouchServerState(conf['couch_url'])
-application = SoledadAuthMiddleware(
- SoledadApp(state))
+# WSGI application that may be used by `twistd -web`
+application = SoledadAuthMiddleware(SoledadApp(state))
resource = WSGIResource(reactor, reactor.getThreadPool(), application)
diff --git a/src/leap/soledad/shared_db.py b/src/leap/soledad/shared_db.py
index c954dbea..33c5c484 100644
--- a/src/leap/soledad/shared_db.py
+++ b/src/leap/soledad/shared_db.py
@@ -20,10 +20,7 @@
A shared database for storing/retrieving encrypted key material.
"""
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
from u1db.remote import http_database
diff --git a/src/leap/soledad/tests/__init__.py b/src/leap/soledad/tests/__init__.py
index 00de687b..c00fb847 100644
--- a/src/leap/soledad/tests/__init__.py
+++ b/src/leap/soledad/tests/__init__.py
@@ -2,6 +2,7 @@
Tests to make sure Soledad provides U1DB functionality and more.
"""
+import os
import u1db
from mock import Mock
@@ -28,8 +29,8 @@ class BaseSoledadTest(BaseLeapTest):
def setUp(self):
# config info
- self.db1_file = "%s/db1.u1db" % self.tempdir
- self.db2_file = "%s/db2.u1db" % self.tempdir
+ self.db1_file = os.path.join(self.tempdir, "db1.u1db")
+ self.db2_file = os.path.join(self.tempdir, "db2.u1db")
self.email = 'leap@leap.se'
# open test dbs
self._db1 = u1db.open(self.db1_file, create=True,
@@ -42,12 +43,15 @@ class BaseSoledadTest(BaseLeapTest):
def tearDown(self):
self._db1.close()
self._db2.close()
+ for f in [self._soledad._local_db_path, self._soledad._secrets_path]:
+ if os.path.isfile(f):
+ os.unlink(f)
self._soledad.close()
def _soledad_instance(self, user='leap@leap.se', passphrase='123',
prefix='',
secrets_path=Soledad.STORAGE_SECRETS_FILE_NAME,
- local_db_path='/soledad.u1db', server_url='',
+ local_db_path='soledad.u1db', server_url='',
cert_file=None, secret_id=None):
def _put_doc_side_effect(doc):
@@ -65,8 +69,9 @@ class BaseSoledadTest(BaseLeapTest):
return Soledad(
user,
passphrase,
- secrets_path=self.tempdir+prefix+secrets_path,
- local_db_path=self.tempdir+prefix+local_db_path,
+ secrets_path=os.path.join(self.tempdir, prefix, secrets_path),
+ local_db_path=os.path.join(
+ self.tempdir, prefix, local_db_path),
server_url=server_url, # Soledad will fail if not given an url.
cert_file=cert_file,
secret_id=secret_id)
diff --git a/src/leap/soledad/tests/test_couch.py b/src/leap/soledad/tests/test_couch.py
index d6b9ad83..b3cbc1bc 100644
--- a/src/leap/soledad/tests/test_couch.py
+++ b/src/leap/soledad/tests/test_couch.py
@@ -31,10 +31,7 @@ from leap.soledad.backends import couch
from leap.soledad.tests import u1db_tests as tests
from leap.soledad.tests.u1db_tests import test_backends
from leap.soledad.tests.u1db_tests import test_sync
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
from leap.soledad.backends.leap_backend import (
LeapDocument,
)
diff --git a/src/leap/soledad/tests/test_crypto.py b/src/leap/soledad/tests/test_crypto.py
index a61b931c..5432856e 100644
--- a/src/leap/soledad/tests/test_crypto.py
+++ b/src/leap/soledad/tests/test_crypto.py
@@ -23,10 +23,7 @@ Tests for cryptographic related stuff.
import os
import shutil
import tempfile
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
import hashlib
@@ -44,7 +41,7 @@ from leap.soledad.backends.leap_backend import (
WrongMac,
)
from leap.soledad.backends.couch import CouchDatabase
-from leap.soledad import KeyAlreadyExists, Soledad
+from leap.soledad import Soledad
from leap.soledad.crypto import SoledadCrypto
from leap.soledad.tests import BaseSoledadTest
from leap.soledad.tests.test_couch import CouchDBTestCase
@@ -192,7 +189,7 @@ class RecoveryDocumentTestCase(BaseSoledadTest):
def test_import_recovery_document(self):
rd = self._soledad.export_recovery_document()
- s = self._soledad_instance(user='anotheruser@leap.se', prefix='/2')
+ s = self._soledad_instance(user='anotheruser@leap.se')
s.import_recovery_document(rd)
s._set_secret_id(self._soledad._secret_id)
self.assertEqual(self._soledad._uuid,
@@ -238,7 +235,7 @@ class CryptoMethodsTestCase(BaseSoledadTest):
def test__has_secret(self):
- sol = self._soledad_instance(user='user@leap.se', prefix='/4')
+ sol = self._soledad_instance(user='user@leap.se')
self.assertTrue(sol._has_secret(), "Should have a secret at "
"this point")
# setting secret id to None should not interfere in the fact we have a
diff --git a/src/leap/soledad/tests/test_leap_backend.py b/src/leap/soledad/tests/test_leap_backend.py
index 8afae6f6..d04ee412 100644
--- a/src/leap/soledad/tests/test_leap_backend.py
+++ b/src/leap/soledad/tests/test_leap_backend.py
@@ -23,32 +23,28 @@ Test Leap backend bits.
import u1db
import os
import ssl
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
import cStringIO
+
from u1db.sync import Synchronizer
from u1db.remote import (
http_client,
http_database,
http_target,
)
+from routes.mapper import Mapper
from leap import soledad
from leap.soledad.backends import leap_backend
from leap.soledad.server import (
SoledadApp,
- SoledadAuthMiddleware
+ SoledadAuthMiddleware,
)
from leap.soledad import auth
from leap.soledad.tests import u1db_tests as tests
-from leap.soledad.tests.u1db_tests.test_remote_sync_target import (
- make_oauth_http_app,
-)
from leap.soledad.tests import BaseSoledadTest
from leap.soledad.tests.u1db_tests import test_backends
from leap.soledad.tests.u1db_tests import test_http_database
@@ -81,8 +77,13 @@ def make_token_soledad_app(state):
return True
return False
+ # we test for action authorization in leap.soledad.tests.test_server
+ def verify_action(environ, uuid):
+ return True
+
application = SoledadAuthMiddleware(app)
application.verify_token = verify_token
+ application.verify_action = verify_action
return application
@@ -128,12 +129,6 @@ def copy_token_http_database_for_test(test, db):
class LeapTests(test_backends.AllDatabaseTests, BaseSoledadTest):
scenarios = LEAP_SCENARIOS + [
- ('oauth_http', {'make_database_for_test':
- test_backends.make_oauth_http_database_for_test,
- 'copy_database_for_test':
- test_backends.copy_oauth_http_database_for_test,
- 'make_document_for_test': make_leap_document_for_test,
- 'make_app_with_state': make_oauth_http_app}),
('token_http', {'make_database_for_test':
make_token_http_database_for_test,
'copy_database_for_test':
@@ -362,13 +357,6 @@ def leap_sync_target(test, path):
test.getURL(path), crypto=test._soledad._crypto)
-def oauth_leap_sync_target(test, path):
- st = leap_sync_target(test, '~/' + path)
- st.set_oauth_credentials(tests.consumer1.key, tests.consumer1.secret,
- tests.token1.key, tests.token1.secret)
- return st
-
-
def token_leap_sync_target(test, path):
st = leap_sync_target(test, path)
st.set_token_credentials('user-uuid', 'auth-token')
@@ -379,12 +367,6 @@ class TestLeapSyncTarget(
test_remote_sync_target.TestRemoteSyncTargets, BaseSoledadTest):
scenarios = [
- ('http', {'make_app_with_state': make_soledad_app,
- 'make_document_for_test': make_leap_document_for_test,
- 'sync_target': leap_sync_target}),
- ('oauth_http', {'make_app_with_state': make_oauth_http_app,
- 'make_document_for_test': make_leap_document_for_test,
- 'sync_target': oauth_leap_sync_target}),
('token_soledad',
{'make_app_with_state': make_token_soledad_app,
'make_document_for_test': make_leap_document_for_test,
diff --git a/src/leap/soledad/tests/test_server.py b/src/leap/soledad/tests/test_server.py
new file mode 100644
index 00000000..ec3f636b
--- /dev/null
+++ b/src/leap/soledad/tests/test_server.py
@@ -0,0 +1,239 @@
+# -*- coding: utf-8 -*-
+# test_server.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 <http://www.gnu.org/licenses/>.
+
+
+"""
+Tests for server-related functionality.
+"""
+
+import os
+import shutil
+import tempfile
+import simplejson as json
+import hashlib
+
+
+from leap.soledad.server import URLToAuth
+from leap.common.testing.basetest import BaseLeapTest
+
+
+class SoledadServerTestCase(BaseLeapTest):
+ """
+ Tests that guarantee that data will always be encrypted when syncing.
+ """
+
+ def setUp(self):
+ pass
+
+ 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.
+
+ 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 | GET, PUT, DELETE
+ /user-db/docs | -
+ /user-db/doc/{id} | -
+ /user-db/sync-from/{source} | GET, PUT, POST
+ """
+ uuid = 'myuuid'
+ authmap = URLToAuth(uuid)
+ dbname = authmap._uuid_dbname(uuid)
+ # test global auth
+ self.assertTrue(
+ authmap.is_authorized(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(
+ self._make_environ('/shared', 'PUT')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared', 'DELETE')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared', 'POST')))
+ # test shared-db docs resource auth
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared/docs', 'GET')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared/docs', 'PUT')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared/docs', 'DELETE')))
+ self.assertFalse(
+ authmap.is_authorized(
+ 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(
+ self._make_environ('/shared/doc/x', 'POST')))
+ # test shared-db sync resource auth
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared/sync-from/x', 'GET')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared/sync-from/x', 'PUT')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared/sync-from/x', 'DELETE')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/shared/sync-from/x', 'POST')))
+ # test user-db database resource auth
+ self.assertTrue(
+ authmap.is_authorized(
+ self._make_environ('/%s' % dbname, 'GET')))
+ self.assertTrue(
+ authmap.is_authorized(
+ self._make_environ('/%s' % dbname, 'PUT')))
+ self.assertTrue(
+ authmap.is_authorized(
+ self._make_environ('/%s' % dbname, 'DELETE')))
+ self.assertFalse(
+ authmap.is_authorized(
+ self._make_environ('/%s' % dbname, 'POST')))
+ # test user-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 user-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 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 = 'myuuid'
+ authmap = URLToAuth(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')))
diff --git a/src/leap/soledad/tests/test_soledad.py b/src/leap/soledad/tests/test_soledad.py
index 45cd7eb2..1c0e6d4a 100644
--- a/src/leap/soledad/tests/test_soledad.py
+++ b/src/leap/soledad/tests/test_soledad.py
@@ -24,10 +24,7 @@ Tests for general Soledad functionality.
import os
import re
import tempfile
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
from leap.common.testing.basetest import BaseLeapTest
@@ -41,7 +38,7 @@ from leap.soledad.backends.leap_backend import LeapDocument
class AuxMethodsTestCase(BaseSoledadTest):
def test__init_dirs(self):
- sol = self._soledad_instance(prefix='/_init_dirs')
+ sol = self._soledad_instance(prefix='_init_dirs')
sol._init_dirs()
local_db_dir = os.path.dirname(sol.local_db_path)
secrets_path = os.path.dirname(sol.secrets_path)
@@ -94,8 +91,12 @@ class AuxMethodsTestCase(BaseSoledadTest):
local_db_path='value_2',
server_url='value_1',
cert_file=None)
- self.assertEqual(self.tempdir+'value_3', sol.secrets_path)
- self.assertEqual(self.tempdir+'value_2', sol.local_db_path)
+ self.assertEqual(
+ os.path.join(self.tempdir, 'value_3'),
+ sol.secrets_path)
+ self.assertEqual(
+ os.path.join(self.tempdir, 'value_2'),
+ sol.local_db_path)
self.assertEqual('value_1', sol.server_url)
diff --git a/src/leap/soledad/tests/test_sqlcipher.py b/src/leap/soledad/tests/test_sqlcipher.py
index 60261111..dfc5577b 100644
--- a/src/leap/soledad/tests/test_sqlcipher.py
+++ b/src/leap/soledad/tests/test_sqlcipher.py
@@ -24,11 +24,10 @@ Test sqlcipher backend internals.
import os
import time
import unittest
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
+import simplejson as json
import threading
+
+
from pysqlcipher import dbapi2
from StringIO import StringIO
@@ -440,6 +439,7 @@ def sync_via_synchronizer_and_leap(test, db_source, db_target,
test.skipTest("full trace hook unsupported over http")
path = test._http_at[db_target]
target = LeapSyncTarget.connect(test.getURL(path), test._soledad._crypto)
+ target.set_token_credentials('user-uuid', 'auth-token')
if trace_hook_shallow:
target._set_trace_hook_shallow(trace_hook_shallow)
return sync.Synchronizer(db_source, target).sync()
@@ -663,6 +663,7 @@ def _make_local_db_and_leap_target(test, path='test'):
test.startServer()
db = test.request_state._create_database(os.path.basename(path))
st = LeapSyncTarget.connect(test.getURL(path), test._soledad._crypto)
+ st.set_token_credentials('user-uuid', 'auth-token')
return db, st
@@ -773,7 +774,7 @@ class SQLCipherEncryptionTest(BaseLeapTest):
os.unlink(dbfile)
def setUp(self):
- self.DB_FILE = self.tempdir + '/test.db'
+ self.DB_FILE = os.path.join(self.tempdir, 'test.db')
self._delete_dbfiles()
def tearDown(self):