diff options
-rw-r--r-- | src/leap/bitmask/keymanager/__init__.py | 99 | ||||
-rw-r--r-- | src/leap/bitmask/keymanager/keys.py | 3 | ||||
-rw-r--r-- | src/leap/bitmask/keymanager/nicknym.py | 12 | ||||
-rw-r--r-- | src/leap/bitmask/keymanager/openpgp.py | 3 | ||||
-rw-r--r-- | src/leap/bitmask/keymanager/refresher.py | 19 | ||||
-rw-r--r-- | src/leap/bitmask/mail/testing/__init__.py | 2 | ||||
-rw-r--r-- | tests/integration/keymanager/test_keymanager.py | 26 | ||||
-rw-r--r-- | tests/integration/keymanager/test_refresher.py | 35 | ||||
-rw-r--r-- | tests/integration/mail/smtp/test_gateway.py | 2 |
9 files changed, 72 insertions, 129 deletions
diff --git a/src/leap/bitmask/keymanager/__init__.py b/src/leap/bitmask/keymanager/__init__.py index 319be08d..ed3726ad 100644 --- a/src/leap/bitmask/keymanager/__init__.py +++ b/src/leap/bitmask/keymanager/__init__.py @@ -19,10 +19,8 @@ Key Manager is a Nicknym agent for LEAP client. """ import fileinput import json -import os import sys import tempfile -import urllib from urlparse import urlparse @@ -32,13 +30,13 @@ from twisted.web import client from twisted.web._responses import NOT_FOUND from leap.common import ca_bundle -from leap.common.check import leap_assert -from leap.common.decorators import memoized_method from leap.common.http import HTTPClient from leap.common.events import emit_async, catalog from leap.bitmask.keymanager import errors as keymanager_errors +from leap.bitmask.keymanager.errors import KeyNotFound from leap.bitmask.keymanager.nicknym import Nicknym +from leap.bitmask.keymanager.refresher import RandomRefreshPublicKey from leap.bitmask.keymanager.validation import ValidationLevels, can_upgrade from leap.bitmask.keymanager.openpgp import OpenPGPScheme @@ -101,7 +99,8 @@ class KeyManager(object): self._combined_ca_bundle = '' self._async_client = HTTPClient(self._combined_ca_bundle) - self._nicknym = Nicknym(self._nickserver_uri, self._ca_cert_path, self._token) + self._nicknym = Nicknym(self._nickserver_uri, + self._ca_cert_path, self._token) self.refresher = None # @@ -155,14 +154,17 @@ class KeyManager(object): except IOError as e: logger.warn("HTTP error retrieving key: %r" % (e,)) logger.warn("%s" % (content,)) - raise keymanager_errors.KeyNotFound(e.message), None, sys.exc_info()[2] + raise keymanager_errors.KeyNotFound(e.message), \ + None, sys.exc_info()[2] except ValueError as v: logger.warn("invalid JSON data from key: %s" % (uri,)) - raise keymanager_errors.KeyNotFound(v.message + ' - ' + uri), None, sys.exc_info()[2] + raise keymanager_errors.KeyNotFound(v.message + ' - ' + uri), \ + None, sys.exc_info()[2] except Exception as e: logger.warn("error retrieving key: %r" % (e,)) - raise keymanager_errors.KeyNotFound(e.message), None, sys.exc_info()[2] + raise keymanager_errors.KeyNotFound(e.message), \ + None, sys.exc_info()[2] # Responses are now text/plain, although it's json anyway, but # this will fail when it shouldn't # leap_assert( @@ -189,7 +191,7 @@ class KeyManager(object): raise KeyNotFound(message), None, sys.exc_info()[2] return response - d = self._async_client_pinned.request( + d = self._nicknym._async_client_pinned.request( str(uri), 'GET', callback=check_404) d.addCallback(client.readBody) return d @@ -219,76 +221,6 @@ class KeyManager(object): raise keymanager_errors.KeyNotFound(uri) defer.returnValue(content) - @defer.inlineCallbacks - def _put(self, uri, data=None): - """ - Send a PUT request to C{uri} containing C{data}. - - The request will be sent using the configured CA certificate path to - verify the server certificate and the configured session id for - authentication. - - :param uri: The URI of the request. - :type uri: str - :param data: The body of the request. - :type data: dict, str or file - - :return: A deferred that will be fired when PUT request finishes - :rtype: Deferred - """ - leap_assert( - self._token is not None, - 'We need a token to interact with webapp!') - if type(data) == dict: - data = urllib.urlencode(data) - headers = {'Authorization': [str('Token token=%s' % self._token)]} - headers['Content-Type'] = ['application/x-www-form-urlencoded'] - try: - res = yield self._async_client_pinned.request(str(uri), 'PUT', - body=str(data), - headers=headers) - except Exception as e: - logger.warn("Error uploading key: %r" % (e,)) - raise e - if 'error' in res: - # FIXME: That's a workaround for 500, - # we need to implement a readBody to assert response code - logger.warn("Error uploading key: %r" % (res,)) - raise Exception(res) - - @memoized_method(invalidation=300) - @defer.inlineCallbacks - def _fetch_keys_from_server(self, address): - """ - Fetch keys bound to address from nickserver and insert them in - local database. - - :param address: The address bound to the keys. - :type address: str - - :return: A Deferred which fires when the key is in the storage, - or which fails with KeyNotFound if the key was not found on - nickserver. - :rtype: Deferred - - """ - # request keys from the nickserver - server_keys = yield self._get_key_from_nicknym(address) - - # insert keys in local database - if self.OPENPGP_KEY in server_keys: - # nicknym server is authoritative for its own domain, - # for other domains the key might come from key servers. - validation_level = ValidationLevels.Weak_Chain - _, domain = _split_email(address) - if (domain == _get_domain(self._nickserver_uri)): - validation_level = ValidationLevels.Provider_Trust - - yield self.put_raw_key( - server_keys['openpgp'], - address=address, - validation=validation_level) - # # key management # @@ -307,7 +239,8 @@ class KeyManager(object): :raise UnsupportedKeyTypeError: if invalid key type """ def send(pubkey): - d = self._nicknym.put_key(self.uid, pubkey.key_data, self._api_uri, self._api_version) + d = self._nicknym.put_key(self.uid, pubkey.key_data, + self._api_uri, self._api_version) d.addCallback(lambda _: emit_async(catalog.KEYMANAGER_DONE_UPLOADING_KEYS, self._address)) @@ -327,9 +260,9 @@ class KeyManager(object): :type address: str :return: A Deferred which fires when the key is in the storage, - or which fails with KeyNotFound if the key was not found on - nickserver. - :rtype: Deferred + or which fails with KeyNotFound if the key was not + found on nickserver. + :rtype: Deferred """ server_keys = yield self._nicknym.fetch_key_with_address(address) diff --git a/src/leap/bitmask/keymanager/keys.py b/src/leap/bitmask/keymanager/keys.py index 1b236c3d..fd454480 100644 --- a/src/leap/bitmask/keymanager/keys.py +++ b/src/leap/bitmask/keymanager/keys.py @@ -292,7 +292,8 @@ class OpenPGPKey(object): :return: True if key expired. :rtype: bool """ - return False if self.expiry_date is None else self.expiry_date < datetime.now() + return False if self.expiry_date is None \ + else self.expiry_date < datetime.now() def parse_address(address): diff --git a/src/leap/bitmask/keymanager/nicknym.py b/src/leap/bitmask/keymanager/nicknym.py index 7af49d6d..5fe99925 100644 --- a/src/leap/bitmask/keymanager/nicknym.py +++ b/src/leap/bitmask/keymanager/nicknym.py @@ -139,12 +139,14 @@ class Nicknym(object): def check_404(response): if response.code == NOT_FOUND: - message = ' %s: Key not found. Request: %s' % (response.code, uri) + message = ' %s: Key not found. Request: %s' \ + % (response.code, uri) logger.warning(message) raise KeyNotFound(message), None, sys.exc_info()[2] return response - d = self._async_client_pinned.request(str(uri), 'GET', callback=check_404) + d = self._async_client_pinned.request(str(uri), 'GET', + callback=check_404) d.addCallback(client.readBody) return d @@ -162,7 +164,8 @@ class Nicknym(object): :rtype: Deferred """ - return self._get_key_from_nicknym(self._nickserver_uri + '?address=' + address) + return self._get_key_from_nicknym(self._nickserver_uri + + '?address=' + address) @memoized_method(invalidation=300) def fetch_key_with_fingerprint(self, fingerprint): @@ -178,4 +181,5 @@ class Nicknym(object): :rtype: Deferred """ - return self._get_key_from_nicknym(self._nickserver_uri + '?fingerprint=' + fingerprint) + return self._get_key_from_nicknym(self._nickserver_uri + + '?fingerprint=' + fingerprint) diff --git a/src/leap/bitmask/keymanager/openpgp.py b/src/leap/bitmask/keymanager/openpgp.py index b256ff00..b72c62ce 100644 --- a/src/leap/bitmask/keymanager/openpgp.py +++ b/src/leap/bitmask/keymanager/openpgp.py @@ -533,12 +533,13 @@ class OpenPGPScheme(object): d.addCallback(delete_key) return d + @defer.inlineCallbacks def unactivate_key(self, address): """ Mark a active doc as deleted. :param address: The unique address for the active content. """ - active_doc = self._get_active_doc_from_address(address, False) + active_doc = yield self._get_active_doc_from_address(address, False) yield self._soledad.delete_doc(active_doc) # diff --git a/src/leap/bitmask/keymanager/refresher.py b/src/leap/bitmask/keymanager/refresher.py index 54232c3b..694a537f 100644 --- a/src/leap/bitmask/keymanager/refresher.py +++ b/src/leap/bitmask/keymanager/refresher.py @@ -17,24 +17,25 @@ """ -A service which continuous refreshes the (public) key directories randomly in a random time interval. +A service which continuous refreshes the (public) key directories randomly in a +random time interval. """ -import logging - from twisted.internet.task import LoopingCall +from twisted.logger import Logger from twisted.internet import defer from random import choice, randrange DEBUG_STOP_REFRESH = "Stop to refresh the key directory ..." DEBUG_START_REFRESH = "Start to refresh the key directory ..." -ERROR_UNEQUAL_FINGERPRINTS = "[WARNING] Your provider might be cheat on you, " \ - "and gave a wrong key back. Fingerprints are unequal, old %s new %s " +ERROR_UNEQUAL_FINGERPRINTS = "[WARNING] Your provider might be cheat " \ + "on you, and gave a wrong key back. " \ + "Fingerprints are unequal, old %s new %s " MIN_RANDOM_INTERVAL_RANGE = 4 * 60 # four minutes MAX_RANDOM_INTERVAL_RANGE = 6 * 60 # six minutes -logger = logging.getLogger(__name__) +logger = Logger() class RandomRefreshPublicKey(object): @@ -74,7 +75,6 @@ class RandomRefreshPublicKey(object): :return: A random key. :rtype: A deferred. """ - # TODO maybe make a check first if key is active and get another one then. keys = yield self._openpgp.get_all_keys() defer.returnValue(None if keys is None or keys == [] else choice(keys)) @@ -117,8 +117,9 @@ class RandomRefreshPublicKey(object): yield self._maybe_unactivate_key(old_updated_key) yield self._openpgp.put_key(old_updated_key) - # No new fetch by address needed, bc that will happen before sending an email - # could be discussed since fetching before sending an email leaks information. + # No new fetch by address needed, bc that will happen before sending an + # email could be discussed since fetching before sending an email + # leaks information. def _get_random_interval_to_refresh(self): """ diff --git a/src/leap/bitmask/mail/testing/__init__.py b/src/leap/bitmask/mail/testing/__init__.py index 5ebfaf52..afb8e437 100644 --- a/src/leap/bitmask/mail/testing/__init__.py +++ b/src/leap/bitmask/mail/testing/__init__.py @@ -69,7 +69,7 @@ class KeyManagerWithSoledadTestCase(unittest.TestCase, BaseLeapTest): def deliverBody(self, x): return '' - self.km._async_client_pinned.request = Mock( + self.km._nicknym._async_client_pinned.request = Mock( return_value=defer.succeed(Response())) d1 = self.km.put_raw_key(PRIVATE_KEY, ADDRESS) diff --git a/tests/integration/keymanager/test_keymanager.py b/tests/integration/keymanager/test_keymanager.py index c623b94a..86e9115f 100644 --- a/tests/integration/keymanager/test_keymanager.py +++ b/tests/integration/keymanager/test_keymanager.py @@ -206,8 +206,8 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): token = "mytoken" km = self._key_manager(token=token) yield km._openpgp.put_raw_key(PUBLIC_KEY, ADDRESS) - km._async_client.request = mock.Mock( - return_value=defer.succeed('')) + km._nicknym._async_client_pinned.request = \ + mock.Mock(return_value=defer.succeed('')) # the following data will be used on the send km.ca_cert_path = 'capath' km.session_id = 'sessionid' @@ -280,9 +280,9 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): check_404_callback = used_kwargs['callback'] fake_response = mock.Mock() fake_response.code = NOT_FOUND - with self.assertRaisesRegexp( - errors.KeyNotFound, - '404: Key not found. Request: %s' % url.replace('?', '\?')): + with self.assertRaisesRegexp(errors.KeyNotFound, + '404: Key not found. Request: ' + '%s' % url.replace('?', '\?')): check_404_callback(fake_response) d.addCallback(check_key_not_found_is_raised_if_404) @@ -323,7 +323,8 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): """ km = self._key_manager(url=NICKSERVER_URI) - key = yield self._fetch_key_with_address(km, ADDRESS_OTHER, PUBLIC_KEY_OTHER) + key = yield self._fetch_key_with_address(km, ADDRESS_OTHER, + PUBLIC_KEY_OTHER) self.assertIsInstance(key, OpenPGPKey) self.assertTrue(ADDRESS_OTHER in key.uids) self.assertEqual(key.validation, ValidationLevels.Weak_Chain) @@ -395,8 +396,8 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): """ km = self._key_manager() - km._nicknym._async_client_pinned.request = mock.Mock( - return_value=defer.succeed(PUBLIC_KEY)) + km._async_client.request = \ + mock.Mock(return_value=defer.succeed(PUBLIC_KEY)) yield km.fetch_key(ADDRESS, "http://site.domain/key") key = yield km.get_key(ADDRESS) @@ -410,7 +411,7 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): """ km = self._key_manager() - km._nicknym._async_client_pinned.request = mock.Mock( + km._async_client.request = mock.Mock( return_value=defer.succeed(self.get_public_binary_key())) yield km.fetch_key(ADDRESS, "http://site.domain/key") @@ -423,7 +424,8 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): """ km = self._key_manager() - km._nicknym._async_client_pinned.request = mock.Mock(return_value=defer.succeed("")) + km._nicknym._async_client_pinned.request = \ + mock.Mock(return_value=defer.succeed("")) d = km.fetch_key(ADDRESS, "http://site.domain/key") return self.assertFailure(d, errors.KeyNotFound) @@ -434,8 +436,8 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): """ km = self._key_manager() - km._nicknym._async_client_pinned.request = mock.Mock( - return_value=defer.succeed(PUBLIC_KEY)) + km._async_client.request = \ + mock.Mock(return_value=defer.succeed(PUBLIC_KEY)) d = km.fetch_key(ADDRESS_2, "http://site.domain/key") return self.assertFailure(d, errors.KeyAddressMismatch) diff --git a/tests/integration/keymanager/test_refresher.py b/tests/integration/keymanager/test_refresher.py index 47dd0578..b5b36b00 100644 --- a/tests/integration/keymanager/test_refresher.py +++ b/tests/integration/keymanager/test_refresher.py @@ -18,7 +18,6 @@ """ Tests for refreshing the key directory. """ - from datetime import datetime from mock import Mock, patch @@ -28,8 +27,9 @@ from twisted.logger import Logger from leap.bitmask.keymanager import openpgp from leap.bitmask.keymanager.keys import OpenPGPKey -from leap.bitmask.keymanager.refresher import RandomRefreshPublicKey, MIN_RANDOM_INTERVAL_RANGE, DEBUG_START_REFRESH, \ - DEBUG_STOP_REFRESH, ERROR_UNEQUAL_FINGERPRINTS +from leap.bitmask.keymanager.refresher import RandomRefreshPublicKey, \ + MIN_RANDOM_INTERVAL_RANGE, DEBUG_START_REFRESH, DEBUG_STOP_REFRESH,\ + ERROR_UNEQUAL_FINGERPRINTS from leap.bitmask.keymanager.testing import KeyManagerWithSoledadTestCase from common import KEY_FINGERPRINT @@ -37,9 +37,6 @@ from common import KEY_FINGERPRINT ANOTHER_FP = 'ANOTHERFINGERPRINT' -logger = Logger() - - class RandomRefreshPublicKeyTestCase(KeyManagerWithSoledadTestCase): @defer.inlineCallbacks @@ -53,7 +50,8 @@ class RandomRefreshPublicKeyTestCase(KeyManagerWithSoledadTestCase): pgp.get_all_keys = Mock(return_value=defer.succeed([key, key_another])) random_key = yield rf._get_random_key() - self.assertTrue(random_key.address == key.address or random_key.address == key_another.address) + self.assertTrue(random_key.address == key.address or + random_key.address == key_another.address) @defer.inlineCallbacks def test_do_not_throw_error_for_empty_key_dict(self): @@ -66,8 +64,7 @@ class RandomRefreshPublicKeyTestCase(KeyManagerWithSoledadTestCase): self.assertTrue(random_address is None) @defer.inlineCallbacks - def _test_log_error_if_fetch_by_fingerprint_returns_wrong_key(self): - # FIXME !!! --------------------------------------------------- + def test_log_error_if_fetch_by_fingerprint_returns_wrong_key(self): pgp = openpgp.OpenPGPScheme( self._soledad, gpgbinary=self.gpg_binary_path) km = self._key_manager() @@ -75,10 +72,12 @@ class RandomRefreshPublicKeyTestCase(KeyManagerWithSoledadTestCase): with patch.object(Logger, 'error') as mock_logger_error: rf = RandomRefreshPublicKey(pgp, km) rf._get_random_key = \ - Mock(return_value=defer.succeed(OpenPGPKey(fingerprint=KEY_FINGERPRINT))) + Mock(return_value=defer.succeed(OpenPGPKey( + fingerprint=KEY_FINGERPRINT))) km._nicknym.fetch_key_with_fingerprint = \ - Mock(return_value=defer.succeed(OpenPGPKey(fingerprint=ANOTHER_FP))) + Mock(return_value=defer.succeed(OpenPGPKey( + fingerprint=ANOTHER_FP))) yield rf.maybe_refresh_key() @@ -92,10 +91,11 @@ class RandomRefreshPublicKeyTestCase(KeyManagerWithSoledadTestCase): km = self._key_manager() rf = RandomRefreshPublicKey(pgp, km) - rf._get_random_key = Mock(return_value=defer.succeed(OpenPGPKey(fingerprint=KEY_FINGERPRINT))) + rf._get_random_key = Mock(return_value=defer.succeed( + OpenPGPKey(fingerprint=KEY_FINGERPRINT))) - km._nicknym.fetch_key_with_fingerprint = \ - Mock(return_value=defer.succeed(OpenPGPKey(fingerprint=ANOTHER_FP))) + km._nicknym.fetch_key_with_fingerprint = Mock( + return_value=defer.succeed(OpenPGPKey(fingerprint=ANOTHER_FP))) yield rf.maybe_refresh_key() @@ -107,13 +107,14 @@ class RandomRefreshPublicKeyTestCase(KeyManagerWithSoledadTestCase): rf = RandomRefreshPublicKey(pgp, km) key = OpenPGPKey(address='zara@leap.se', expiry_date=datetime.now()) self.assertTrue(key.address is 'zara@leap.se') - km._openpgp.unactivate_key = Mock(return_value=defer.succeed(None)) + pgp.unactivate_key = Mock(return_value=defer.succeed(None)) + yield rf._maybe_unactivate_key(key) + self.assertTrue(key.address is None) self.assertFalse(key.is_active()) - def _test_start_refreshing(self): - # FIXME !!! --------------------------------------------------- + def test_start_refreshing(self): pgp = openpgp.OpenPGPScheme( self._soledad, gpgbinary=self.gpg_binary_path) diff --git a/tests/integration/mail/smtp/test_gateway.py b/tests/integration/mail/smtp/test_gateway.py index e286be97..38abfe7f 100644 --- a/tests/integration/mail/smtp/test_gateway.py +++ b/tests/integration/mail/smtp/test_gateway.py @@ -113,7 +113,7 @@ class TestSmtpGateway(KeyManagerWithSoledadTestCase): self._soledad, gpgbinary=self.gpg_binary_path) yield pgp.delete_key(pubkey) # mock the key fetching - self.km._fetch_keys_from_server = Mock( + self.km._nicknym.fetch_key_with_address = Mock( return_value=fail(errors.KeyNotFound())) user = TEST_USER proto = getSMTPFactory( |