From 9cdd52be577fff75830c854bd7738ee1649e7083 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Fri, 19 Aug 2016 21:37:34 -0300 Subject: Started deferring leap session creation #759 Started adapting get_leap_session to deferreds Soledad and keymanager setup calls will now happen in deferreds and leap session creation itself is a deferred with callbacks This is a start in breaking the big blocking calls we were doing on the main thread, this was done without changing code inside the leap libraries yet so things can be further optimized This breaks the ~4 seconds get_leap_session piece into smaller 1 seconds one, that can be further optimized and deferred to even smaller calls There are requests calls happening on the main thread that should get this number even further down Also moved some pieces from bitmask libraries to our bootstrap, because they are not bitmask libraries anymore and that was causing confusion --- .../test/support/integration/app_test_client.py | 4 +- .../test/support/integration/multi_user_client.py | 2 +- .../unit/bitmask_libraries/test_abstract_leap.py | 2 +- service/test/unit/bitmask_libraries/test_certs.py | 6 +- .../test/unit/bitmask_libraries/test_keymanager.py | 65 ++++++++ .../test/unit/bitmask_libraries/test_nicknym.py | 66 -------- .../test/unit/bitmask_libraries/test_provider.py | 44 +++--- .../test/unit/bitmask_libraries/test_session.py | 172 --------------------- .../bitmask_libraries/test_smtp_cert_downloader.py | 84 ---------- .../test_smtp_client_certificate.py | 50 +++++- service/test/unit/config/test_leap.py | 8 +- service/test/unit/config/test_sessions.py | 160 +++++++++++++++++++ service/test/unit/maintenance/test_commands.py | 2 +- service/test/unit/resources/test_login_resource.py | 4 +- service/test/unit/resources/test_users_resource.py | 2 +- 15 files changed, 301 insertions(+), 370 deletions(-) create mode 100644 service/test/unit/bitmask_libraries/test_keymanager.py delete mode 100644 service/test/unit/bitmask_libraries/test_nicknym.py delete mode 100644 service/test/unit/bitmask_libraries/test_session.py delete mode 100644 service/test/unit/bitmask_libraries/test_smtp_cert_downloader.py create mode 100644 service/test/unit/config/test_sessions.py (limited to 'service/test') diff --git a/service/test/support/integration/app_test_client.py b/service/test/support/integration/app_test_client.py index c2bf22cb..5d1e760a 100644 --- a/service/test/support/integration/app_test_client.py +++ b/service/test/support/integration/app_test_client.py @@ -37,8 +37,7 @@ from twisted.cred import checkers, credentials from pixelated.adapter.mailstore.leap_attachment_store import LeapAttachmentStore from pixelated.adapter.services.feedback_service import FeedbackService from pixelated.application import ServicesFactory, UserAgentMode, SingleUserServicesFactory, set_up_protected_resources -from pixelated.bitmask_libraries.config import LeapConfig -from pixelated.bitmask_libraries.session import LeapSession +from pixelated.config.sessions import LeapSession from pixelated.config.services import Services, ServicesFactory, SingleUserServicesFactory from pixelated.config.site import PixelatedSite @@ -197,7 +196,6 @@ class AppTestClient(object): else: self.service_factory = StubServicesFactory(self.accounts, mode) provider = mock() - provider.config = LeapConfig(self._tmp_dir.name) self.resource = set_up_protected_resources(RootResource(self.service_factory), provider, self.service_factory, checker=StubSRPChecker(provider)) diff --git a/service/test/support/integration/multi_user_client.py b/service/test/support/integration/multi_user_client.py index d6133e64..4b2cb832 100644 --- a/service/test/support/integration/multi_user_client.py +++ b/service/test/support/integration/multi_user_client.py @@ -22,7 +22,7 @@ from leap.auth import SRPAuth from pixelated.application import UserAgentMode, set_up_protected_resources from pixelated.config.services import ServicesFactory -from pixelated.bitmask_libraries.session import LeapSession, LeapSessionFactory +from pixelated.config.sessions import LeapSessionFactory, LeapSession import pixelated.config.services from pixelated.resources.root_resource import RootResource from test.support.integration import AppTestClient diff --git a/service/test/unit/bitmask_libraries/test_abstract_leap.py b/service/test/unit/bitmask_libraries/test_abstract_leap.py index 6f2351cd..237a1152 100644 --- a/service/test/unit/bitmask_libraries/test_abstract_leap.py +++ b/service/test/unit/bitmask_libraries/test_abstract_leap.py @@ -40,7 +40,7 @@ class AbstractLeapTest(unittest.TestCase): uuid=self._uuid, token=self._token) - self.nicknym = MagicMock() + self.keymanager = MagicMock() self.soledad_account = MagicMock() diff --git a/service/test/unit/bitmask_libraries/test_certs.py b/service/test/unit/bitmask_libraries/test_certs.py index 5d447537..bd9b32d3 100644 --- a/service/test/unit/bitmask_libraries/test_certs.py +++ b/service/test/unit/bitmask_libraries/test_certs.py @@ -1,14 +1,16 @@ import unittest from pixelated.bitmask_libraries.certs import LeapCertificate +from pixelated.config import leap_config from mock import MagicMock class CertsTest(unittest.TestCase): def setUp(self): - config = MagicMock(leap_home='/some/leap/home') - self.provider = MagicMock(server_name=u'test.leap.net', config=config) + leap_config.leap_home = '/some/leap/home' + + self.provider = MagicMock(server_name=u'test.leap.net') def test_set_cert_and_fingerprint_sets_cert(self): LeapCertificate.set_cert_and_fingerprint('some cert', None) diff --git a/service/test/unit/bitmask_libraries/test_keymanager.py b/service/test/unit/bitmask_libraries/test_keymanager.py new file mode 100644 index 00000000..1a1038b8 --- /dev/null +++ b/service/test/unit/bitmask_libraries/test_keymanager.py @@ -0,0 +1,65 @@ +# +# 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 . +from mock import patch +from mockito import when + +from test_abstract_leap import AbstractLeapTest +from leap.keymanager import openpgp, KeyNotFound +from pixelated.bitmask_libraries.keymanager import Keymanager +from pixelated.bitmask_libraries.certs import LeapCertificate +from pixelated.config import leap_config + + +class KeymanagerTest(AbstractLeapTest): + @patch('pixelated.bitmask_libraries.keymanager.KeyManager') + def test_that_keymanager_is_created(self, keymanager_mock): + LeapCertificate.provider_api_cert = '/some/path/to/provider_ca_cert' + when(self.provider)._discover_nicknym_server().thenReturn('https://nicknym.some-server.test:6425/') + leap_config.gpg_binary = '/path/to/gpg' + + Keymanager(self.provider, + self.soledad, + 'test_user@some-server.test', + self.auth.token, + self.auth.uuid) + + keymanager_mock.assert_called_with( + 'test_user@some-server.test', + 'https://nicknym.some-server.test:6425/', + self.soledad, + token=self.auth.token, + ca_cert_path='/some/path/to/provider_ca_cert', + api_uri='https://api.some-server.test:4430', + api_version='1', + uid=self.auth.uuid, + gpgbinary='/path/to/gpg') + + @patch('pixelated.bitmask_libraries.keymanager.KeyManager') + def test_gen_key(self, keymanager_mock): + # given + keyman = keymanager_mock.return_value + keyman.get_key.side_effect = KeyNotFound + keymanager = Keymanager(self.provider, + self.soledad, + 'test_user@some-server.test', + self.auth.token, + self.auth.uuid) + + # when/then + keymanager.generate_openpgp_key() + + keyman.get_key.assert_called_with('test_user@some-server.test', private=True, fetch_remote=False) + keyman.gen_key.assert_called() diff --git a/service/test/unit/bitmask_libraries/test_nicknym.py b/service/test/unit/bitmask_libraries/test_nicknym.py deleted file mode 100644 index d15f1a75..00000000 --- a/service/test/unit/bitmask_libraries/test_nicknym.py +++ /dev/null @@ -1,66 +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 . -from mock import patch - -from test_abstract_leap import AbstractLeapTest -from leap.keymanager import openpgp, KeyNotFound -from pixelated.bitmask_libraries.nicknym import NickNym -from pixelated.bitmask_libraries.certs import LeapCertificate - - -class NickNymTest(AbstractLeapTest): - # @patch('pixelated.bitmask_libraries.nicknym.KeyManager.__init__', return_value=None) - @patch('pixelated.bitmask_libraries.nicknym.KeyManager') - def test_that_keymanager_is_created(self, keymanager_mock): - # given - LeapCertificate.provider_api_cert = '/some/path/to/provider_ca_cert' - # when - NickNym(self.provider, - self.config, - self.soledad, - 'test_user@some-server.test', - self.auth.token, - self.auth.uuid) - - # then - keymanager_mock.assert_called_with( - 'test_user@some-server.test', - 'https://nicknym.some-server.test:6425/', - self.soledad, - token=self.auth.token, - ca_cert_path='/some/path/to/provider_ca_cert', - api_uri='https://api.some-server.test:4430', - api_version='1', - uid=self.auth.uuid, - gpgbinary='/path/to/gpg') - - @patch('pixelated.bitmask_libraries.nicknym.KeyManager') - def test_gen_key(self, keymanager_mock): - # given - keyman = keymanager_mock.return_value - keyman.get_key.side_effect = KeyNotFound - nicknym = NickNym(self.provider, - self.config, - self.soledad, - 'test_user@some-server.test', - self.auth.token, - self.auth.uuid) - - # when/then - nicknym.generate_openpgp_key() - - keyman.get_key.assert_called_with('test_user@some-server.test', private=True, fetch_remote=False) - keyman.gen_key.assert_called() diff --git a/service/test/unit/bitmask_libraries/test_provider.py b/service/test/unit/bitmask_libraries/test_provider.py index df851203..1284698f 100644 --- a/service/test/unit/bitmask_libraries/test_provider.py +++ b/service/test/unit/bitmask_libraries/test_provider.py @@ -18,9 +18,9 @@ import json from mock import patch, MagicMock, ANY from httmock import all_requests, HTTMock, urlmatch from requests import HTTPError -from pixelated.bitmask_libraries.config import LeapConfig from pixelated.bitmask_libraries.provider import LeapProvider from pixelated.bitmask_libraries.certs import LeapCertificate +from pixelated.config import leap_config from test_abstract_leap import AbstractLeapTest import requests @@ -139,12 +139,12 @@ PROVIDER_WEB_CERT = '/tmp/bootstrap-ca.crt' class LeapProviderTest(AbstractLeapTest): def setUp(self): - self.config = LeapConfig(leap_home='/tmp/foobar') + leap_config.set_leap_home('/tmp/foobar') LeapCertificate.set_cert_and_fingerprint(PROVIDER_WEB_CERT, None) def test_provider_fetches_provider_json(self): - with HTTMock(provider_json_mock): - provider = LeapProvider('some-provider.test', self.config) + with HTTMock(provider_json_mock, soledad_json_mock): + provider = LeapProvider('some-provider.test') self.assertEqual("1", provider.api_version) self.assertEqual("some-provider.test", provider.domain) @@ -156,42 +156,36 @@ class LeapProviderTest(AbstractLeapTest): def test_provider_json_throws_exception_on_status_code(self): with HTTMock(not_found_mock): - self.assertRaises(HTTPError, LeapProvider, 'some-provider.test', self.config) + self.assertRaises(HTTPError, LeapProvider, 'some-provider.test') def test_fetch_soledad_json(self): with HTTMock(provider_json_mock, soledad_json_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + provider = LeapProvider('some-provider.test') soledad = provider.fetch_soledad_json() self.assertEqual("some value", soledad.get('some key')) - def test_throw_exception_for_fetch_soledad_status_code(self): - with HTTMock(provider_json_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) - - self.assertRaises(HTTPError, provider.fetch_soledad_json) - def test_fetch_smtp_json(self): - with HTTMock(provider_json_mock, smtp_json_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + with HTTMock(provider_json_mock, soledad_json_mock, smtp_json_mock, not_found_mock): + provider = LeapProvider('some-provider.test') smtp = provider.fetch_smtp_json() self.assertEqual('mx.some-provider.test', smtp.get('hosts').get('leap-mx').get('hostname')) def test_throw_exception_for_fetch_smtp_status_code(self): - with HTTMock(provider_json_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + with HTTMock(provider_json_mock, soledad_json_mock, not_found_mock): + provider = LeapProvider('some-provider.test') self.assertRaises(HTTPError, provider.fetch_smtp_json) def test_fetch_valid_certificate(self): - with HTTMock(provider_json_mock, ca_cert_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + with HTTMock(provider_json_mock, soledad_json_mock, ca_cert_mock, not_found_mock): + provider = LeapProvider('some-provider.test') provider.fetch_valid_certificate() def test_throw_exception_for_invalid_certificate(self): expected_exception_message = 'Certificate fingerprints don\'t match! Expected [0123456789012345678901234567890123456789012345678901234567890123] but got [06e2300bdbc118c290eda0dc977c24080718f4eeca68c8b0ad431872a2baa22d]' - with HTTMock(provider_json_invalid_fingerprint_mock, ca_cert_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + with HTTMock(provider_json_invalid_fingerprint_mock, soledad_json_mock, ca_cert_mock, not_found_mock): + provider = LeapProvider('some-provider.test') with self.assertRaises(Exception) as cm: provider.fetch_valid_certificate() self.assertEqual(expected_exception_message, cm.exception.message) @@ -204,8 +198,8 @@ class LeapProviderTest(AbstractLeapTest): with patch('pixelated.bitmask_libraries.provider.requests.session', new=session_func): with patch('pixelated.bitmask_libraries.provider.requests.get', new=get_func): - with HTTMock(provider_json_mock, ca_cert_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + with HTTMock(provider_json_mock, ca_cert_mock, soledad_json_mock, not_found_mock): + provider = LeapProvider('some-provider.test') provider.fetch_valid_certificate() session.get.assert_any_call('https://some-provider.test/ca.crt', verify=PROVIDER_WEB_CERT, timeout=15) @@ -217,7 +211,7 @@ class LeapProviderTest(AbstractLeapTest): with patch('pixelated.bitmask_libraries.provider.requests.get', new=get_func): with HTTMock(provider_json_mock, soledad_json_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + provider = LeapProvider('some-provider.test') provider.fetch_soledad_json() get_func.assert_called_with('https://api.some-provider.test:4430/1/config/soledad-service.json', verify=PROVIDER_API_CERT, timeout=15) @@ -227,8 +221,8 @@ class LeapProviderTest(AbstractLeapTest): LeapCertificate.set_cert_and_fingerprint(None, 'some fingerprint') with patch('pixelated.bitmask_libraries.provider.requests.session', new=session_func): - with HTTMock(provider_json_mock, ca_cert_mock, not_found_mock): - provider = LeapProvider('some-provider.test', self.config) + with HTTMock(provider_json_mock, ca_cert_mock, soledad_json_mock, not_found_mock): + provider = LeapProvider('some-provider.test') provider.fetch_valid_certificate() session.get.assert_any_call('https://some-provider.test/ca.crt', verify=False, timeout=15) diff --git a/service/test/unit/bitmask_libraries/test_session.py b/service/test/unit/bitmask_libraries/test_session.py deleted file mode 100644 index 84f9f023..00000000 --- a/service/test/unit/bitmask_libraries/test_session.py +++ /dev/null @@ -1,172 +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 . - -from mock import patch -from mock import MagicMock -from twisted.internet import defer -from pixelated.bitmask_libraries.session import LeapSession, SessionCache -from test_abstract_leap import AbstractLeapTest -from leap.common.events.catalog import KEYMANAGER_FINISHED_KEY_GENERATION - - -class SessionTest(AbstractLeapTest): - - def setUp(self): - super(SessionTest, self).setUp() - self.smtp_mock = MagicMock() - - @patch('pixelated.bitmask_libraries.session.register') - @patch('pixelated.bitmask_libraries.session.Account') - @defer.inlineCallbacks - def test_background_jobs_are_started_during_initial_sync(self, *unused): - mailFetcherMock = MagicMock() - with patch('pixelated.bitmask_libraries.session.reactor.callFromThread', new=_execute_func) as _: - with patch.object(LeapSession, '_create_incoming_mail_fetcher', return_value=mailFetcherMock) as _: - session = self._create_session() - yield session.initial_sync() - mailFetcherMock.startService.assert_called_once() - - @patch('pixelated.bitmask_libraries.session.register') - @patch('pixelated.bitmask_libraries.session.unregister') - @patch('pixelated.bitmask_libraries.session.Account') - @defer.inlineCallbacks - def test_that_close_stops_background_jobs(self, *unused): - with patch('pixelated.bitmask_libraries.session.reactor.callFromThread', new=_execute_func) as _: - with patch('pixelated.bitmask_libraries.session.LeapSession._create_incoming_mail_fetcher') as mail_fetcher_mock: - session = self._create_session() - yield session.initial_sync() - session.close() - mail_fetcher_mock.stopService.assert_called_once() - - def test_that_sync_deferes_to_soledad(self): - with patch('pixelated.bitmask_libraries.session.reactor.callFromThread', new=_execute_func) as _: - with patch('pixelated.bitmask_libraries.session.LeapSession._create_incoming_mail_fetcher') as mail_fetcher_mock: - session = self._create_session() - yield session.sync() - self.soledad_session.sync.assert_called_once() - - def test_session_registers_to_generated_keys(self): - email = 'someone@somedomain.tld' - self.provider.address_for.return_value = email - with patch('pixelated.bitmask_libraries.session.register') as register_mock: - session = self._create_session() - - register_mock.assert_called_once_with(KEYMANAGER_FINISHED_KEY_GENERATION, session._set_fresh_account, uid=email) - - @patch('pixelated.bitmask_libraries.session.register') - def test_close_unregisters_from_generate_keys_events(self, _): - email = 'someone@somedomain.tld' - self.provider.address_for.return_value = email - session = self._create_session() - - with patch('pixelated.bitmask_libraries.session.unregister') as unregister_mock: - session.close() - - unregister_mock.assert_called_once_with(KEYMANAGER_FINISHED_KEY_GENERATION, uid=email) - - @patch('pixelated.bitmask_libraries.session.register') - def test_close_stops_soledad(self, _): - email = 'someone@somedomain.tld' - self.provider.address_for.return_value = email - session = self._create_session() - - with patch('pixelated.bitmask_libraries.session.unregister') as unregister_mock: - session.close() - - self.soledad_session.close.assert_called_once_with() - - @patch('pixelated.bitmask_libraries.session.register') - def test_close_removes_session_from_cache(self, _): - email = 'someone@somedomain.tld' - self.provider.address_for.return_value = email - session = self._create_session() - - key = SessionCache.session_key(self.provider, self.auth.username) - SessionCache.remember_session(key, session) - - self.assertEqual(session, SessionCache.lookup_session(key)) - - with patch('pixelated.bitmask_libraries.session.unregister') as unregister_mock: - session.close() - - self.assertIsNone(SessionCache.lookup_session(key)) - - @patch('pixelated.bitmask_libraries.session.register') - def test_close_ends_account_session(self, _): - account_mock = MagicMock() - email = 'someone@somedomain.tld' - self.provider.address_for.return_value = email - session = self._create_session() - session.account = account_mock - - with patch('pixelated.bitmask_libraries.session.unregister') as unregister_mock: - session.close() - - account_mock.end_session.assert_called_once_with() - - @patch('pixelated.bitmask_libraries.session.register') - def test_session_fresh_is_initially_false(self, _): - session = self._create_session() - - self.assertFalse(session.fresh_account) - - @patch('pixelated.bitmask_libraries.session.register') - def test_session_sets_status_to_fresh_on_key_generation_event(self, _): - session = self._create_session() - self.provider.address_for.return_value = 'someone@somedomain.tld' - - session._set_fresh_account(None, 'someone@somedomain.tld') - - self.assertTrue(session.fresh_account) - - @patch('pixelated.bitmask_libraries.session.register') - def test_closed_session_not_reused(self, _): - session = self._create_session() - SessionCache.remember_session('somekey', session) - session._is_closed = True - - result = SessionCache.lookup_session('somekey') - - self.assertIsNone(result) - - @patch('pixelated.bitmask_libraries.session.register') - def test_session_does_not_set_status_fresh_for_unkown_emails(self, _): - session = self._create_session() - self.provider.address_for.return_value = 'someone@somedomain.tld' - - session._set_fresh_account(None, 'another_email@somedomain.tld') - - self.assertFalse(session.fresh_account) - - @patch('pixelated.bitmask_libraries.session.register') - @patch('pixelated.bitmask_libraries.session.unregister') - @patch('pixelated.bitmask_libraries.session.Account') - @defer.inlineCallbacks - def test_session_initial_sync_only_triggered_once(self, *unused): - mailFetcherMock = MagicMock() - with patch('pixelated.bitmask_libraries.session.reactor.callFromThread', new=_execute_func) as _: - with patch.object(LeapSession, '_create_incoming_mail_fetcher', return_value=mailFetcherMock) as _: - session = self._create_session() - session._has_been_initially_synced = True - yield session.initial_sync() - self.assertFalse(mailFetcherMock.startService.called) - - def _create_session(self): - return LeapSession(self.provider, self.auth, self.mail_store, self.soledad_session, self.nicknym, self.smtp_mock) - - -def _execute_func(func): - func() diff --git a/service/test/unit/bitmask_libraries/test_smtp_cert_downloader.py b/service/test/unit/bitmask_libraries/test_smtp_cert_downloader.py deleted file mode 100644 index 2a4afa40..00000000 --- a/service/test/unit/bitmask_libraries/test_smtp_cert_downloader.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# Copyright (c) 2015 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 . -import unittest -from mockito import mock, unstub -from requests import HTTPError -from pixelated.bitmask_libraries.session import SmtpCertDownloader -from tempfile import NamedTemporaryFile -from httmock import all_requests, HTTMock, urlmatch - -CERTIFICATE_DATA = 'some cert data' -USERNAME = 'some_user_name' - - -@all_requests -def not_found_mock(url, request): - return {'status_code': 404, - 'content': 'foobar'} - - -@urlmatch(netloc='api.some-server.test:4430', path='/1/smtp_cert', method='POST') -def smtp_cert_mock(url, request): - if request.body == 'address=%s' % USERNAME: - return { - "status_code": 200, - "content": CERTIFICATE_DATA - } - else: - return { - 'status_code': 401 - } - - -class TestSmtpCertDownloader(unittest.TestCase): - - def setUp(self): - self._provider = mock() - self._config = mock() - self._config.leap_home = '/tmp' - self._auth = mock() - - self._provider.config = self._config - self._provider.api_uri = 'https://api.some-server.test:4430' - self._provider.api_version = '1' - self._provider.server_name = 'some.host.tld' - - self._auth.username = USERNAME - self._auth.token = 'some token' - - def tearDown(self): - unstub() - - def test_download_certificate(self): - with HTTMock(smtp_cert_mock, not_found_mock): - cert_data = SmtpCertDownloader(self._provider, self._auth).download() - - self.assertEqual(CERTIFICATE_DATA, cert_data) - - def test_error_if_not_found(self): - downloader = SmtpCertDownloader(self._provider, self._auth) - with HTTMock(not_found_mock): - self.assertRaises(HTTPError, downloader.download) - - def test_download_to(self): - downloader = SmtpCertDownloader(self._provider, self._auth) - - with NamedTemporaryFile() as tmp_file: - with HTTMock(smtp_cert_mock, not_found_mock): - downloader.download_to(tmp_file.name) - - file_content = open(tmp_file.name).read() - self.assertEqual(CERTIFICATE_DATA, file_content) diff --git a/service/test/unit/bitmask_libraries/test_smtp_client_certificate.py b/service/test/unit/bitmask_libraries/test_smtp_client_certificate.py index e938d6f5..050f2d94 100644 --- a/service/test/unit/bitmask_libraries/test_smtp_client_certificate.py +++ b/service/test/unit/bitmask_libraries/test_smtp_client_certificate.py @@ -16,12 +16,36 @@ import os import unittest import tempdir -from pixelated.bitmask_libraries import session from leap.srp_session import SRPSession import leap.common.certs as certs from mockito import mock, unstub, when, verify, never, any as ANY -from pixelated.bitmask_libraries.session import SmtpClientCertificate +from pixelated.config.sessions import SmtpClientCertificate + +from tempfile import NamedTemporaryFile +from httmock import all_requests, HTTMock, urlmatch + +CERTIFICATE_DATA = 'some cert data' +USERNAME = 'some_user_name' + + +@all_requests +def not_found_mock(url, request): + return {'status_code': 404, + 'content': 'foobar'} + + +@urlmatch(netloc='api.some-server.test:4430', path='/1/smtp_cert', method='POST') +def smtp_cert_mock(url, request): + if request.body == 'address=%s' % USERNAME: + return { + "status_code": 200, + "content": CERTIFICATE_DATA + } + else: + return { + 'status_code': 401 + } class TestSmtpClientCertificate(unittest.TestCase): @@ -29,11 +53,12 @@ class TestSmtpClientCertificate(unittest.TestCase): def setUp(self): self.tmp_dir = tempdir.TempDir() self.provider = mock() + self.provider.api_uri = 'https://api.some-server.test:4430' + self.provider.api_version = '1' + self.provider.server_name = 'some.host.tld' self.provider.domain = 'some-provider.tld' - self.auth = SRPSession('username', 'token', 'uuid', 'session_id', {}) + self.auth = SRPSession(USERNAME, 'token', 'uuid', 'session_id', {}) self.pem_path = os.path.join(self.tmp_dir.name, 'providers', 'some-provider.tld', 'keys', 'client', 'smtp.pem') - self.downloader = mock() - when(session).SmtpCertDownloader(self.provider, self.auth).thenReturn(self.downloader) def tearDown(self): self.tmp_dir.dissolve() @@ -41,20 +66,20 @@ class TestSmtpClientCertificate(unittest.TestCase): def test_download_certificate(self): cert = SmtpClientCertificate(self.provider, self.auth, self.tmp_dir.name) + when(cert).download_to(ANY()).thenReturn(None) result = cert.cert_path() self.assertEqual(self.pem_path, result) - verify(self.downloader).download_to(self.pem_path) def test_download_certificate_if_redownload_necessary_e_g_certificate_expired(self): self.pretend_all_paths_exist() when(certs).should_redownload(self.pem_path).thenReturn(True) cert = SmtpClientCertificate(self.provider, self.auth, self.tmp_dir.name) + when(cert).download_to(ANY()).thenReturn(None) result = cert.cert_path() self.assertEqual(self.pem_path, result) - verify(self.downloader).download_to(self.pem_path) def pretend_all_paths_exist(self): when(os.path).exists(ANY()).thenReturn(True) @@ -66,4 +91,13 @@ class TestSmtpClientCertificate(unittest.TestCase): result = cert.cert_path() self.assertEqual(self.pem_path, result) - verify(self.downloader, never).download_to(ANY()) + + def test_download_to(self): + cert = SmtpClientCertificate(self.provider, self.auth, self.tmp_dir.name) + + with NamedTemporaryFile() as tmp_file: + with HTTMock(smtp_cert_mock, not_found_mock): + cert.download_to(tmp_file.name) + + file_content = open(tmp_file.name).read() + self.assertEqual(CERTIFICATE_DATA, file_content) diff --git a/service/test/unit/config/test_leap.py b/service/test/unit/config/test_leap.py index 5576cca8..b87065d2 100644 --- a/service/test/unit/config/test_leap.py +++ b/service/test/unit/config/test_leap.py @@ -9,7 +9,7 @@ class TestAuth(unittest.TestCase): @patch('pixelated.config.leap.LeapSessionFactory') @defer.inlineCallbacks - def test_create_leap_session_calls_initinal_sync(self, session_factory__ctor_mock): + def test_create_leap_session_calls_initial_sync(self, session_factory__ctor_mock): session_factory_mock = session_factory__ctor_mock.return_value provider_mock = MagicMock() auth_mock = MagicMock() @@ -19,7 +19,7 @@ class TestAuth(unittest.TestCase): yield create_leap_session(provider_mock, 'username', 'password', auth=auth_mock) - session.initial_sync.assert_called_with() + session.first_required_sync.assert_called_with() @patch('pixelated.config.leap.LeapSessionFactory') @defer.inlineCallbacks @@ -29,10 +29,10 @@ class TestAuth(unittest.TestCase): auth_mock = MagicMock() session = MagicMock() - session.initial_sync.side_effect = [InvalidAuthTokenError, defer.succeed(None)] + session.first_required_sync.side_effect = [InvalidAuthTokenError, defer.succeed(None)] session_factory_mock.create.return_value = session yield create_leap_session(provider_mock, 'username', 'password', auth=auth_mock) session.close.assert_called_with() - self.assertEqual(2, session.initial_sync.call_count) + self.assertEqual(2, session.first_required_sync.call_count) diff --git a/service/test/unit/config/test_sessions.py b/service/test/unit/config/test_sessions.py new file mode 100644 index 00000000..be418a73 --- /dev/null +++ b/service/test/unit/config/test_sessions.py @@ -0,0 +1,160 @@ +# +# 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 . + +from mock import patch +from mock import MagicMock +from twisted.internet import defer +from pixelated.config.sessions import LeapSession, SessionCache + +from test.unit.bitmask_libraries.test_abstract_leap import AbstractLeapTest +from leap.common.events.catalog import KEYMANAGER_FINISHED_KEY_GENERATION + + +class SessionTest(AbstractLeapTest): + + def setUp(self): + super(SessionTest, self).setUp() + self.smtp_mock = MagicMock() + + @patch('pixelated.config.sessions.register') + @patch('pixelated.config.sessions.Account') + @defer.inlineCallbacks + def test_background_jobs_are_started_during_initial_sync(self, *unused): + mailFetcherMock = MagicMock() + with patch('pixelated.config.sessions.reactor.callFromThread', new=_execute_func) as _: + with patch.object(LeapSession, '_create_incoming_mail_fetcher', return_value=mailFetcherMock) as _: + session = self._create_session() + yield session.first_required_sync() + mailFetcherMock.startService.assert_called_once() + + @patch('pixelated.config.sessions.register') + @patch('pixelated.config.sessions.unregister') + @patch('pixelated.config.sessions.Account') + @defer.inlineCallbacks + def test_that_close_stops_background_jobs(self, *unused): + with patch('pixelated.config.sessions.reactor.callFromThread', new=_execute_func) as _: + with patch('pixelated.config.sessions.LeapSession._create_incoming_mail_fetcher') as mail_fetcher_mock: + session = self._create_session() + yield session.first_required_sync() + session.close() + mail_fetcher_mock.stopService.assert_called_once() + + def test_that_sync_defers_to_soledad(self): + with patch('pixelated.config.sessions.reactor.callFromThread', new=_execute_func) as _: + with patch('pixelated.config.sessions.LeapSession._create_incoming_mail_fetcher') as mail_fetcher_mock: + session = self._create_session() + yield session.sync() + self.soledad_session.sync.assert_called_once() + + def test_session_registers_to_generated_keys(self): + email = 'someone@somedomain.tld' + self.provider.address_for.return_value = email + with patch('pixelated.config.sessions.register') as register_mock: + session = self._create_session() + + register_mock.assert_called_once_with(KEYMANAGER_FINISHED_KEY_GENERATION, session._set_fresh_account, uid=email) + + @patch('pixelated.config.sessions.register') + def test_close_unregisters_from_generate_keys_events(self, _): + email = 'someone@somedomain.tld' + self.provider.address_for.return_value = email + session = self._create_session() + + with patch('pixelated.config.sessions.unregister') as unregister_mock: + session.close() + + unregister_mock.assert_called_once_with(KEYMANAGER_FINISHED_KEY_GENERATION, uid=email) + + @patch('pixelated.config.sessions.register') + def test_close_stops_soledad(self, _): + email = 'someone@somedomain.tld' + self.provider.address_for.return_value = email + session = self._create_session() + + with patch('pixelated.config.sessions.unregister') as unregister_mock: + session.close() + + self.soledad_session.close.assert_called_once_with() + + @patch('pixelated.config.sessions.register') + def test_close_removes_session_from_cache(self, _): + email = 'someone@somedomain.tld' + self.provider.address_for.return_value = email + session = self._create_session() + + key = SessionCache.session_key(self.provider, self.auth.username) + SessionCache.remember_session(key, session) + + self.assertEqual(session, SessionCache.lookup_session(key)) + + with patch('pixelated.config.sessions.unregister') as unregister_mock: + session.close() + + self.assertIsNone(SessionCache.lookup_session(key)) + + @patch('pixelated.config.sessions.register') + def test_close_ends_account_session(self, _): + account_mock = MagicMock() + email = 'someone@somedomain.tld' + self.provider.address_for.return_value = email + session = self._create_session() + session.account = account_mock + + with patch('pixelated.config.sessions.unregister') as unregister_mock: + session.close() + + account_mock.end_session.assert_called_once_with() + + @patch('pixelated.config.sessions.register') + def test_session_fresh_is_initially_false(self, _): + session = self._create_session() + + self.assertFalse(session.fresh_account) + + @patch('pixelated.config.sessions.register') + def test_session_sets_status_to_fresh_on_key_generation_event(self, _): + session = self._create_session() + self.provider.address_for.return_value = 'someone@somedomain.tld' + + session._set_fresh_account(None, 'someone@somedomain.tld') + + self.assertTrue(session.fresh_account) + + @patch('pixelated.config.sessions.register') + def test_closed_session_not_reused(self, _): + session = self._create_session() + SessionCache.remember_session('somekey', session) + session._is_closed = True + + result = SessionCache.lookup_session('somekey') + + self.assertIsNone(result) + + @patch('pixelated.config.sessions.register') + def test_session_does_not_set_status_fresh_for_unkown_emails(self, _): + session = self._create_session() + self.provider.address_for.return_value = 'someone@somedomain.tld' + + session._set_fresh_account(None, 'another_email@somedomain.tld') + + self.assertFalse(session.fresh_account) + + def _create_session(self): + return LeapSession(self.provider, self.auth, self.mail_store, self.soledad_session, self.keymanager, self.smtp_mock) + + +def _execute_func(func): + func() diff --git a/service/test/unit/maintenance/test_commands.py b/service/test/unit/maintenance/test_commands.py index a0fc58d6..812c1bc2 100644 --- a/service/test/unit/maintenance/test_commands.py +++ b/service/test/unit/maintenance/test_commands.py @@ -17,7 +17,7 @@ import unittest import email from pixelated.maintenance import delete_all_mails, load_mails -from pixelated.bitmask_libraries.session import LeapSession +from pixelated.config.sessions import LeapSession from pixelated.adapter.mailstore import MailStore from leap.soledad.client import Soledad from leap.soledad.common.document import SoledadDocument diff --git a/service/test/unit/resources/test_login_resource.py b/service/test/unit/resources/test_login_resource.py index 2fba72fd..d006e934 100644 --- a/service/test/unit/resources/test_login_resource.py +++ b/service/test/unit/resources/test_login_resource.py @@ -9,7 +9,7 @@ from twisted.trial import unittest from twisted.web.resource import IResource from twisted.web.test.requesthelper import DummyRequest -from pixelated.bitmask_libraries.session import LeapSession, LeapSessionFactory +from pixelated.config.sessions import LeapSessionFactory, LeapSession from pixelated.resources.login_resource import LoginResource from test.unit.resources import DummySite @@ -220,7 +220,7 @@ class TestLoginPOST(unittest.TestCase): d.addCallback(assert_login_setup_service_for_user) return d - @patch('pixelated.bitmask_libraries.session.LeapSessionFactory.create') + @patch('pixelated.config.sessions.LeapSessionFactory.create') @patch('leap.auth.SRPAuth.authenticate') @patch('pixelated.config.services.Services.setup') def test_leap_session_is_not_created_when_leap_auth_fails(self, mock_service_setup, mock_leap_srp_auth, mock_leap_session_create): diff --git a/service/test/unit/resources/test_users_resource.py b/service/test/unit/resources/test_users_resource.py index bfd61022..9862209d 100644 --- a/service/test/unit/resources/test_users_resource.py +++ b/service/test/unit/resources/test_users_resource.py @@ -9,7 +9,7 @@ from twisted.trial import unittest from twisted.web.resource import IResource from twisted.web.test.requesthelper import DummyRequest -from pixelated.bitmask_libraries.session import LeapSession, LeapSessionFactory +from pixelated.config.sessions import LeapSessionFactory, LeapSession from pixelated.config.services import Services, ServicesFactory from pixelated.resources.login_resource import LoginResource from pixelated.resources.users import UsersResource -- cgit v1.2.3