diff options
author | Brad Warren <bmw@users.noreply.github.com> | 2017-01-31 03:55:54 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-31 03:55:54 +0300 |
commit | be5bcfe463d9649c350181fe1a84e9d513db1ff3 (patch) | |
tree | 7603ad81d46ccb0576160eca09b23520f3b5a0c9 /acme | |
parent | 240438eec73ad20c50db3bc242a0a229cabf8f66 (diff) |
Remove optional dependencies (#4088)
* Stop using already_listening in standalone
* remove already_listening
* remove psutil entirely
* fix #595
* Add basic perform test
* make pep8 happy
* Add test_perform_eacces
* add _setup_perform_error
* Add test_perform_unexpected_socket_error
* add test_perform_eaddrinuse_no_retry
* add test_perform_eaddrinuse_retry
* cleanup tests
* stop using dnspython
* don't install dns extras in tox
* remove dns extras from setup.py
* Add simple_verify back to DNS response
* remove dnspython from oldest tests
Diffstat (limited to 'acme')
-rw-r--r-- | acme/acme/challenges.py | 31 | ||||
-rw-r--r-- | acme/acme/challenges_test.py | 49 | ||||
-rw-r--r-- | acme/acme/dns_resolver.py | 45 | ||||
-rw-r--r-- | acme/acme/dns_resolver_test.py | 77 | ||||
-rw-r--r-- | acme/acme/test_util.py | 16 | ||||
-rw-r--r-- | acme/acme/util.py | 18 | ||||
-rw-r--r-- | acme/acme/util_test.py | 18 | ||||
-rw-r--r-- | acme/setup.py | 6 |
8 files changed, 18 insertions, 242 deletions
diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 3bfc1e62f..83b9b9edd 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -9,7 +9,6 @@ from cryptography.hazmat.primitives import hashes import OpenSSL import requests -from acme import dns_resolver from acme import errors from acme import crypto_util from acme import fields @@ -214,36 +213,24 @@ class DNS01Response(KeyAuthorizationChallengeResponse): def simple_verify(self, chall, domain, account_public_key): """Simple verify. + This method no longer checks DNS records and is a simple wrapper + around `KeyAuthorizationChallengeResponse.verify`. + :param challenges.DNS01 chall: Corresponding challenge. :param unicode domain: Domain name being verified. :param JWK account_public_key: Public key for the key pair being authorized. - :returns: ``True`` iff validation with the TXT records resolved from a - DNS server is successful. + :return: ``True`` iff verification of the key authorization was + successful. :rtype: bool """ - if not self.verify(chall, account_public_key): + # pylint: disable=unused-argument + verified = self.verify(chall, account_public_key) + if not verified: logger.debug("Verification of key authorization in response failed") - return False - - validation_domain_name = chall.validation_domain_name(domain) - validation = chall.validation(account_public_key) - logger.debug("Verifying %s at %s...", chall.typ, validation_domain_name) - - try: - txt_records = dns_resolver.txt_records_for_name( - validation_domain_name) - except errors.DependencyError: - raise errors.DependencyError("Local validation for 'dns-01' " - "challenges requires 'dnspython'") - exists = validation in txt_records - if not exists: - logger.debug("Key authorization from response (%r) doesn't match " - "any DNS response in %r", self.key_authorization, - txt_records) - return exists + return verified @Challenge.register # pylint: disable=too-many-ancestors diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index 5ac07abdd..49e790102 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -10,7 +10,6 @@ from six.moves.urllib import parse as urllib_parse # pylint: disable=import-err from acme import errors from acme import jose from acme import test_util -from acme.dns_resolver import DNS_REQUIREMENT CERT = test_util.load_comparable_cert('cert.pem') KEY = jose.JWKRSA(key=test_util.load_rsa_private_key('rsa512_key.pem')) @@ -92,7 +91,6 @@ class DNS01ResponseTest(unittest.TestCase): from acme.challenges import DNS01 self.chall = DNS01(token=(b'x' * 16)) self.response = self.chall.response(KEY) - self.records_for_name_path = "acme.dns_resolver.txt_records_for_name" def test_to_partial_json(self): self.assertEqual(self.jmsg, self.msg.to_partial_json()) @@ -105,45 +103,16 @@ class DNS01ResponseTest(unittest.TestCase): from acme.challenges import DNS01Response hash(DNS01Response.from_json(self.jmsg)) - def test_simple_verify_bad_key_authorization(self): + def test_simple_verify_failure(self): key2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem')) - self.response.simple_verify(self.chall, "local", key2.public_key()) - - @mock.patch('acme.dns_resolver.DNS_AVAILABLE', False) - def test_simple_verify_without_dns(self): - self.assertRaises( - errors.DependencyError, self.response.simple_verify, - self.chall, 'local', KEY.public_key()) - - @test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), - "optional dependency dnspython is not available") - def test_simple_verify_good_validation(self): # pragma: no cover - with mock.patch(self.records_for_name_path) as mock_resolver: - mock_resolver.return_value = [ - self.chall.validation(KEY.public_key())] - self.assertTrue(self.response.simple_verify( - self.chall, "local", KEY.public_key())) - mock_resolver.assert_called_once_with( - self.chall.validation_domain_name("local")) - - @test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), - "optional dependency dnspython is not available") - def test_simple_verify_good_validation_multitxts(self): # pragma: no cover - with mock.patch(self.records_for_name_path) as mock_resolver: - mock_resolver.return_value = [ - "!", self.chall.validation(KEY.public_key())] - self.assertTrue(self.response.simple_verify( - self.chall, "local", KEY.public_key())) - mock_resolver.assert_called_once_with( - self.chall.validation_domain_name("local")) - - @test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), - "optional dependency dnspython is not available") - def test_simple_verify_bad_validation(self): # pragma: no cover - with mock.patch(self.records_for_name_path) as mock_resolver: - mock_resolver.return_value = ["!"] - self.assertFalse(self.response.simple_verify( - self.chall, "local", KEY.public_key())) + public_key = key2.public_key() + verified = self.response.simple_verify(self.chall, "local", public_key) + self.assertFalse(verified) + + def test_simple_verify_success(self): + public_key = KEY.public_key() + verified = self.response.simple_verify(self.chall, "local", public_key) + self.assertTrue(verified) class DNS01Test(unittest.TestCase): diff --git a/acme/acme/dns_resolver.py b/acme/acme/dns_resolver.py deleted file mode 100644 index 2677d92ad..000000000 --- a/acme/acme/dns_resolver.py +++ /dev/null @@ -1,45 +0,0 @@ -"""DNS Resolver for ACME client. -Required only for local validation of 'dns-01' challenges. -""" -import logging - -from acme import errors -from acme import util - -DNS_REQUIREMENT = 'dnspython>=1.12' - -try: - util.activate(DNS_REQUIREMENT) - # pragma: no cover - import dns.exception - import dns.resolver - DNS_AVAILABLE = True -except errors.DependencyError: # pragma: no cover - DNS_AVAILABLE = False - - -logger = logging.getLogger(__name__) - - -def txt_records_for_name(name): - """Resolve the name and return the TXT records. - - :param unicode name: Domain name being verified. - - :returns: A list of txt records, if empty the name could not be resolved - :rtype: list of unicode - - """ - if not DNS_AVAILABLE: - raise errors.DependencyError( - '{0} is required to use this function'.format(DNS_REQUIREMENT)) - try: - dns_response = dns.resolver.query(name, 'TXT') - except dns.resolver.NXDOMAIN as error: - return [] - except dns.exception.DNSException as error: - logger.error("Error resolving %s: %s", name, str(error)) - return [] - - return [txt_rec.decode("utf-8") for rdata in dns_response - for txt_rec in rdata.strings] diff --git a/acme/acme/dns_resolver_test.py b/acme/acme/dns_resolver_test.py deleted file mode 100644 index 2e2edd0e7..000000000 --- a/acme/acme/dns_resolver_test.py +++ /dev/null @@ -1,77 +0,0 @@ -"""Tests for acme.dns_resolver.""" -import unittest - -import mock -from six.moves import reload_module # pylint: disable=import-error - -from acme import errors -from acme import test_util -from acme.dns_resolver import DNS_REQUIREMENT - - -if test_util.requirement_available(DNS_REQUIREMENT): - import dns - - -def create_txt_response(name, txt_records): - """ - Returns an RRSet containing the 'txt_records' as the result of a DNS - query for 'name'. - - This takes advantage of the fact that an Answer object mostly behaves - like an RRset. - """ - return dns.rrset.from_text_list(name, 60, "IN", "TXT", txt_records) - - -class TxtRecordsForNameTest(unittest.TestCase): - """Tests for acme.dns_resolver.txt_records_for_name.""" - @classmethod - def _call(cls, *args, **kwargs): - from acme.dns_resolver import txt_records_for_name - return txt_records_for_name(*args, **kwargs) - - -@test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), - "optional dependency dnspython is not available") -class TxtRecordsForNameWithDnsTest(TxtRecordsForNameTest): - """Tests for acme.dns_resolver.txt_records_for_name with dns.""" - @mock.patch("acme.dns_resolver.dns.resolver.query") - def test_txt_records_for_name_with_single_response(self, mock_dns): - mock_dns.return_value = create_txt_response('name', ['response']) - self.assertEqual(['response'], self._call('name')) - - @mock.patch("acme.dns_resolver.dns.resolver.query") - def test_txt_records_for_name_with_multiple_responses(self, mock_dns): - mock_dns.return_value = create_txt_response( - 'name', ['response1', 'response2']) - self.assertEqual(['response1', 'response2'], self._call('name')) - - @mock.patch("acme.dns_resolver.dns.resolver.query") - def test_txt_records_for_name_domain_not_found(self, mock_dns): - mock_dns.side_effect = dns.resolver.NXDOMAIN - self.assertEquals([], self._call('name')) - - @mock.patch("acme.dns_resolver.dns.resolver.query") - def test_txt_records_for_name_domain_other_error(self, mock_dns): - mock_dns.side_effect = dns.exception.DNSException - self.assertEquals([], self._call('name')) - - -class TxtRecordsForNameWithoutDnsTest(TxtRecordsForNameTest): - """Tests for acme.dns_resolver.txt_records_for_name without dns.""" - def setUp(self): - from acme import dns_resolver - dns_resolver.DNS_AVAILABLE = False - - def tearDown(self): - from acme import dns_resolver - reload_module(dns_resolver) - - def test_exception_raised(self): - self.assertRaises( - errors.DependencyError, self._call, "example.org") - - -if __name__ == '__main__': - unittest.main() # pragma: no cover diff --git a/acme/acme/test_util.py b/acme/acme/test_util.py index ba968511f..0f5763682 100644 --- a/acme/acme/test_util.py +++ b/acme/acme/test_util.py @@ -11,9 +11,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization import OpenSSL -from acme import errors from acme import jose -from acme import util def vector_path(*names): @@ -78,20 +76,6 @@ def load_pyopenssl_private_key(*names): return OpenSSL.crypto.load_privatekey(loader, load_vector(*names)) -def requirement_available(requirement): - """Checks if requirement can be imported. - - :rtype: bool - :returns: ``True`` iff requirement can be imported - - """ - try: - util.activate(requirement) - except errors.DependencyError: # pragma: no cover - return False - return True # pragma: no cover - - def skip_unless(condition, reason): # pragma: no cover """Skip tests unless a condition holds. diff --git a/acme/acme/util.py b/acme/acme/util.py index ac445b271..1fff89a9e 100644 --- a/acme/acme/util.py +++ b/acme/acme/util.py @@ -1,25 +1,7 @@ """ACME utilities.""" -import pkg_resources import six -from acme import errors - def map_keys(dikt, func): """Map dictionary keys.""" return dict((func(key), value) for key, value in six.iteritems(dikt)) - - -def activate(requirement): - """Make requirement importable. - - :param str requirement: the distribution and version to activate - - :raises acme.errors.DependencyError: if cannot activate requirement - - """ - try: - for distro in pkg_resources.require(requirement): # pylint: disable=not-callable - distro.activate() - except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict): - raise errors.DependencyError('{0} is unavailable'.format(requirement)) diff --git a/acme/acme/util_test.py b/acme/acme/util_test.py index ba6465409..00aa8b02d 100644 --- a/acme/acme/util_test.py +++ b/acme/acme/util_test.py @@ -1,8 +1,6 @@ """Tests for acme.util.""" import unittest -from acme import errors - class MapKeysTest(unittest.TestCase): """Tests for acme.util.map_keys.""" @@ -14,21 +12,5 @@ class MapKeysTest(unittest.TestCase): self.assertEqual({2: 2, 4: 4}, map_keys({1: 2, 3: 4}, lambda x: x + 1)) -class ActivateTest(unittest.TestCase): - """Tests for acme.util.activate.""" - - @classmethod - def _call(cls, *args, **kwargs): - from acme.util import activate - return activate(*args, **kwargs) - - def test_failure(self): - self.assertRaises(errors.DependencyError, self._call, 'acme>99.0.0') - - def test_success(self): - self._call('acme') - import acme as unused_acme - - if __name__ == '__main__': unittest.main() # pragma: no cover diff --git a/acme/setup.py b/acme/setup.py index 37b71368c..d05d814ad 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -33,11 +33,6 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') -# dnspython 1.12 is required to support both Python 2 and Python 3. -dns_extras = [ - 'dnspython>=1.12', -] - dev_extras = [ 'nose', 'tox', @@ -77,7 +72,6 @@ setup( include_package_data=True, install_requires=install_requires, extras_require={ - 'dns': dns_extras, 'dev': dev_extras, 'docs': docs_extras, }, |