diff options
author | Bruno Wagner Gonçalves <bwagner@riseup.net> | 2015-05-04 14:22:33 -0300 |
---|---|---|
committer | Bruno Wagner Gonçalves <bwagner@riseup.net> | 2015-05-04 14:22:33 -0300 |
commit | a28d2b333261d788c44d1835dda4d86427ab7728 (patch) | |
tree | e4cdd206d838b9ea9dbb684676f3254a00bc4c39 /service | |
parent | 4fe2aebde4a8bafcb2937bed4c9861f67230cb9b (diff) | |
parent | 30fe9e6c31ed8d5d0ee76c13e3344e67e26f1479 (diff) |
Merge pull request #389 from pixelated-project/srp_changes
SRP changes to support LEAP 0.7
Diffstat (limited to 'service')
-rw-r--r-- | service/pixelated/bitmask_libraries/auth.py | 42 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/leap_srp.py | 147 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/nicknym.py | 10 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/provider.py | 6 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/register.py | 5 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/session.py | 59 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/smtp.py | 9 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/soledad.py | 19 | ||||
-rw-r--r-- | service/pixelated/config/app_factory.py | 5 | ||||
-rw-r--r-- | service/pixelated/config/register.py | 3 | ||||
-rw-r--r-- | service/setup.py | 1 | ||||
-rw-r--r-- | service/test/unit/bitmask_libraries/test_abstract_leap.py | 12 | ||||
-rw-r--r-- | service/test/unit/bitmask_libraries/test_leap_srp.py | 157 | ||||
-rw-r--r-- | service/test/unit/bitmask_libraries/test_nicknym.py | 18 | ||||
-rw-r--r-- | service/test/unit/bitmask_libraries/test_session.py | 2 | ||||
-rw-r--r-- | service/test/unit/bitmask_libraries/test_smtp.py | 9 | ||||
-rw-r--r-- | service/test/unit/bitmask_libraries/test_soledad.py | 12 |
17 files changed, 90 insertions, 426 deletions
diff --git a/service/pixelated/bitmask_libraries/auth.py b/service/pixelated/bitmask_libraries/auth.py deleted file mode 100644 index 9a2fdcb2..00000000 --- a/service/pixelated/bitmask_libraries/auth.py +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from .leap_srp import LeapSecureRemotePassword -from .certs import which_api_CA_bundle - -USE_PASSWORD = None - - -class LeapCredentials(object): - def __init__(self, user_name, password, db_passphrase=USE_PASSWORD): - self.user_name = user_name - self.password = password - self.db_passphrase = db_passphrase if db_passphrase is not None else password - - -class LeapAuthenticator(object): - def __init__(self, provider): - self._provider = provider - - def authenticate(self, credentials): - config = self._provider.config - srp = LeapSecureRemotePassword(ca_bundle=which_api_CA_bundle(self._provider), timeout_in_s=config.timeout_in_s) - srp_session = srp.authenticate(self._provider.api_uri, credentials.user_name, credentials.password) - return srp_session - - def register(self, credentials): - config = self._provider.config - srp = LeapSecureRemotePassword(ca_bundle=which_api_CA_bundle(self._provider), timeout_in_s=config.timeout_in_s) - srp.register(self._provider.api_uri, credentials.user_name, credentials.password) diff --git a/service/pixelated/bitmask_libraries/leap_srp.py b/service/pixelated/bitmask_libraries/leap_srp.py deleted file mode 100644 index 7a627c1d..00000000 --- a/service/pixelated/bitmask_libraries/leap_srp.py +++ /dev/null @@ -1,147 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -import binascii -import json - -import requests -from requests import Session -from srp import User, srp, create_salted_verification_key -from requests.exceptions import HTTPError, SSLError, Timeout -from config import SYSTEM_CA_BUNDLE - - -REGISTER_USER_LOGIN_KEY = 'user[login]' -REGISTER_USER_VERIFIER_KEY = 'user[password_verifier]' -REGISTER_USER_SALT_KEY = 'user[password_salt]' - - -class LeapAuthException(Exception): - def __init__(self, *args, **kwargs): - super(LeapAuthException, self).__init__(*args, **kwargs) - - -class LeapSRPSession(object): - def __init__(self, user_name, api_server_name, uuid, token, session_id, api_version='1'): - self.user_name = user_name - self.api_server_name = api_server_name - self.uuid = uuid - self.token = token - self.session_id = session_id - self.api_version = api_version - - def __str__(self): - return 'LeapSRPSession(%s, %s, %s, %s, %s, %s)' % (self.user_name, self.api_server_name, self.uuid, self.token, self.session_id, self.api_version) - - -class LeapSecureRemotePassword(object): - def __init__(self, hash_alg=srp.SHA256, ng_type=srp.NG_1024, ca_bundle=SYSTEM_CA_BUNDLE, timeout_in_s=15, - leap_api_version='1'): - - self.hash_alg = hash_alg - self.ng_type = ng_type - self.timeout_in_s = timeout_in_s - self.ca_bundle = ca_bundle - self.leap_api_version = leap_api_version - - def authenticate(self, api_uri, username, password): - session = Session() - try: - return self._authenticate_with_session(session, api_uri, username, password) - except Timeout, e: - raise LeapAuthException(e) - finally: - session.close() - - def _authenticate_with_session(self, http_session, api_uri, username, password): - try: - srp_user = User(username.encode('utf-8'), password.encode('utf-8'), self.hash_alg, self.ng_type) - - salt, B_challenge = self._begin_authentication(srp_user, http_session, api_uri) - M2_verfication_code, leap_session = self._process_challenge(srp_user, http_session, api_uri, salt, - B_challenge) - self._verify_session(srp_user, M2_verfication_code) - - return leap_session - except (HTTPError, SSLError), e: - raise LeapAuthException(e) - - def _begin_authentication(self, user, session, api_uri): - _, A = user.start_authentication() - - auth_data = { - "login": user.get_username(), - "A": binascii.hexlify(A) - } - session_url = '%s/%s/sessions' % (api_uri, self.leap_api_version) - response = session.post(session_url, data=auth_data, verify=self.ca_bundle, timeout=self.timeout_in_s) - response.raise_for_status() - json_content = json.loads(response.content) - - salt = _safe_unhexlify(json_content.get('salt')) - B = _safe_unhexlify(json_content.get('B')) - - return salt, B - - def _process_challenge(self, user, session, api_uri, salt, B): - M = user.process_challenge(salt, B) - - auth_data = { - "client_auth": binascii.hexlify(M) - } - - auth_url = '%s/%s/sessions/%s' % (api_uri, self.leap_api_version, user.get_username()) - response = session.put(auth_url, data=auth_data, verify=self.ca_bundle, timeout=self.timeout_in_s) - response.raise_for_status() - auth_json = json.loads(response.content) - - M2 = _safe_unhexlify(auth_json.get('M2')) - uuid = auth_json.get('id') - token = auth_json.get('token') - session_id = response.cookies.get('_session_id') - - return M2, LeapSRPSession(user.get_username(), api_uri, uuid, token, session_id) - - def _verify_session(self, user, M2): - user.verify_session(M2) - if not user.authenticated(): - raise LeapAuthException() - - def register(self, api_uri, username, password): - try: - salt, verifier = create_salted_verification_key(username, password, self.hash_alg, self.ng_type) - return self._post_registration_data(api_uri, username, salt, verifier) - except (HTTPError, SSLError, Timeout), e: - raise LeapAuthException(e) - - def _post_registration_data(self, api_uri, username, salt, verifier): - users_url = '%s/%s/users' % (api_uri, self.leap_api_version) - - user_data = { - REGISTER_USER_LOGIN_KEY: username, - REGISTER_USER_SALT_KEY: binascii.hexlify(salt), - REGISTER_USER_VERIFIER_KEY: binascii.hexlify(verifier) - } - - response = requests.post(users_url, data=user_data, verify=self.ca_bundle, timeout=self.timeout_in_s) - response.raise_for_status() - reg_json = json.loads(response.content) - - return reg_json['ok'] - - -def _safe_unhexlify(hex_str): - return binascii.unhexlify(hex_str) \ - if (len(hex_str) % 2 == 0) else binascii.unhexlify('0' + hex_str) diff --git a/service/pixelated/bitmask_libraries/nicknym.py b/service/pixelated/bitmask_libraries/nicknym.py index ef846bba..bee90897 100644 --- a/service/pixelated/bitmask_libraries/nicknym.py +++ b/service/pixelated/bitmask_libraries/nicknym.py @@ -18,14 +18,14 @@ from .certs import which_api_CA_bundle class NickNym(object): - def __init__(self, provider, config, soledad_session, srp_session): + def __init__(self, provider, config, soledad_session, username, token, uuid): nicknym_url = _discover_nicknym_server(provider) - self._email = '%s@%s' % (srp_session.user_name, provider.domain) - self.keymanager = KeyManager('%s@%s' % (srp_session.user_name, provider.domain), nicknym_url, + self._email = '%s@%s' % (username, provider.domain) + self.keymanager = KeyManager('%s@%s' % (username, provider.domain), nicknym_url, soledad_session.soledad, - srp_session.token, which_api_CA_bundle(provider), provider.api_uri, + token, which_api_CA_bundle(provider), provider.api_uri, provider.api_version, - srp_session.uuid, config.gpg_binary) + uuid, config.gpg_binary) def generate_openpgp_key(self): if not self._key_exists(self._email): diff --git a/service/pixelated/bitmask_libraries/provider.py b/service/pixelated/bitmask_libraries/provider.py index a0bf4843..1564c974 100644 --- a/service/pixelated/bitmask_libraries/provider.py +++ b/service/pixelated/bitmask_libraries/provider.py @@ -25,6 +25,7 @@ class LeapProvider(object): def __init__(self, server_name, config): self.server_name = server_name self.config = config + self.local_ca_crt = '%s/ca.crt' % self.config.leap_home self.provider_json = self.fetch_provider_json() @@ -62,12 +63,13 @@ class LeapProvider(object): if 'mx' not in self.services: raise Exception - def download_certificate_to(self, filename): + def download_certificate(self, filename=None): """ Downloads the server certificate, validates it against the provided fingerprint and stores it to file """ + path = filename or self.local_ca_crt cert = self.fetch_valid_certificate() - with open(filename, 'w') as out: + with open(path, 'w') as out: out.write(cert) def fetch_valid_certificate(self): diff --git a/service/pixelated/bitmask_libraries/register.py b/service/pixelated/bitmask_libraries/register.py index bb54477f..d5cce0d8 100644 --- a/service/pixelated/bitmask_libraries/register.py +++ b/service/pixelated/bitmask_libraries/register.py @@ -21,14 +21,15 @@ import os.path import pixelated.bitmask_libraries.session as LeapSession from pixelated.bitmask_libraries.config import LeapConfig from pixelated.bitmask_libraries.provider import LeapProvider -from pixelated.bitmask_libraries.auth import LeapAuthenticator, LeapCredentials +from leap.auth import SRPAuth def register_new_user(username, server_name): config = LeapConfig() provider = LeapProvider(server_name, config) password = getpass.getpass('Please enter password for %s: ' % username) - LeapAuthenticator(provider).register(LeapCredentials(username, password)) + srp_auth = SRPAuth(provider, provider.local_ca_crt) + srp_auth.register(username, password) session = LeapSession.open(username, password, server_name) session.nicknym.generate_openpgp_key() diff --git a/service/pixelated/bitmask_libraries/session.py b/service/pixelated/bitmask_libraries/session.py index eb6f39d7..1c8c8c3b 100644 --- a/service/pixelated/bitmask_libraries/session.py +++ b/service/pixelated/bitmask_libraries/session.py @@ -27,7 +27,7 @@ from pixelated.bitmask_libraries.provider import LeapProvider from pixelated.bitmask_libraries.certs import refresh_ca_bundle from twisted.internet import reactor from .nicknym import NickNym -from .auth import LeapAuthenticator, LeapCredentials +from leap.auth import SRPAuth from .soledad import SoledadSessionFactory, SoledadSession from .smtp import LeapSmtp from .config import DEFAULT_LEAP_HOME @@ -40,7 +40,7 @@ def open(username, password, server_name, leap_home=DEFAULT_LEAP_HOME): config = LeapConfig(leap_home=leap_home) provider = LeapProvider(server_name, config) refresh_ca_bundle(provider) - session = LeapSessionFactory(provider).create(LeapCredentials(username, password)) + session = LeapSessionFactory(provider).create(username, password) return session @@ -65,7 +65,7 @@ class LeapSession(object): - ``incoming_mail_fetcher`` Background job for fetching incoming mails from LEAP server (LeapIncomingMail) """ - def __init__(self, provider, srp_session, soledad_session, nicknym, soledad_account, incoming_mail_fetcher, smtp): + def __init__(self, provider, user_auth, soledad_session, nicknym, soledad_account, incoming_mail_fetcher, smtp): """ Constructor. @@ -76,7 +76,7 @@ class LeapSession(object): self.smtp = smtp self.config = provider.config self.provider = provider - self.srp_session = srp_session + self.user_auth = user_auth self.soledad_session = soledad_session self.nicknym = nicknym self.account = soledad_account @@ -87,7 +87,7 @@ class LeapSession(object): def account_email(self): domain = self.provider.domain - name = self.srp_session.user_name + name = self.user_auth.username return '%s@%s' % (name, domain) def close(self): @@ -112,28 +112,29 @@ class LeapSessionFactory(object): self._provider = provider self._config = provider.config - def create(self, credentials): - key = self._session_key(credentials) + def create(self, username, password): + key = self._session_key(username) session = self._lookup_session(key) if not session: - session = self._create_new_session(credentials) + session = self._create_new_session(username, password) self._remember_session(key, session) return session - def _create_new_session(self, credentials): + def _create_new_session(self, username, password): self._create_dir(self._provider.config.leap_home) - self._provider.download_certificate_to('%s/ca.crt' % self._provider.config.leap_home) + self._provider.download_certificate() - auth = LeapAuthenticator(self._provider).authenticate(credentials) - soledad = SoledadSessionFactory.create(self._provider, auth, credentials.db_passphrase) + srp_auth = SRPAuth(self._provider.api_uri, self._provider.local_ca_crt) + auth = srp_auth.authenticate(username, password) - nicknym = self._create_nicknym(auth, soledad) - account = self._create_account(auth, soledad) - incoming_mail_fetcher = self._create_incoming_mail_fetcher(nicknym, soledad, - account, auth) + soledad = SoledadSessionFactory.create(self._provider, auth.token, auth.uuid, password) - smtp = LeapSmtp(self._provider, nicknym.keymanager, auth) + nicknym = self._create_nicknym(auth.username, auth.token, auth.uuid, soledad) + account = self._create_account(auth.uuid, soledad) + incoming_mail_fetcher = self._create_incoming_mail_fetcher(nicknym, soledad, account, auth.username) + + smtp = LeapSmtp(self._provider, auth.username, auth.session_id, nicknym.keymanager) smtp.ensure_running() @@ -150,8 +151,8 @@ class LeapSessionFactory(object): global SESSIONS SESSIONS[key] = session - def _session_key(self, credentials): - return hash((self._provider, credentials.user_name)) + def _session_key(self, username): + return hash((self._provider, username)) def _create_dir(self, path): try: @@ -162,21 +163,17 @@ class LeapSessionFactory(object): else: raise - def _create_soledad_session(self, srp_session, db_passphrase): - return SoledadSession(self._provider, db_passphrase, srp_session) - - def _create_nicknym(self, srp_session, soledad_session): - return NickNym(self._provider, self._config, soledad_session, srp_session) + def _create_nicknym(self, username, token, uuid, soledad_session): + return NickNym(self._provider, self._config, soledad_session, username, token, uuid) - def _create_account(self, srp_session, soledad_session): + def _create_account(self, uuid, soledad_session): memstore = MemoryStore(permanent_store=SoledadStore(soledad_session.soledad)) - return SoledadBackedAccount(srp_session.uuid, soledad_session.soledad, memstore) + return SoledadBackedAccount(uuid, soledad_session.soledad, memstore) - def _create_incoming_mail_fetcher(self, nicknym, soledad_session, account, auth): + def _create_incoming_mail_fetcher(self, nicknym, soledad_session, account, username): return LeapIncomingMail(nicknym.keymanager, soledad_session.soledad, account, - self._config.fetch_interval_in_s, self._account_email(auth)) + self._config.fetch_interval_in_s, self._account_email(username)) - def _account_email(self, auth): + def _account_email(self, username): domain = self._provider.domain - name = auth.user_name - return '%s@%s' % (name, domain) + return '%s@%s' % (username, domain) diff --git a/service/pixelated/bitmask_libraries/smtp.py b/service/pixelated/bitmask_libraries/smtp.py index ba5e7102..a0f9c6e4 100644 --- a/service/pixelated/bitmask_libraries/smtp.py +++ b/service/pixelated/bitmask_libraries/smtp.py @@ -27,10 +27,11 @@ class LeapSmtp(object): TWISTED_PORT = 4650 - def __init__(self, provider, keymanager=None, leap_srp_session=None): + def __init__(self, provider, username, session_id, keymanager=None): self._provider = provider + self.username = username + self.session_id = session_id self._keymanager = keymanager - self._srp_session = leap_srp_session self._hostname, self._port = self._discover_smtp_server() self._smtp_port = None self._smtp_service = None @@ -56,7 +57,7 @@ class LeapSmtp(object): os.makedirs(os.path.dirname(cert_path)) cert_url = '%s/%s/cert' % (self._provider.api_uri, self._provider.api_version) - cookies = {"_session_id": self._srp_session.session_id} + cookies = {"_session_id": self.session_id} response = requests.get(cert_url, verify=which_api_CA_bundle(self._provider), cookies=cookies, timeout=self._provider.config.timeout_in_s) response.raise_for_status() @@ -76,7 +77,7 @@ class LeapSmtp(object): def start(self): self._download_client_certificates() cert_path = self._client_cert_path() - email = '%s@%s' % (self._srp_session.user_name, self._provider.domain) + email = '%s@%s' % (self.username, self._provider.domain) self._smtp_service, self._smtp_port = setup_smtp_gateway( port=self.TWISTED_PORT, diff --git a/service/pixelated/bitmask_libraries/soledad.py b/service/pixelated/bitmask_libraries/soledad.py index 83a8caa9..f3fca95a 100644 --- a/service/pixelated/bitmask_libraries/soledad.py +++ b/service/pixelated/bitmask_libraries/soledad.py @@ -46,15 +46,16 @@ class LeapKeyManager(object): class SoledadSessionFactory(object): @classmethod - def create(cls, provider, srp_session, encryption_passphrase): - return SoledadSession(provider, encryption_passphrase, srp_session) + def create(cls, provider, user_token, user_uuid, encryption_passphrase): + return SoledadSession(provider, encryption_passphrase, user_token, user_uuid) class SoledadSession(object): - def __init__(self, provider, encryption_passphrase, leap_srp_session): + def __init__(self, provider, encryption_passphrase, user_token, user_uuid): self.provider = provider self.config = provider.config - self.leap_srp_session = leap_srp_session + self.user_uuid = user_uuid + self.user_token = user_token self.soledad = self._init_soledad(encryption_passphrase) @@ -66,8 +67,8 @@ class SoledadSession(object): secrets = self._secrets_path() local_db = self._local_db_path() - return Soledad(self.leap_srp_session.uuid, unicode(encryption_passphrase), secrets, - local_db, server_url, which_api_CA_bundle(self.provider), self.leap_srp_session.token, defer_encryption=False) + return Soledad(self.user_uuid, unicode(encryption_passphrase), secrets, + local_db, server_url, which_api_CA_bundle(self.provider), self.user_token, defer_encryption=False) except (WrongMac, UnknownMacMethod), e: raise SoledadWrongPassphraseException(e) @@ -76,10 +77,10 @@ class SoledadSession(object): return "%s/soledad" % self.config.leap_home def _secrets_path(self): - return "%s/%s.secret" % (self._leap_path(), self.leap_srp_session.uuid) + return "%s/%s.secret" % (self._leap_path(), self.user_uuid) def _local_db_path(self): - return "%s/%s.db" % (self._leap_path(), self.leap_srp_session.uuid) + return "%s/%s.db" % (self._leap_path(), self.user_uuid) def _create_database_dir(self): try: @@ -102,7 +103,7 @@ class SoledadSession(object): host = hosts.keys()[0] server_url = 'https://%s:%d/user-%s' % \ (hosts[host]['hostname'], hosts[host]['port'], - self.leap_srp_session.uuid) + self.user_uuid) return server_url except Exception, e: raise SoledadDiscoverException(e) diff --git a/service/pixelated/config/app_factory.py b/service/pixelated/config/app_factory.py index 51f76741..96412386 100644 --- a/service/pixelated/config/app_factory.py +++ b/service/pixelated/config/app_factory.py @@ -31,7 +31,6 @@ from pixelated.adapter.search import SearchEngine from pixelated.adapter.services.draft_service import DraftService from pixelated.adapter.listeners.mailbox_indexer_listener import MailboxIndexerListener import pixelated.bitmask_libraries.session as LeapSession -from pixelated.bitmask_libraries.leap_srp import LeapAuthException from requests.exceptions import ConnectionError from leap.common.events import ( register, @@ -70,10 +69,6 @@ def init_leap_session(app, leap_home): print("Can't connect to the requested provider", error) reactor.stop() sys.exit(1) - except LeapAuthException, e: - print("Couldn't authenticate with the credentials provided %s" % e.message) - reactor.stop() - sys.exit(1) return leap_session diff --git a/service/pixelated/config/register.py b/service/pixelated/config/register.py index d54b10ff..8a7caa8b 100644 --- a/service/pixelated/config/register.py +++ b/service/pixelated/config/register.py @@ -15,7 +15,6 @@ # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. import re -from pixelated.bitmask_libraries.leap_srp import LeapAuthException from pixelated.bitmask_libraries.register import register_new_user @@ -23,8 +22,6 @@ def register(username, server_name): try: validate_username(username) register_new_user(username, server_name) - except LeapAuthException: - print('User already exists') except ValueError: print('Only lowercase letters, digits, . - and _ allowed.') diff --git a/service/setup.py b/service/setup.py index 973cac47..a33ea995 100644 --- a/service/setup.py +++ b/service/setup.py @@ -62,6 +62,7 @@ setup(name='pixelated-user-agent', 'srp==1.0.4', 'dirspec==4.2.0', 'u1db==13.09', + 'leap.auth==0.1.0', 'leap.keymanager==0.3.8', 'leap.soledad.common==0.6.3', 'leap.soledad.client==0.6.3', diff --git a/service/test/unit/bitmask_libraries/test_abstract_leap.py b/service/test/unit/bitmask_libraries/test_abstract_leap.py index c11c7ea9..64de09bc 100644 --- a/service/test/unit/bitmask_libraries/test_abstract_leap.py +++ b/service/test/unit/bitmask_libraries/test_abstract_leap.py @@ -22,9 +22,9 @@ from mock import Mock, MagicMock class AbstractLeapTest(unittest.TestCase): - uuid = str(uuid4()) - session_id = str(uuid4()) - token = str(uuid4()) + _uuid = str(uuid4()) + _session_id = str(uuid4()) + _token = str(uuid4()) leap_home = os.path.join(tempfile.mkdtemp(), 'leap') @@ -33,7 +33,11 @@ class AbstractLeapTest(unittest.TestCase): api_uri='https://api.some-server.test:4430', api_version='1') soledad = Mock() soledad_session = Mock(soledad=soledad) - srp_session = Mock(user_name='test_user', api_server_name='some-server.test', uuid=uuid, session_id=session_id, token=token) + auth = Mock(username='test_user', + api_server_name='some-server.test', + uuid=_uuid, + session_id=_session_id, + token=_token) nicknym = MagicMock() diff --git a/service/test/unit/bitmask_libraries/test_leap_srp.py b/service/test/unit/bitmask_libraries/test_leap_srp.py deleted file mode 100644 index 6d067e5d..00000000 --- a/service/test/unit/bitmask_libraries/test_leap_srp.py +++ /dev/null @@ -1,157 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -import json -import unittest -import binascii -from urlparse import parse_qs - -from httmock import urlmatch, all_requests, HTTMock, response -from requests.exceptions import Timeout -import srp -from pixelated.bitmask_libraries.leap_srp import LeapSecureRemotePassword, LeapAuthException - - -(salt_bytes, verification_key_bytes) = srp.create_salted_verification_key('username', 'password', hash_alg=srp.SHA256, ng_type=srp.NG_1024) -verifier = None - - -@all_requests -def not_found_mock(url, request): - return {'status_code': 404, - 'content': 'foobar'} - - -@all_requests -def timeout_mock(url, request): - raise Timeout() - - -@urlmatch(netloc=r'(.*\.)?leap\.local$') -def srp_login_server_simulator_mock(url, request): - global verifier - - data = parse_qs(request.body) - if 'login' in data: - # SRP Authentication Step 1 - A = binascii.unhexlify(data.get('A')[0]) - - verifier = srp.Verifier('username', salt_bytes, verification_key_bytes, A, hash_alg=srp.SHA256, ng_type=srp.NG_1024) - (salt, B) = verifier.get_challenge() - - content = { - 'salt': binascii.hexlify(salt), - 'B': binascii.hexlify(B) - } - - return {'status_code': 200, - 'content': json.dumps(content)} - - else: - # SRP Authentication Step 2 - data = parse_qs(request.body) - client_auth = binascii.unhexlify(data.get('client_auth')[0]) - - M2 = verifier.verify_session(client_auth) - - if not verifier.authenticated(): - return {'status_code': 404, - 'content': ''} - - content = { - 'M2': binascii.hexlify(M2), - 'id': 'some id', - 'token': 'some token' - } - headers = { - 'Content-Type': 'application/json', - 'Set-Cookie': '_session_id=some_session_id;'} - return response(200, content, headers, None, 5, request) - - -class LeapSRPTest(unittest.TestCase): - - def test_status_code_is_checked(self): - with HTTMock(not_found_mock): - lsrp = LeapSecureRemotePassword() - self.assertRaises(LeapAuthException, lsrp.authenticate, 'https://api.leap.local', 'username', 'password') - - def test_invalid_username(self): - with HTTMock(srp_login_server_simulator_mock): - lsrp = LeapSecureRemotePassword() - self.assertRaises(LeapAuthException, lsrp.authenticate, 'https://api.leap.local', 'invalid_user', 'password') - - def test_invalid_password(self): - with HTTMock(srp_login_server_simulator_mock): - lsrp = LeapSecureRemotePassword() - self.assertRaises(LeapAuthException, lsrp.authenticate, 'https://api.leap.local', 'username', 'invalid') - - def test_login(self): - with HTTMock(srp_login_server_simulator_mock): - lsrp = LeapSecureRemotePassword() - leap_session = lsrp.authenticate('https://api.leap.local', 'username', 'password') - - self.assertIsNotNone(leap_session) - self.assertEqual('username', leap_session.user_name) - self.assertEqual('1', leap_session.api_version) - self.assertEqual('https://api.leap.local', leap_session.api_server_name) - self.assertEqual('some token', leap_session.token) - self.assertEqual('some_session_id', leap_session.session_id) - - def test_timeout(self): - with HTTMock(timeout_mock): - lrsp = LeapSecureRemotePassword() - self.assertRaises(LeapAuthException, lrsp.authenticate, 'https://api.leap.local', 'username', 'password') - - def test_register_raises_auth_exception_on_error(self): - with HTTMock(not_found_mock): - lsrp = LeapSecureRemotePassword() - self.assertRaises(LeapAuthException, lsrp.register, 'https://api.leap.local', 'username', 'password') - - def test_register(self): - @urlmatch(netloc=r'(.*\.)?leap\.local$', path='/1/users') - def register_success(url, request): - - content = { - 'login': 'username', - 'ok': True - } - - return {'status_code': 201, - 'content': content} - - with HTTMock(register_success, not_found_mock): - lsrp = LeapSecureRemotePassword() - self.assertTrue(lsrp.register('https://api.leap.local', 'username', 'password')) - - def test_register_user_exists(self): - @urlmatch(netloc=r'(.*\.)?leap\.local$', path='/1/users') - def register_error_user_exists(url, request): - content = {"errors": { - "login": [ - "has already been taken", "has already been taken", "has already been taken" - ]}} - - return {'status_code': 422, - 'content': content} - - with HTTMock(register_error_user_exists, not_found_mock): - lsrp = LeapSecureRemotePassword() - self.assertRaises(LeapAuthException, lsrp.register, 'https://api.leap.local', 'username', 'password') - - def test_registration_timeout(self): - with HTTMock(timeout_mock): - lsrp = LeapSecureRemotePassword() - self.assertRaises(LeapAuthException, lsrp.register, 'https://api.leap.local', 'username', 'password') diff --git a/service/test/unit/bitmask_libraries/test_nicknym.py b/service/test/unit/bitmask_libraries/test_nicknym.py index 6e589ae9..b892c22c 100644 --- a/service/test/unit/bitmask_libraries/test_nicknym.py +++ b/service/test/unit/bitmask_libraries/test_nicknym.py @@ -26,12 +26,17 @@ class NickNymTest(AbstractLeapTest): # given # when - NickNym(self.provider, self.config, self.soledad_session, self.srp_session) + NickNym(self.provider, + self.config, + self.soledad_session, + self.auth.username, + self.auth.token, + self.auth.uuid) # then init_mock.assert_called_with('test_user@some-server.test', 'https://nicknym.some-server.test:6425/', - self.soledad, self.token, '/some/path/to/provider_ca_cert', - 'https://api.some-server.test:4430', '1', self.uuid, + self.soledad, self.auth.token, '/some/path/to/provider_ca_cert', + 'https://api.some-server.test:4430', '1', self.auth.uuid, '/path/to/gpg') @patch('pixelated.bitmask_libraries.nicknym.KeyManager') @@ -39,7 +44,12 @@ class NickNymTest(AbstractLeapTest): # given keyman = keymanager_mock.return_value keyman.get_key.side_effect = KeyNotFound - nicknym = NickNym(self.provider, self.config, self.soledad_session, self.srp_session) + nicknym = NickNym(self.provider, + self.config, + self.soledad_session, + self.auth.username, + self.auth.token, + self.auth.uuid) # when/then nicknym.generate_openpgp_key() diff --git a/service/test/unit/bitmask_libraries/test_session.py b/service/test/unit/bitmask_libraries/test_session.py index 0bfd59f2..62330481 100644 --- a/service/test/unit/bitmask_libraries/test_session.py +++ b/service/test/unit/bitmask_libraries/test_session.py @@ -65,7 +65,7 @@ class SessionTest(AbstractLeapTest): self.assertEqual('test_user@some-server.test', session.account_email()) def _create_session(self): - return LeapSession(self.provider, self.srp_session, self.soledad_session, self.nicknym, self.soledad_account, + return LeapSession(self.provider, self.auth, self.soledad_session, self.nicknym, self.soledad_account, self.mail_fetcher_mock, self.smtp_mock) diff --git a/service/test/unit/bitmask_libraries/test_smtp.py b/service/test/unit/bitmask_libraries/test_smtp.py index c0e30573..aa2bc9ba 100644 --- a/service/test/unit/bitmask_libraries/test_smtp.py +++ b/service/test/unit/bitmask_libraries/test_smtp.py @@ -53,7 +53,7 @@ class LeapSmtpTest(AbstractLeapTest): self.config.timeout_in_s = 15 def test_that_client_cert_gets_downloaded(self): - smtp = LeapSmtp(self.provider, self.keymanager, self.srp_session) + smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager) with HTTMock(ca_cert_mock, not_found_mock): smtp._download_client_certificates() @@ -66,7 +66,8 @@ class LeapSmtpTest(AbstractLeapTest): @patch('pixelated.bitmask_libraries.smtp.setup_smtp_gateway') def test_that_start_calls_setup_smtp_gateway(self, gateway_mock): - smtp = LeapSmtp(self.provider, self.keymanager, self.srp_session) + smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager) + port = 500 smtp.TWISTED_PORT = port gateway_mock.return_value = (None, None) @@ -77,14 +78,14 @@ class LeapSmtpTest(AbstractLeapTest): gateway_mock.assert_called_with(keymanager=self.keymanager, smtp_cert=cert_path, smtp_key=cert_path, userid='test_user@some-server.test', smtp_port='1234', encrypted_only=False, smtp_host='smtp.some-sever.test', port=port) def test_that_client_stop_does_nothing_if_not_started(self): - smtp = LeapSmtp(self.provider, self.keymanager, self.srp_session) + smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager) with HTTMock(not_found_mock): smtp.stop() @patch('pixelated.bitmask_libraries.smtp.setup_smtp_gateway') def test_that_running_smtp_sevice_is_stopped(self, gateway_mock): - smtp = LeapSmtp(self.provider, self.keymanager, self.srp_session) + smtp = LeapSmtp(self.provider, self.auth.username, self.auth.session_id, self.keymanager) smtp_service = MagicMock() smtp_port = MagicMock() diff --git a/service/test/unit/bitmask_libraries/test_soledad.py b/service/test/unit/bitmask_libraries/test_soledad.py index a71275e0..a3a1094a 100644 --- a/service/test/unit/bitmask_libraries/test_soledad.py +++ b/service/test/unit/bitmask_libraries/test_soledad.py @@ -34,19 +34,19 @@ class SoledadSessionTest(AbstractLeapTest): @patch('pixelated.bitmask_libraries.soledad.Soledad.__init__') def test_that_soledad_is_created_with_required_params(self, soledad_mock, init_mock): # when - SoledadSession(self.provider, 'any-passphrase', self.srp_session) + SoledadSession(self.provider, 'any-passphrase', self.auth.token, self.auth.uuid) # then - init_mock.assert_called_with(self.uuid, 'any-passphrase', '%s/soledad/%s.secret' % (self.leap_home, self.uuid), - '%s/soledad/%s.db' % (self.leap_home, self.uuid), - 'https://couch1.some-server.test:1234/user-%s' % self.uuid, + init_mock.assert_called_with(self.auth.uuid, 'any-passphrase', '%s/soledad/%s.secret' % (self.leap_home, self.auth.uuid), + '%s/soledad/%s.db' % (self.leap_home, self.auth.uuid), + 'https://couch1.some-server.test:1234/user-%s' % self.auth.uuid, '/some/path/to/ca_cert', self.token, defer_encryption=False) def test_that_sync_is_called(self, soledad_mock): instance = soledad_mock.return_value instance.server_url = '/foo/bar' instance.need_sync.return_value = True - soledad_session = SoledadSession(self.provider, 'any-passphrase', self.srp_session) + soledad_session = SoledadSession(self.provider, 'any-passphrase', self.auth.token, self.auth.uuid) # when soledad_session.sync() @@ -59,7 +59,7 @@ class SoledadSessionTest(AbstractLeapTest): instance = mock.return_value instance.server_url = '/foo/bar' instance.need_sync.return_value = False - soledad_session = SoledadSession(self.provider, 'any-passphrase', self.srp_session) + soledad_session = SoledadSession(self.provider, 'any-passphrase', self.auth.token, self.auth.uuid) # when soledad_session.sync() |