diff --git a/MANIFEST.in b/MANIFEST.in index 3bd657b87..b628121e1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,14 @@ include CHANGES.rst include CONTRIBUTING.md include linter_plugin.py include letsencrypt/EULA -recursive-include letsencrypt *.json -recursive-include letsencrypt *.conf + recursive-include letsencrypt/client/tests/testdata * + +recursive-include acme/schemata *.json +recursive-include acme/jose/testdata * + +recursive-include letsencrypt_apache/tests/testdata * +include letsencrypt_apache/options-ssl.conf + +recursive-include letsencrypt_nginx/tests/testdata * +include letsencrypt_nginx/options-ssl.conf diff --git a/README.rst b/README.rst index 4f170f11b..3ec317c55 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ All you need to do is:: user@www:~$ sudo letsencrypt -d www.example.org auth -and if you have a compatbile web server (Apache), Let's Encrypt can +and if you have a compatbile web server (Apache or Nginx), Let's Encrypt can not only get a new certificate, but also deploy it and configure your server automatically!:: @@ -60,7 +60,8 @@ Current Features * web servers supported: - - apache2.x (tested and working on Ubuntu Linux) + - apache/2.x (tested and working on Ubuntu Linux) + - nginx/0.8.48+ (tested and mostly working on Ubuntu Linux) - standalone (runs its own webserver to prove you control the domain) * the private key is generated locally on your system @@ -70,7 +71,7 @@ Current Features * can revoke certificates * adjustable RSA key bitlength (2048 (default), 4096, ...) * optionally can install a http->https redirect, so your site effectively - runs https only + runs https only (Apache only) * fully automated * configuration changes are logged and can be reverted using the CLI * text and ncurses UI diff --git a/letsencrypt/acme/__init__.py b/acme/__init__.py similarity index 100% rename from letsencrypt/acme/__init__.py rename to acme/__init__.py diff --git a/letsencrypt/acme/challenges.py b/acme/challenges.py similarity index 96% rename from letsencrypt/acme/challenges.py rename to acme/challenges.py index 9c0f263c7..11a1c9a60 100644 --- a/letsencrypt/acme/challenges.py +++ b/acme/challenges.py @@ -5,8 +5,8 @@ import hashlib import Crypto.Random -from letsencrypt.acme import jose -from letsencrypt.acme import other +from acme import jose +from acme import other # pylint: disable=too-few-public-methods @@ -186,8 +186,8 @@ class ProofOfPossession(ContinuityChallenge): class Hints(jose.JSONObjectWithFields): """Hints for "proofOfPossession" challenge. - :ivar jwk: JSON Web Key (:class:`letsencrypt.acme.jose.JWK`) - :ivar list certs: List of :class:`letsencrypt.acme.jose.ComparableX509` + :ivar jwk: JSON Web Key (:class:`acme.jose.JWK`) + :ivar list certs: List of :class:`acme.jose.ComparableX509` certificates. """ @@ -221,7 +221,7 @@ class ProofOfPossessionResponse(ChallengeResponse): """ACME "proofOfPossession" challenge response. :ivar str nonce: Random data, **not** base64-encoded. - :ivar signature: :class:`~letsencrypt.acme.other.Signature` of this message. + :ivar signature: :class:`~acme.other.Signature` of this message. """ typ = "proofOfPossession" diff --git a/letsencrypt/acme/challenges_test.py b/acme/challenges_test.py similarity index 80% rename from letsencrypt/acme/challenges_test.py rename to acme/challenges_test.py index 9ca9f6dd8..0669dd581 100644 --- a/letsencrypt/acme/challenges_test.py +++ b/acme/challenges_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.challenges.""" +"""Tests for acme.challenges.""" import os import pkg_resources import unittest @@ -6,23 +6,22 @@ import unittest import Crypto.PublicKey.RSA import M2Crypto -from letsencrypt.acme import jose -from letsencrypt.acme import other +from acme import jose +from acme import other CERT = jose.ComparableX509(M2Crypto.X509.load_cert( pkg_resources.resource_filename( - 'letsencrypt.client.tests', os.path.join('testdata', 'cert.pem')))) + 'letsencrypt.tests', os.path.join('testdata', 'cert.pem')))) KEY = jose.HashableRSAKey(Crypto.PublicKey.RSA.importKey( pkg_resources.resource_string( - 'letsencrypt.acme.jose', - os.path.join('testdata', 'rsa512_key.pem')))) + 'acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) class SimpleHTTPSTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import SimpleHTTPS + from acme.challenges import SimpleHTTPS self.msg = SimpleHTTPS( token='evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA') self.jmsg = { @@ -34,18 +33,18 @@ class SimpleHTTPSTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import SimpleHTTPS + from acme.challenges import SimpleHTTPS self.assertEqual(self.msg, SimpleHTTPS.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import SimpleHTTPS + from acme.challenges import SimpleHTTPS hash(SimpleHTTPS.from_json(self.jmsg)) class SimpleHTTPSResponseTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import SimpleHTTPSResponse + from acme.challenges import SimpleHTTPSResponse self.msg = SimpleHTTPSResponse(path='6tbIMBC5Anhl5bOlWT5ZFA') self.jmsg = { 'type': 'simpleHttps', @@ -60,19 +59,19 @@ class SimpleHTTPSResponseTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import SimpleHTTPSResponse + from acme.challenges import SimpleHTTPSResponse self.assertEqual( self.msg, SimpleHTTPSResponse.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import SimpleHTTPSResponse + from acme.challenges import SimpleHTTPSResponse hash(SimpleHTTPSResponse.from_json(self.jmsg)) class DVSNITest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import DVSNI + from acme.challenges import DVSNI self.msg = DVSNI( r="O*\xb4-\xad\xec\x95>\xed\xa9\r0\x94\xe8\x97\x9c&6" "\xbf'\xb3\xed\x9a9nX\x0f'\\m\xe7\x12", @@ -91,21 +90,21 @@ class DVSNITest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import DVSNI + from acme.challenges import DVSNI self.assertEqual(self.msg, DVSNI.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import DVSNI + from acme.challenges import DVSNI hash(DVSNI.from_json(self.jmsg)) def test_from_json_invalid_r_length(self): - from letsencrypt.acme.challenges import DVSNI + from acme.challenges import DVSNI self.jmsg['r'] = 'abcd' self.assertRaises( jose.DeserializationError, DVSNI.from_json, self.jmsg) def test_from_json_invalid_nonce_length(self): - from letsencrypt.acme.challenges import DVSNI + from acme.challenges import DVSNI self.jmsg['nonce'] = 'abcd' self.assertRaises( jose.DeserializationError, DVSNI.from_json, self.jmsg) @@ -114,7 +113,7 @@ class DVSNITest(unittest.TestCase): class DVSNIResponseTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import DVSNIResponse + from acme.challenges import DVSNIResponse self.msg = DVSNIResponse( s='\xf5\xd6\xe3\xb2]\xe0L\x0bN\x9cKJ\x14I\xa1K\xa3#\xf9\xa8' '\xcd\x8c7\x0e\x99\x19)\xdc\xb7\xf3\x9bw') @@ -124,7 +123,7 @@ class DVSNIResponseTest(unittest.TestCase): } def test_z_and_domain(self): - from letsencrypt.acme.challenges import DVSNI + from acme.challenges import DVSNI challenge = DVSNI( r="O*\xb4-\xad\xec\x95>\xed\xa9\r0\x94\xe8\x97\x9c&6" "\xbf'\xb3\xed\x9a9nX\x0f'\\m\xe7\x12", @@ -140,18 +139,18 @@ class DVSNIResponseTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import DVSNIResponse + from acme.challenges import DVSNIResponse self.assertEqual(self.msg, DVSNIResponse.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import DVSNIResponse + from acme.challenges import DVSNIResponse hash(DVSNIResponse.from_json(self.jmsg)) class RecoveryContactTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import RecoveryContact + from acme.challenges import RecoveryContact self.msg = RecoveryContact( activation_url='https://example.ca/sendrecovery/a5bd99383fb0', success_url='https://example.ca/confirmrecovery/bb1b9928932', @@ -167,11 +166,11 @@ class RecoveryContactTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import RecoveryContact + from acme.challenges import RecoveryContact self.assertEqual(self.msg, RecoveryContact.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import RecoveryContact + from acme.challenges import RecoveryContact hash(RecoveryContact.from_json(self.jmsg)) def test_json_without_optionals(self): @@ -179,7 +178,7 @@ class RecoveryContactTest(unittest.TestCase): del self.jmsg['successURL'] del self.jmsg['contact'] - from letsencrypt.acme.challenges import RecoveryContact + from acme.challenges import RecoveryContact msg = RecoveryContact.from_json(self.jmsg) self.assertTrue(msg.activation_url is None) @@ -191,7 +190,7 @@ class RecoveryContactTest(unittest.TestCase): class RecoveryContactResponseTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import RecoveryContactResponse + from acme.challenges import RecoveryContactResponse self.msg = RecoveryContactResponse(token='23029d88d9e123e') self.jmsg = {'type': 'recoveryContact', 'token': '23029d88d9e123e'} @@ -199,18 +198,18 @@ class RecoveryContactResponseTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import RecoveryContactResponse + from acme.challenges import RecoveryContactResponse self.assertEqual( self.msg, RecoveryContactResponse.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import RecoveryContactResponse + from acme.challenges import RecoveryContactResponse hash(RecoveryContactResponse.from_json(self.jmsg)) def test_json_without_optionals(self): del self.jmsg['token'] - from letsencrypt.acme.challenges import RecoveryContactResponse + from acme.challenges import RecoveryContactResponse msg = RecoveryContactResponse.from_json(self.jmsg) self.assertTrue(msg.token is None) @@ -220,7 +219,7 @@ class RecoveryContactResponseTest(unittest.TestCase): class RecoveryTokenTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import RecoveryToken + from acme.challenges import RecoveryToken self.msg = RecoveryToken() self.jmsg = {'type': 'recoveryToken'} @@ -228,18 +227,18 @@ class RecoveryTokenTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import RecoveryToken + from acme.challenges import RecoveryToken self.assertEqual(self.msg, RecoveryToken.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import RecoveryToken + from acme.challenges import RecoveryToken hash(RecoveryToken.from_json(self.jmsg)) class RecoveryTokenResponseTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import RecoveryTokenResponse + from acme.challenges import RecoveryTokenResponse self.msg = RecoveryTokenResponse(token='23029d88d9e123e') self.jmsg = {'type': 'recoveryToken', 'token': '23029d88d9e123e'} @@ -247,18 +246,18 @@ class RecoveryTokenResponseTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import RecoveryTokenResponse + from acme.challenges import RecoveryTokenResponse self.assertEqual( self.msg, RecoveryTokenResponse.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import RecoveryTokenResponse + from acme.challenges import RecoveryTokenResponse hash(RecoveryTokenResponse.from_json(self.jmsg)) def test_json_without_optionals(self): del self.jmsg['token'] - from letsencrypt.acme.challenges import RecoveryTokenResponse + from acme.challenges import RecoveryTokenResponse msg = RecoveryTokenResponse.from_json(self.jmsg) self.assertTrue(msg.token is None) @@ -282,7 +281,7 @@ class ProofOfPossessionHintsTest(unittest.TestCase): authorized_for = ('www.example.com', 'example.net') serial_numbers = (34234239832, 23993939911, 17) - from letsencrypt.acme.challenges import ProofOfPossession + from acme.challenges import ProofOfPossession self.msg = ProofOfPossession.Hints( jwk=jwk, issuers=issuers, cert_fingerprints=cert_fingerprints, certs=(CERT,), subject_key_identifiers=subject_key_identifiers, @@ -304,12 +303,12 @@ class ProofOfPossessionHintsTest(unittest.TestCase): self.assertEqual(self.jmsg_to, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import ProofOfPossession + from acme.challenges import ProofOfPossession self.assertEqual( self.msg, ProofOfPossession.Hints.from_json(self.jmsg_from)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import ProofOfPossession + from acme.challenges import ProofOfPossession hash(ProofOfPossession.Hints.from_json(self.jmsg_from)) def test_json_without_optionals(self): @@ -318,7 +317,7 @@ class ProofOfPossessionHintsTest(unittest.TestCase): del self.jmsg_from[optional] del self.jmsg_to[optional] - from letsencrypt.acme.challenges import ProofOfPossession + from acme.challenges import ProofOfPossession msg = ProofOfPossession.Hints.from_json(self.jmsg_from) self.assertEqual(msg.cert_fingerprints, ()) @@ -334,7 +333,7 @@ class ProofOfPossessionHintsTest(unittest.TestCase): class ProofOfPossessionTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import ProofOfPossession + from acme.challenges import ProofOfPossession hints = ProofOfPossession.Hints( jwk=jose.JWKRSA(key=KEY.publickey()), cert_fingerprints=(), certs=(), serial_numbers=(), subject_key_identifiers=(), @@ -360,12 +359,12 @@ class ProofOfPossessionTest(unittest.TestCase): self.assertEqual(self.jmsg_to, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import ProofOfPossession + from acme.challenges import ProofOfPossession self.assertEqual( self.msg, ProofOfPossession.from_json(self.jmsg_from)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import ProofOfPossession + from acme.challenges import ProofOfPossession hash(ProofOfPossession.from_json(self.jmsg_from)) @@ -384,7 +383,7 @@ class ProofOfPossessionResponseTest(unittest.TestCase): nonce='\x99\xc7Q\xb3f2\xbc\xdci\xfe\xd6\x98k\xc67\xdf', ) - from letsencrypt.acme.challenges import ProofOfPossessionResponse + from acme.challenges import ProofOfPossessionResponse self.msg = ProofOfPossessionResponse( nonce='xD\xf9\xb9\xdbU\xed\xaa\x17\xf1y|\x81\x88\x99 ', signature=signature) @@ -407,19 +406,19 @@ class ProofOfPossessionResponseTest(unittest.TestCase): self.assertEqual(self.jmsg_to, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import ProofOfPossessionResponse + from acme.challenges import ProofOfPossessionResponse self.assertEqual( self.msg, ProofOfPossessionResponse.from_json(self.jmsg_from)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import ProofOfPossessionResponse + from acme.challenges import ProofOfPossessionResponse hash(ProofOfPossessionResponse.from_json(self.jmsg_from)) class DNSTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import DNS + from acme.challenges import DNS self.msg = DNS(token='17817c66b60ce2e4012dfad92657527a') self.jmsg = {'type': 'dns', 'token': '17817c66b60ce2e4012dfad92657527a'} @@ -427,18 +426,18 @@ class DNSTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import DNS + from acme.challenges import DNS self.assertEqual(self.msg, DNS.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import DNS + from acme.challenges import DNS hash(DNS.from_json(self.jmsg)) class DNSResponseTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.challenges import DNSResponse + from acme.challenges import DNSResponse self.msg = DNSResponse() self.jmsg = {'type': 'dns'} @@ -446,13 +445,13 @@ class DNSResponseTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.challenges import DNSResponse + from acme.challenges import DNSResponse self.assertEqual(self.msg, DNSResponse.from_json(self.jmsg)) def test_from_json_hashable(self): - from letsencrypt.acme.challenges import DNSResponse + from acme.challenges import DNSResponse hash(DNSResponse.from_json(self.jmsg)) if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/errors.py b/acme/errors.py similarity index 77% rename from letsencrypt/acme/errors.py rename to acme/errors.py index d69efda11..957e781af 100644 --- a/letsencrypt/acme/errors.py +++ b/acme/errors.py @@ -1,5 +1,5 @@ """ACME errors.""" -from letsencrypt.acme.jose import errors as jose_errors +from acme.jose import errors as jose_errors class Error(Exception): """Generic ACME error.""" diff --git a/letsencrypt/acme/fields.py b/acme/fields.py similarity index 94% rename from letsencrypt/acme/fields.py rename to acme/fields.py index f001f1cd5..3af336fe5 100644 --- a/letsencrypt/acme/fields.py +++ b/acme/fields.py @@ -1,7 +1,7 @@ """ACME JSON fields.""" import pyrfc3339 -from letsencrypt.acme import jose +from acme import jose class RFC3339Field(jose.Field): diff --git a/letsencrypt/acme/fields_test.py b/acme/fields_test.py similarity index 69% rename from letsencrypt/acme/fields_test.py rename to acme/fields_test.py index 204849408..40f0ad59b 100644 --- a/letsencrypt/acme/fields_test.py +++ b/acme/fields_test.py @@ -1,35 +1,39 @@ -"""Tests for letsencrypt.acme.fields.""" +"""Tests for acme.fields.""" import datetime import unittest import pytz -from letsencrypt.acme import jose +from acme import jose class RFC3339FieldTest(unittest.TestCase): - """Tests for letsencrypt.acme.fields.RFC3339Field.""" + """Tests for acme.fields.RFC3339Field.""" def setUp(self): self.decoded = datetime.datetime(2015, 3, 27, tzinfo=pytz.utc) self.encoded = '2015-03-27T00:00:00Z' def test_default_encoder(self): - from letsencrypt.acme.fields import RFC3339Field + from acme.fields import RFC3339Field self.assertEqual( self.encoded, RFC3339Field.default_encoder(self.decoded)) def test_default_encoder_naive_fails(self): - from letsencrypt.acme.fields import RFC3339Field + from acme.fields import RFC3339Field self.assertRaises( ValueError, RFC3339Field.default_encoder, datetime.datetime.now()) def test_default_decoder(self): - from letsencrypt.acme.fields import RFC3339Field + from acme.fields import RFC3339Field self.assertEqual( self.decoded, RFC3339Field.default_decoder(self.encoded)) def test_default_decoder_raises_deserialization_error(self): - from letsencrypt.acme.fields import RFC3339Field + from acme.fields import RFC3339Field self.assertRaises( jose.DeserializationError, RFC3339Field.default_decoder, '') + + +if __name__ == '__main__': + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/__init__.py b/acme/jose/__init__.py similarity index 77% rename from letsencrypt/acme/jose/__init__.py rename to acme/jose/__init__.py index 20f9ba7d3..db3258a3d 100644 --- a/letsencrypt/acme/jose/__init__.py +++ b/acme/jose/__init__.py @@ -22,21 +22,21 @@ particular the following RFCs: https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-signature/ """ -from letsencrypt.acme.jose.b64 import ( +from acme.jose.b64 import ( b64decode, b64encode, ) -from letsencrypt.acme.jose.errors import ( +from acme.jose.errors import ( DeserializationError, SerializationError, Error, UnrecognizedTypeError, ) -from letsencrypt.acme.jose.interfaces import JSONDeSerializable +from acme.jose.interfaces import JSONDeSerializable -from letsencrypt.acme.jose.json_util import ( +from acme.jose.json_util import ( Field, JSONObjectWithFields, TypedJSONObjectWithFields, @@ -48,7 +48,7 @@ from letsencrypt.acme.jose.json_util import ( encode_csr, ) -from letsencrypt.acme.jose.jwa import ( +from acme.jose.jwa import ( HS256, HS384, HS512, @@ -61,14 +61,14 @@ from letsencrypt.acme.jose.jwa import ( RS512, ) -from letsencrypt.acme.jose.jwk import ( +from acme.jose.jwk import ( JWK, JWKRSA, ) -from letsencrypt.acme.jose.jws import JWS +from acme.jose.jws import JWS -from letsencrypt.acme.jose.util import ( +from acme.jose.util import ( ComparableX509, HashableRSAKey, ImmutableMap, diff --git a/letsencrypt/acme/jose/b64.py b/acme/jose/b64.py similarity index 100% rename from letsencrypt/acme/jose/b64.py rename to acme/jose/b64.py diff --git a/letsencrypt/acme/jose/b64_test.py b/acme/jose/b64_test.py similarity index 87% rename from letsencrypt/acme/jose/b64_test.py rename to acme/jose/b64_test.py index 89ff27f5d..0c243cb2a 100644 --- a/letsencrypt/acme/jose/b64_test.py +++ b/acme/jose/b64_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.jose.b64.""" +"""Tests for acme.jose.b64.""" import unittest @@ -19,11 +19,11 @@ B64_URL_UNSAFE_EXAMPLES = { class B64EncodeTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.b64.b64encode.""" + """Tests for acme.jose.b64.b64encode.""" @classmethod def _call(cls, data): - from letsencrypt.acme.jose.b64 import b64encode + from acme.jose.b64 import b64encode return b64encode(data) def test_unsafe_url(self): @@ -39,11 +39,11 @@ class B64EncodeTest(unittest.TestCase): class B64DecodeTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.b64.b64decode.""" + """Tests for acme.jose.b64.b64decode.""" @classmethod def _call(cls, data): - from letsencrypt.acme.jose.b64 import b64decode + from acme.jose.b64 import b64decode return b64decode(data) def test_unsafe_url(self): @@ -69,4 +69,4 @@ class B64DecodeTest(unittest.TestCase): if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/errors.py b/acme/jose/errors.py similarity index 100% rename from letsencrypt/acme/jose/errors.py rename to acme/jose/errors.py diff --git a/letsencrypt/acme/jose/errors_test.py b/acme/jose/errors_test.py similarity index 71% rename from letsencrypt/acme/jose/errors_test.py rename to acme/jose/errors_test.py index dd6af6c1a..919980920 100644 --- a/letsencrypt/acme/jose/errors_test.py +++ b/acme/jose/errors_test.py @@ -1,10 +1,10 @@ -"""Tests for letsencrypt.acme.jose.errors.""" +"""Tests for acme.jose.errors.""" import unittest class UnrecognizedTypeErrorTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.jose.errors import UnrecognizedTypeError + from acme.jose.errors import UnrecognizedTypeError self.error = UnrecognizedTypeError('foo', {'type': 'foo'}) def test_str(self): @@ -14,4 +14,4 @@ class UnrecognizedTypeErrorTest(unittest.TestCase): if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/interfaces.py b/acme/jose/interfaces.py similarity index 96% rename from letsencrypt/acme/jose/interfaces.py rename to acme/jose/interfaces.py index 8e06f99f9..27dcf863f 100644 --- a/letsencrypt/acme/jose/interfaces.py +++ b/acme/jose/interfaces.py @@ -3,7 +3,7 @@ import abc import collections import json -from letsencrypt.acme.jose import util +from acme.jose import util # pylint: disable=no-self-argument,no-method-argument,no-init,inherit-non-class # pylint: disable=too-few-public-methods @@ -110,7 +110,7 @@ class JSONDeSerializable(object): # in particular... assert Bar().to_partial_json() != ['foo', 'foo'] - :raises letsencrypt.acme.jose.errors.SerializationError: + :raises acme.jose.errors.SerializationError: in case of any serialization error. :returns: Partially serializable object. @@ -125,7 +125,7 @@ class JSONDeSerializable(object): assert Bar().to_json() == ['foo', 'foo'] - :raises letsencrypt.acme.jose.errors.SerializationError: + :raises acme.jose.errors.SerializationError: in case of any serialization error. :returns: Fully serialized object. @@ -157,7 +157,7 @@ class JSONDeSerializable(object): types, as decoded from JSON document. Not necessarily :class:`dict` (as decoded from "JSON object" document). - :raises letsencrypt.acme.jose.errors.DeserializationError: + :raises acme.jose.errors.DeserializationError: if decoding was unsuccessful, e.g. in case of unparseable X509 certificate, or wrong padding in JOSE base64 encoded string, etc. diff --git a/letsencrypt/acme/jose/interfaces_test.py b/acme/jose/interfaces_test.py similarity index 87% rename from letsencrypt/acme/jose/interfaces_test.py rename to acme/jose/interfaces_test.py index 4c0fc6eb9..380c3a2a5 100644 --- a/letsencrypt/acme/jose/interfaces_test.py +++ b/acme/jose/interfaces_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.jose.interfaces.""" +"""Tests for acme.jose.interfaces.""" import unittest @@ -6,7 +6,7 @@ class JSONDeSerializableTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes def setUp(self): - from letsencrypt.acme.jose.interfaces import JSONDeSerializable + from acme.jose.interfaces import JSONDeSerializable # pylint: disable=missing-docstring,invalid-name @@ -44,8 +44,7 @@ class JSONDeSerializableTest(unittest.TestCase): @classmethod def from_json(cls, jobj): - return cls(Basic.from_json(jobj.keys()[0]), - Basic.from_json(jobj.values()[0])) + pass # pragma: no cover self.basic1 = Basic('foo1') self.basic2 = Basic('foo2') @@ -76,7 +75,7 @@ class JSONDeSerializableTest(unittest.TestCase): self.assertEqual(self.tuple.to_json(), (('foo', ))) def test_from_json_not_implemented(self): - from letsencrypt.acme.jose.interfaces import JSONDeSerializable + from acme.jose.interfaces import JSONDeSerializable self.assertRaises(TypeError, JSONDeSerializable.from_json, 'xxx') def test_json_loads(self): @@ -95,7 +94,7 @@ class JSONDeSerializableTest(unittest.TestCase): self.seq.json_dumps_pretty(), '[\n "foo1",\n "foo2"\n]') def test_json_dump_default(self): - from letsencrypt.acme.jose.interfaces import JSONDeSerializable + from acme.jose.interfaces import JSONDeSerializable self.assertEqual( 'foo1', JSONDeSerializable.json_dump_default(self.basic1)) @@ -106,10 +105,10 @@ class JSONDeSerializableTest(unittest.TestCase): self.assertTrue(jobj[1] is self.basic2) def test_json_dump_default_type_error(self): - from letsencrypt.acme.jose.interfaces import JSONDeSerializable + from acme.jose.interfaces import JSONDeSerializable self.assertRaises( TypeError, JSONDeSerializable.json_dump_default, object()) if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/json_util.py b/acme/jose/json_util.py similarity index 95% rename from letsencrypt/acme/jose/json_util.py rename to acme/jose/json_util.py index ac8cdf7aa..0c91c3412 100644 --- a/letsencrypt/acme/jose/json_util.py +++ b/acme/jose/json_util.py @@ -12,10 +12,10 @@ import logging import M2Crypto -from letsencrypt.acme.jose import b64 -from letsencrypt.acme.jose import errors -from letsencrypt.acme.jose import interfaces -from letsencrypt.acme.jose import util +from acme.jose import b64 +from acme.jose import errors +from acme.jose import interfaces +from acme.jose import util class Field(object): @@ -27,8 +27,8 @@ class Field(object): ``encoder`` (``decoder``) is a callable that accepts a single parameter, i.e. a value to be encoded (decoded), and returns the serialized (deserialized) value. In case of errors it should raise - :class:`~letsencrypt.acme.jose.errors.SerializationError` - (:class:`~letsencrypt.acme.jose.errors.DeserializationError`). + :class:`~acme.jose.errors.SerializationError` + (:class:`~acme.jose.errors.DeserializationError`). Note, that ``decoder`` should perform partial serialization only. @@ -96,7 +96,7 @@ class Field(object): """Default decoder. Recursively deserialize into immutable types ( - :class:`letsencrypt.acme.jose.util.frozendict` instead of + :class:`acme.jose.util.frozendict` instead of :func:`dict`, :func:`tuple` instead of :func:`list`). """ @@ -304,7 +304,7 @@ def encode_cert(cert): """Encode certificate as JOSE Base-64 DER. :param cert: Certificate. - :type cert: :class:`letsencrypt.acme.jose.util.ComparableX509` + :type cert: :class:`acme.jose.util.ComparableX509` """ return b64.b64encode(cert.as_der()) @@ -381,7 +381,7 @@ class TypedJSONObjectWithFields(JSONObjectWithFields): :returns: Serializable JSON object representing ACME typed object. :meth:`validate` will almost certainly not work, due to reasons - explained in :class:`letsencrypt.acme.interfaces.IJSONSerializable`. + explained in :class:`acme.interfaces.IJSONSerializable`. :rtype: dict """ @@ -393,7 +393,7 @@ class TypedJSONObjectWithFields(JSONObjectWithFields): def from_json(cls, jobj): """Deserialize ACME object from valid JSON object. - :raises letsencrypt.acme.errors.UnrecognizedTypeError: if type + :raises acme.errors.UnrecognizedTypeError: if type of the ACME object has not been registered. """ diff --git a/letsencrypt/acme/jose/json_util_test.py b/acme/jose/json_util_test.py similarity index 85% rename from letsencrypt/acme/jose/json_util_test.py rename to acme/jose/json_util_test.py index 88818ed07..5726ef2a8 100644 --- a/letsencrypt/acme/jose/json_util_test.py +++ b/acme/jose/json_util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.jose.json_util.""" +"""Tests for acme.jose.json_util.""" import os import pkg_resources import unittest @@ -6,19 +6,19 @@ import unittest import M2Crypto import mock -from letsencrypt.acme.jose import errors -from letsencrypt.acme.jose import interfaces -from letsencrypt.acme.jose import util +from acme.jose import errors +from acme.jose import interfaces +from acme.jose import util CERT = M2Crypto.X509.load_cert(pkg_resources.resource_filename( - 'letsencrypt.client.tests', os.path.join('testdata', 'cert.pem'))) + 'letsencrypt.tests', os.path.join('testdata', 'cert.pem'))) CSR = M2Crypto.X509.load_request(pkg_resources.resource_filename( - 'letsencrypt.client.tests', os.path.join('testdata', 'csr.pem'))) + 'letsencrypt.tests', os.path.join('testdata', 'csr.pem'))) class FieldTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.json_util.Field.""" + """Tests for acme.jose.json_util.Field.""" def test_descriptors(self): mock_value = mock.MagicMock() @@ -31,7 +31,7 @@ class FieldTest(unittest.TestCase): def encoder(unused_value): return 'e' - from letsencrypt.acme.jose.json_util import Field + from acme.jose.json_util import Field field = Field('foo') field = field.encoder(encoder) @@ -45,45 +45,45 @@ class FieldTest(unittest.TestCase): class MockField(interfaces.JSONDeSerializable): # pylint: disable=missing-docstring def to_partial_json(self): - return 'foo' + return 'foo' # pragma: no cover @classmethod def from_json(cls, jobj): - pass + pass # pragma: no cover mock_field = MockField() - from letsencrypt.acme.jose.json_util import Field + from acme.jose.json_util import Field self.assertTrue(Field.default_encoder(mock_field) is mock_field) # in particular... self.assertNotEqual('foo', Field.default_encoder(mock_field)) def test_default_encoder_passthrough(self): mock_value = mock.MagicMock() - from letsencrypt.acme.jose.json_util import Field + from acme.jose.json_util import Field self.assertTrue(Field.default_encoder(mock_value) is mock_value) def test_default_decoder_list_to_tuple(self): - from letsencrypt.acme.jose.json_util import Field + from acme.jose.json_util import Field self.assertEqual((1, 2, 3), Field.default_decoder([1, 2, 3])) def test_default_decoder_dict_to_frozendict(self): - from letsencrypt.acme.jose.json_util import Field + from acme.jose.json_util import Field obj = Field.default_decoder({'x': 2}) self.assertTrue(isinstance(obj, util.frozendict)) self.assertEqual(obj, util.frozendict(x=2)) def test_default_decoder_passthrough(self): mock_value = mock.MagicMock() - from letsencrypt.acme.jose.json_util import Field + from acme.jose.json_util import Field self.assertTrue(Field.default_decoder(mock_value) is mock_value) class JSONObjectWithFieldsTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.json_util.JSONObjectWithFields.""" + """Tests for acme.jose.json_util.JSONObjectWithFields.""" # pylint: disable=protected-access def setUp(self): - from letsencrypt.acme.jose.json_util import JSONObjectWithFields - from letsencrypt.acme.jose.json_util import Field + from acme.jose.json_util import JSONObjectWithFields + from acme.jose.json_util import Field class MockJSONObjectWithFields(JSONObjectWithFields): # pylint: disable=invalid-name,missing-docstring,no-self-argument @@ -185,11 +185,11 @@ class DeEncodersTest(unittest.TestCase): ) def test_decode_b64_jose_padding_error(self): - from letsencrypt.acme.jose.json_util import decode_b64jose + from acme.jose.json_util import decode_b64jose self.assertRaises(errors.DeserializationError, decode_b64jose, 'x') def test_decode_b64_jose_size(self): - from letsencrypt.acme.jose.json_util import decode_b64jose + from acme.jose.json_util import decode_b64jose self.assertEqual('foo', decode_b64jose('Zm9v', size=3)) self.assertRaises( errors.DeserializationError, decode_b64jose, 'Zm9v', size=2) @@ -197,44 +197,44 @@ class DeEncodersTest(unittest.TestCase): errors.DeserializationError, decode_b64jose, 'Zm9v', size=4) def test_decode_b64_jose_minimum_size(self): - from letsencrypt.acme.jose.json_util import decode_b64jose + from acme.jose.json_util import decode_b64jose self.assertEqual('foo', decode_b64jose('Zm9v', size=3, minimum=True)) self.assertEqual('foo', decode_b64jose('Zm9v', size=2, minimum=True)) self.assertRaises(errors.DeserializationError, decode_b64jose, 'Zm9v', size=4, minimum=True) def test_decode_hex16(self): - from letsencrypt.acme.jose.json_util import decode_hex16 + from acme.jose.json_util import decode_hex16 self.assertEqual('foo', decode_hex16('666f6f')) def test_decode_hex16_minimum_size(self): - from letsencrypt.acme.jose.json_util import decode_hex16 + from acme.jose.json_util import decode_hex16 self.assertEqual('foo', decode_hex16('666f6f', size=3, minimum=True)) self.assertEqual('foo', decode_hex16('666f6f', size=2, minimum=True)) self.assertRaises(errors.DeserializationError, decode_hex16, '666f6f', size=4, minimum=True) def test_decode_hex16_odd_length(self): - from letsencrypt.acme.jose.json_util import decode_hex16 + from acme.jose.json_util import decode_hex16 self.assertRaises(errors.DeserializationError, decode_hex16, 'x') def test_encode_cert(self): - from letsencrypt.acme.jose.json_util import encode_cert + from acme.jose.json_util import encode_cert self.assertEqual(self.b64_cert, encode_cert(CERT)) def test_decode_cert(self): - from letsencrypt.acme.jose.json_util import decode_cert + from acme.jose.json_util import decode_cert cert = decode_cert(self.b64_cert) self.assertTrue(isinstance(cert, util.ComparableX509)) self.assertEqual(cert, CERT) self.assertRaises(errors.DeserializationError, decode_cert, '') def test_encode_csr(self): - from letsencrypt.acme.jose.json_util import encode_csr + from acme.jose.json_util import encode_csr self.assertEqual(self.b64_cert, encode_csr(CERT)) def test_decode_csr(self): - from letsencrypt.acme.jose.json_util import decode_csr + from acme.jose.json_util import decode_csr csr = decode_csr(self.b64_csr) self.assertTrue(isinstance(csr, util.ComparableX509)) self.assertEqual(csr, CSR) @@ -244,7 +244,7 @@ class DeEncodersTest(unittest.TestCase): class TypedJSONObjectWithFieldsTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.jose.json_util import TypedJSONObjectWithFields + from acme.jose.json_util import TypedJSONObjectWithFields # pylint: disable=missing-docstring,abstract-method # pylint: disable=too-few-public-methods @@ -294,4 +294,4 @@ class TypedJSONObjectWithFieldsTest(unittest.TestCase): if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/jwa.py b/acme/jose/jwa.py similarity index 96% rename from letsencrypt/acme/jose/jwa.py rename to acme/jose/jwa.py index b1f058d77..97c770b78 100644 --- a/letsencrypt/acme/jose/jwa.py +++ b/acme/jose/jwa.py @@ -13,9 +13,9 @@ from Crypto.Hash import SHA512 from Crypto.Signature import PKCS1_PSS from Crypto.Signature import PKCS1_v1_5 -from letsencrypt.acme.jose import errors -from letsencrypt.acme.jose import interfaces -from letsencrypt.acme.jose import jwk +from acme.jose import errors +from acme.jose import interfaces +from acme.jose import jwk class JWA(interfaces.JSONDeSerializable): # pylint: disable=abstract-method diff --git a/letsencrypt/acme/jose/jwa_test.py b/acme/jose/jwa_test.py similarity index 80% rename from letsencrypt/acme/jose/jwa_test.py rename to acme/jose/jwa_test.py index 48fdfce0d..f66b2a250 100644 --- a/letsencrypt/acme/jose/jwa_test.py +++ b/acme/jose/jwa_test.py @@ -1,11 +1,11 @@ -"""Tests for letsencrypt.acme.jose.jwa.""" +"""Tests for acme.jose.jwa.""" import os import pkg_resources import unittest from Crypto.PublicKey import RSA -from letsencrypt.acme.jose import errors +from acme.jose import errors RSA256_KEY = RSA.importKey(pkg_resources.resource_string( @@ -17,19 +17,19 @@ RSA1024_KEY = RSA.importKey(pkg_resources.resource_string( class JWASignatureTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.jwa.JWASignature.""" + """Tests for acme.jose.jwa.JWASignature.""" def setUp(self): - from letsencrypt.acme.jose.jwa import JWASignature + from acme.jose.jwa import JWASignature class MockSig(JWASignature): # pylint: disable=missing-docstring,too-few-public-methods # pylint: disable=abstract-class-not-used def sign(self, key, msg): - raise NotImplementedError() + raise NotImplementedError() # pragma: no cover def verify(self, key, msg, sig): - raise NotImplementedError() + raise NotImplementedError() # pragma: no cover # pylint: disable=invalid-name self.Sig1 = MockSig('Sig1') @@ -48,15 +48,15 @@ class JWASignatureTest(unittest.TestCase): self.assertEqual(self.Sig2.to_partial_json(), 'Sig2') def test_from_json(self): - from letsencrypt.acme.jose.jwa import JWASignature - from letsencrypt.acme.jose.jwa import RS256 + from acme.jose.jwa import JWASignature + from acme.jose.jwa import RS256 self.assertTrue(JWASignature.from_json('RS256') is RS256) class JWAHSTest(unittest.TestCase): # pylint: disable=too-few-public-methods def test_it(self): - from letsencrypt.acme.jose.jwa import HS256 + from acme.jose.jwa import HS256 sig = ( "\xceR\xea\xcd\x94\xab\xcf\xfb\xe0\xacA.:\x1a'\x08i\xe2\xc4" "\r\x85+\x0e\x85\xaeUZ\xd4\xb3\x97zO" @@ -69,19 +69,19 @@ class JWAHSTest(unittest.TestCase): # pylint: disable=too-few-public-methods class JWARSTest(unittest.TestCase): def test_sign_no_private_part(self): - from letsencrypt.acme.jose.jwa import RS256 + from acme.jose.jwa import RS256 self.assertRaises( errors.Error, RS256.sign, RSA512_KEY.publickey(), 'foo') def test_sign_key_too_small(self): - from letsencrypt.acme.jose.jwa import RS256 - from letsencrypt.acme.jose.jwa import PS256 + from acme.jose.jwa import RS256 + from acme.jose.jwa import PS256 self.assertRaises(errors.Error, RS256.sign, RSA256_KEY, 'foo') self.assertRaises(errors.Error, PS256.sign, RSA256_KEY, 'foo') self.assertRaises(errors.Error, PS256.sign, RSA512_KEY, 'foo') def test_rs(self): - from letsencrypt.acme.jose.jwa import RS256 + from acme.jose.jwa import RS256 sig = ( '|\xc6\xb2\xa4\xab(\x87\x99\xfa*:\xea\xf8\xa0N&}\x9f\x0f\xc0O' '\xc6t\xa3\xe6\xfa\xbb"\x15Y\x80Y\xe0\x81\xb8\x88)\xba\x0c\x9c' @@ -95,11 +95,11 @@ class JWARSTest(unittest.TestCase): self.assertFalse(RS256.verify(RSA512_KEY, 'foo', sig + '!') is False) def test_ps(self): - from letsencrypt.acme.jose.jwa import PS256 + from acme.jose.jwa import PS256 sig = PS256.sign(RSA1024_KEY, 'foo') self.assertTrue(PS256.verify(RSA1024_KEY, 'foo', sig) is True) self.assertTrue(PS256.verify(RSA1024_KEY, 'foo', sig + '!') is False) if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/jwk.py b/acme/jose/jwk.py similarity index 95% rename from letsencrypt/acme/jose/jwk.py rename to acme/jose/jwk.py index ec35baa18..7c55e99a8 100644 --- a/letsencrypt/acme/jose/jwk.py +++ b/acme/jose/jwk.py @@ -4,10 +4,10 @@ import binascii import Crypto.PublicKey.RSA -from letsencrypt.acme.jose import b64 -from letsencrypt.acme.jose import errors -from letsencrypt.acme.jose import json_util -from letsencrypt.acme.jose import util +from acme.jose import b64 +from acme.jose import errors +from acme.jose import json_util +from acme.jose import util class JWK(json_util.TypedJSONObjectWithFields): diff --git a/letsencrypt/acme/jose/jwk_test.py b/acme/jose/jwk_test.py similarity index 80% rename from letsencrypt/acme/jose/jwk_test.py rename to acme/jose/jwk_test.py index 1328528e8..39d595f94 100644 --- a/letsencrypt/acme/jose/jwk_test.py +++ b/acme/jose/jwk_test.py @@ -1,12 +1,12 @@ -"""Tests for letsencrypt.acme.jose.jwk.""" +"""Tests for acme.jose.jwk.""" import os import pkg_resources import unittest from Crypto.PublicKey import RSA -from letsencrypt.acme.jose import errors -from letsencrypt.acme.jose import util +from acme.jose import errors +from acme.jose import util RSA256_KEY = util.HashableRSAKey(RSA.importKey(pkg_resources.resource_string( @@ -16,10 +16,10 @@ RSA512_KEY = util.HashableRSAKey(RSA.importKey(pkg_resources.resource_string( class JWKOctTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.jwk.JWKOct.""" + """Tests for acme.jose.jwk.JWKOct.""" def setUp(self): - from letsencrypt.acme.jose.jwk import JWKOct + from acme.jose.jwk import JWKOct self.jwk = JWKOct(key='foo') self.jobj = {'kty': 'oct', 'k': 'foo'} @@ -27,15 +27,15 @@ class JWKOctTest(unittest.TestCase): self.assertEqual(self.jwk.to_partial_json(), self.jobj) def test_from_json(self): - from letsencrypt.acme.jose.jwk import JWKOct + from acme.jose.jwk import JWKOct self.assertEqual(self.jwk, JWKOct.from_json(self.jobj)) def test_from_json_hashable(self): - from letsencrypt.acme.jose.jwk import JWKOct + from acme.jose.jwk import JWKOct hash(JWKOct.from_json(self.jobj)) def test_load(self): - from letsencrypt.acme.jose.jwk import JWKOct + from acme.jose.jwk import JWKOct self.assertEqual(self.jwk, JWKOct.load('foo')) def test_public(self): @@ -43,10 +43,10 @@ class JWKOctTest(unittest.TestCase): class JWKRSATest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.jwk.JWKRSA.""" + """Tests for acme.jose.jwk.JWKRSA.""" def setUp(self): - from letsencrypt.acme.jose.jwk import JWKRSA + from acme.jose.jwk import JWKRSA self.jwk256 = JWKRSA(key=RSA256_KEY.publickey()) self.jwk256_private = JWKRSA(key=RSA256_KEY) self.jwk256json = { @@ -71,7 +71,7 @@ class JWKRSATest(unittest.TestCase): self.assertNotEqual(self.jwk512, self.jwk256) def test_load(self): - from letsencrypt.acme.jose.jwk import JWKRSA + from acme.jose.jwk import JWKRSA self.assertEqual( JWKRSA(key=util.HashableRSAKey(RSA256_KEY)), JWKRSA.load( pkg_resources.resource_string( @@ -85,18 +85,18 @@ class JWKRSATest(unittest.TestCase): self.assertEqual(self.jwk512.to_partial_json(), self.jwk512json) def test_from_json(self): - from letsencrypt.acme.jose.jwk import JWK + from acme.jose.jwk import JWK self.assertEqual(self.jwk256, JWK.from_json(self.jwk256json)) # TODO: fix schemata to allow RSA512 #self.assertEqual(self.jwk512, JWK.from_json(self.jwk512json)) def test_from_json_hashable(self): - from letsencrypt.acme.jose.jwk import JWK + from acme.jose.jwk import JWK hash(JWK.from_json(self.jwk256json)) def test_from_json_non_schema_errors(self): # valid against schema, but still failing - from letsencrypt.acme.jose.jwk import JWK + from acme.jose.jwk import JWK self.assertRaises(errors.DeserializationError, JWK.from_json, {'kty': 'RSA', 'e': 'AQAB', 'n': ''}) self.assertRaises(errors.DeserializationError, JWK.from_json, @@ -104,4 +104,4 @@ class JWKRSATest(unittest.TestCase): if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/jws.py b/acme/jose/jws.py similarity index 96% rename from letsencrypt/acme/jose/jws.py rename to acme/jose/jws.py index fc37227fd..06923e145 100644 --- a/letsencrypt/acme/jose/jws.py +++ b/acme/jose/jws.py @@ -5,12 +5,12 @@ import sys import M2Crypto -from letsencrypt.acme.jose import b64 -from letsencrypt.acme.jose import errors -from letsencrypt.acme.jose import json_util -from letsencrypt.acme.jose import jwa -from letsencrypt.acme.jose import jwk -from letsencrypt.acme.jose import util +from acme.jose import b64 +from acme.jose import errors +from acme.jose import json_util +from acme.jose import jwa +from acme.jose import jwk +from acme.jose import util class MediaType(object): @@ -103,9 +103,9 @@ class Header(json_util.JSONObjectWithFields): .. todo:: Supports only "jwk" header parameter lookup. :returns: (Public) key found in the header. - :rtype: :class:`letsencrypt.acme.jose.jwk.JWK` + :rtype: :class:`acme.jose.jwk.JWK` - :raises letsencrypt.acme.jose.errors.Error: if key could not be found + :raises acme.jose.errors.Error: if key could not be found """ if self.jwk is None: @@ -180,7 +180,7 @@ class Signature(json_util.JSONObjectWithFields): """Verify. :param key: Key used for verification. - :type key: :class:`letsencrypt.acme.jose.jwk.JWK` + :type key: :class:`acme.jose.jwk.JWK` """ key = self.combined.find_key() if key is None else key @@ -195,7 +195,7 @@ class Signature(json_util.JSONObjectWithFields): """Sign. :param key: Key for signature. - :type key: :class:`letsencrypt.acme.jose.jwk.JWK` + :type key: :class:`acme.jose.jwk.JWK` """ assert isinstance(key, alg.kty) @@ -241,8 +241,6 @@ class Signature(json_util.JSONObjectWithFields): class JWS(json_util.JSONObjectWithFields): """JSON Web Signature. - from letsencrypt.acme.jose import interfaces - :ivar str payload: JWS Payload. :ivar str signaturea: JWS Signatures. diff --git a/letsencrypt/acme/jose/jws_test.py b/acme/jose/jws_test.py similarity index 82% rename from letsencrypt/acme/jose/jws_test.py rename to acme/jose/jws_test.py index dca61c3d9..6ecce63d2 100644 --- a/letsencrypt/acme/jose/jws_test.py +++ b/acme/jose/jws_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.jose.jws.""" +"""Tests for acme.jose.jws.""" import base64 import os import pkg_resources @@ -8,49 +8,49 @@ import Crypto.PublicKey.RSA import M2Crypto import mock -from letsencrypt.acme.jose import b64 -from letsencrypt.acme.jose import errors -from letsencrypt.acme.jose import jwa -from letsencrypt.acme.jose import jwk -from letsencrypt.acme.jose import util +from acme.jose import b64 +from acme.jose import errors +from acme.jose import jwa +from acme.jose import jwk +from acme.jose import util CERT = util.ComparableX509(M2Crypto.X509.load_cert( pkg_resources.resource_filename( - 'letsencrypt.client.tests', 'testdata/cert.pem'))) + 'letsencrypt.tests', 'testdata/cert.pem'))) RSA512_KEY = Crypto.PublicKey.RSA.importKey(pkg_resources.resource_string( __name__, os.path.join('testdata', 'rsa512_key.pem'))) class MediaTypeTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.jws.MediaType.""" + """Tests for acme.jose.jws.MediaType.""" def test_decode(self): - from letsencrypt.acme.jose.jws import MediaType + from acme.jose.jws import MediaType self.assertEqual('application/app', MediaType.decode('application/app')) self.assertEqual('application/app', MediaType.decode('app')) self.assertRaises( errors.DeserializationError, MediaType.decode, 'app;foo') def test_encode(self): - from letsencrypt.acme.jose.jws import MediaType + from acme.jose.jws import MediaType self.assertEqual('app', MediaType.encode('application/app')) self.assertEqual('application/app;foo', MediaType.encode('application/app;foo')) class HeaderTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.jws.Header.""" + """Tests for acme.jose.jws.Header.""" def setUp(self): - from letsencrypt.acme.jose.jws import Header + from acme.jose.jws import Header self.header1 = Header(jwk='foo') self.header2 = Header(jwk='bar') self.crit = Header(crit=('a', 'b')) self.empty = Header() def test_add_non_empty(self): - from letsencrypt.acme.jose.jws import Header + from acme.jose.jws import Header self.assertEqual(Header(jwk='foo', crit=('a', 'b')), self.header1 + self.crit) @@ -65,12 +65,12 @@ class HeaderTest(unittest.TestCase): self.assertRaises(TypeError, self.header1.__add__, 'xxx') def test_crit_decode_always_errors(self): - from letsencrypt.acme.jose.jws import Header + from acme.jose.jws import Header self.assertRaises(errors.DeserializationError, Header.from_json, {'crit': ['a', 'b']}) def test_x5c_decoding(self): - from letsencrypt.acme.jose.jws import Header + from acme.jose.jws import Header header = Header(x5c=(CERT, CERT)) jobj = header.to_partial_json() cert_b64 = base64.b64encode(CERT.as_der()) @@ -86,30 +86,30 @@ class HeaderTest(unittest.TestCase): class SignatureTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.jws.Signature.""" + """Tests for acme.jose.jws.Signature.""" def test_from_json(self): - from letsencrypt.acme.jose.jws import Header - from letsencrypt.acme.jose.jws import Signature + from acme.jose.jws import Header + from acme.jose.jws import Signature self.assertEqual( Signature(signature='foo', header=Header(alg=jwa.RS256)), Signature.from_json( {'signature': 'Zm9v', 'header': {'alg': 'RS256'}})) def test_from_json_no_alg_error(self): - from letsencrypt.acme.jose.jws import Signature + from acme.jose.jws import Signature self.assertRaises(errors.DeserializationError, Signature.from_json, {'signature': 'foo'}) class JWSTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.jws.JWS.""" + """Tests for acme.jose.jws.JWS.""" def setUp(self): self.privkey = jwk.JWKRSA(key=RSA512_KEY) self.pubkey = self.privkey.public() - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS self.unprotected = JWS.sign( payload='foo', key=self.privkey, alg=jwa.RS256) self.protected = JWS.sign( @@ -140,7 +140,7 @@ class JWSTest(unittest.TestCase): '_893n1zQjpim_eLS5J1F61lkvrCrCDErTEJnBGOGesJ72M7b6Ve1cAJA', compact) - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS mixed = JWS.from_compact(compact) self.assertNotEqual(self.mixed, mixed) @@ -148,7 +148,7 @@ class JWSTest(unittest.TestCase): set(['alg']), set(mixed.signature.combined.not_omitted())) def test_from_compact_missing_components(self): - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS self.assertRaises(errors.DeserializationError, JWS.from_compact, '.') def test_json_omitempty(self): @@ -160,7 +160,7 @@ class JWSTest(unittest.TestCase): unprotected_jobj['header'] = unprotected_jobj['header'].to_json() - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS self.assertEqual(JWS.from_json(protected_jobj), self.protected) self.assertEqual(JWS.from_json(unprotected_jobj), self.unprotected) @@ -175,7 +175,7 @@ class JWSTest(unittest.TestCase): jobj_from['header'] = jobj_from['header'].to_json() self.assertEqual(self.mixed.to_partial_json(flat=True), jobj_to) - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS self.assertEqual(self.mixed, JWS.from_json(jobj_from)) def test_json_not_flat(self): @@ -187,16 +187,16 @@ class JWSTest(unittest.TestCase): jobj_from['signatures'] = [jobj_to['signatures'][0].to_json()] self.assertEqual(self.mixed.to_partial_json(flat=False), jobj_to) - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS self.assertEqual(self.mixed, JWS.from_json(jobj_from)) def test_from_json_mixed_flat(self): - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS self.assertRaises(errors.DeserializationError, JWS.from_json, {'signatures': (), 'signature': 'foo'}) def test_from_json_hashable(self): - from letsencrypt.acme.jose.jws import JWS + from acme.jose.jws import JWS hash(JWS.from_json(self.mixed.to_json())) @@ -207,14 +207,14 @@ class CLITest(unittest.TestCase): __name__, os.path.join('testdata', 'rsa512_key.pem')) def test_unverified(self): - from letsencrypt.acme.jose.jws import CLI + from acme.jose.jws import CLI with mock.patch('sys.stdin') as sin: sin.read.return_value = '{"payload": "foo", "signature": "xxx"}' with mock.patch('sys.stdout'): self.assertEqual(-1, CLI.run(['verify'])) def test_json(self): - from letsencrypt.acme.jose.jws import CLI + from acme.jose.jws import CLI with mock.patch('sys.stdin') as sin: sin.read.return_value = 'foo' @@ -225,7 +225,7 @@ class CLITest(unittest.TestCase): self.assertEqual(0, CLI.run(['verify'])) def test_compact(self): - from letsencrypt.acme.jose.jws import CLI + from acme.jose.jws import CLI with mock.patch('sys.stdin') as sin: sin.read.return_value = 'foo' @@ -238,4 +238,4 @@ class CLITest(unittest.TestCase): if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/jose/testdata/README b/acme/jose/testdata/README similarity index 79% rename from letsencrypt/acme/jose/testdata/README rename to acme/jose/testdata/README index 4b37ae921..72ec557e0 100644 --- a/letsencrypt/acme/jose/testdata/README +++ b/acme/jose/testdata/README @@ -4,7 +4,7 @@ The following command has been used to generate test keys: and for the CSR: - python -c from 'letsencrypt.client.crypto_util import make_csr; + python -c from 'letsencrypt.crypto_util import make_csr; import pkg_resources; open("csr2.pem", - "w").write(make_csr(pkg_resources.resource_string("letsencrypt.client.tests", + "w").write(make_csr(pkg_resources.resource_string("letsencrypt.tests", "testdata/rsa512_key.pem"), ["example2.com"])[0])' diff --git a/letsencrypt/acme/jose/testdata/csr2.pem b/acme/jose/testdata/csr2.pem similarity index 100% rename from letsencrypt/acme/jose/testdata/csr2.pem rename to acme/jose/testdata/csr2.pem diff --git a/letsencrypt/acme/jose/testdata/rsa1024_key.pem b/acme/jose/testdata/rsa1024_key.pem similarity index 100% rename from letsencrypt/acme/jose/testdata/rsa1024_key.pem rename to acme/jose/testdata/rsa1024_key.pem diff --git a/letsencrypt/acme/jose/testdata/rsa256_key.pem b/acme/jose/testdata/rsa256_key.pem similarity index 100% rename from letsencrypt/acme/jose/testdata/rsa256_key.pem rename to acme/jose/testdata/rsa256_key.pem diff --git a/letsencrypt/acme/jose/testdata/rsa512_key.pem b/acme/jose/testdata/rsa512_key.pem similarity index 100% rename from letsencrypt/acme/jose/testdata/rsa512_key.pem rename to acme/jose/testdata/rsa512_key.pem diff --git a/letsencrypt/acme/jose/util.py b/acme/jose/util.py similarity index 100% rename from letsencrypt/acme/jose/util.py rename to acme/jose/util.py diff --git a/letsencrypt/acme/jose/util_test.py b/acme/jose/util_test.py similarity index 87% rename from letsencrypt/acme/jose/util_test.py rename to acme/jose/util_test.py index fc75497e0..b5592c57e 100644 --- a/letsencrypt/acme/jose/util_test.py +++ b/acme/jose/util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.jose.util.""" +"""Tests for acme.jose.util.""" import functools import os import pkg_resources @@ -8,10 +8,10 @@ import Crypto.PublicKey.RSA class HashableRSAKeyTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.util.HashableRSAKey.""" + """Tests for acme.jose.util.HashableRSAKey.""" def setUp(self): - from letsencrypt.acme.jose.util import HashableRSAKey + from acme.jose.util import HashableRSAKey self.key = HashableRSAKey(Crypto.PublicKey.RSA.importKey( pkg_resources.resource_string( __name__, os.path.join('testdata', 'rsa256_key.pem')))) @@ -28,17 +28,17 @@ class HashableRSAKeyTest(unittest.TestCase): self.assertTrue(isinstance(hash(self.key), int)) def test_publickey(self): - from letsencrypt.acme.jose.util import HashableRSAKey + from acme.jose.util import HashableRSAKey self.assertTrue(isinstance(self.key.publickey(), HashableRSAKey)) class ImmutableMapTest(unittest.TestCase): - """Tests for letsencrypt.acme.jose.util.ImmutableMap.""" + """Tests for acme.jose.util.ImmutableMap.""" def setUp(self): # pylint: disable=invalid-name,too-few-public-methods # pylint: disable=missing-docstring - from letsencrypt.acme.jose.util import ImmutableMap + from acme.jose.util import ImmutableMap class A(ImmutableMap): __slots__ = ('x', 'y') @@ -101,18 +101,18 @@ class ImmutableMapTest(unittest.TestCase): class frozendictTest(unittest.TestCase): # pylint: disable=invalid-name - """Tests for letsencrypt.acme.jose.util.frozendict.""" + """Tests for acme.jose.util.frozendict.""" def setUp(self): - from letsencrypt.acme.jose.util import frozendict + from acme.jose.util import frozendict self.fdict = frozendict(x=1, y='2') def test_init_dict(self): - from letsencrypt.acme.jose.util import frozendict + from acme.jose.util import frozendict self.assertEqual(self.fdict, frozendict({'x': 1, 'y': '2'})) def test_init_other_raises_type_error(self): - from letsencrypt.acme.jose.util import frozendict + from acme.jose.util import frozendict # specifically fail for generators... self.assertRaises(TypeError, frozendict, {'a': 'b'}.iteritems()) @@ -137,4 +137,4 @@ class frozendictTest(unittest.TestCase): # pylint: disable=invalid-name if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/messages.py b/acme/messages.py similarity index 89% rename from letsencrypt/acme/messages.py rename to acme/messages.py index 41b7389a7..6d46f894c 100644 --- a/letsencrypt/acme/messages.py +++ b/acme/messages.py @@ -22,11 +22,11 @@ """ import jsonschema -from letsencrypt.acme import challenges -from letsencrypt.acme import errors -from letsencrypt.acme import jose -from letsencrypt.acme import other -from letsencrypt.acme import util +from acme import challenges +from acme import errors +from acme import jose +from acme import other +from acme import util class Message(jose.TypedJSONObjectWithFields): @@ -41,7 +41,7 @@ class Message(jose.TypedJSONObjectWithFields): Subclasses must overrride it with a value that is acceptable by :func:`jsonschema.validate`, most probably using - :func:`letsencrypt.acme.util.load_schema`. + :func:`acme.util.load_schema`. """ @@ -53,10 +53,10 @@ class Message(jose.TypedJSONObjectWithFields): :param jobj: JSON object. - :raises letsencrypt.acme.errors.SchemaValidationError: if the input + :raises acme.errors.SchemaValidationError: if the input JSON object could not be validated against JSON schema specified in :attr:`schema`. - :raises letsencrypt.acme.jose.errors.DeserializationError: for any + :raises acme.jose.errors.DeserializationError: for any other generic error in decoding. :returns: instance of the class @@ -79,7 +79,7 @@ class Challenge(Message): :ivar str nonce: Random data, **not** base64-encoded. :ivar list challenges: List of - :class:`~letsencrypt.acme.challenges.Challenge` objects. + :class:`~acme.challenges.Challenge` objects. .. todo:: 1. can challenges contain two challenges of the same type? @@ -121,7 +121,7 @@ class ChallengeRequest(Message): class Authorization(Message): """ACME "authorization" message. - :ivar jwk: :class:`letsencrypt.acme.jose.JWK` + :ivar jwk: :class:`acme.jose.JWK` """ typ = "authorization" @@ -139,8 +139,8 @@ class AuthorizationRequest(Message): :ivar str nonce: Random data from the corresponding :attr:`Challenge.nonce`, **not** base64-encoded. :ivar list responses: List of completed challenges ( - :class:`letsencrypt.acme.challenges.ChallengeResponse`). - :ivar signature: Signature (:class:`letsencrypt.acme.other.Signature`). + :class:`acme.challenges.ChallengeResponse`). + :ivar signature: Signature (:class:`acme.other.Signature`). """ typ = "authorizationRequest" @@ -184,7 +184,7 @@ class AuthorizationRequest(Message): """Verify signature. .. warning:: Caller must check that the public key encoded in the - :attr:`signature`'s :class:`letsencrypt.acme.jose.JWK` object + :attr:`signature`'s :class:`acme.jose.JWK` object is the correct key for a given context. :param str name: Hostname @@ -202,10 +202,10 @@ class Certificate(Message): """ACME "certificate" message. :ivar certificate: The certificate (:class:`M2Crypto.X509.X509` - wrapped in :class:`letsencrypt.acme.util.ComparableX509`). + wrapped in :class:`acme.util.ComparableX509`). :ivar list chain: Chain of certificates (:class:`M2Crypto.X509.X509` - wrapped in :class:`letsencrypt.acme.util.ComparableX509` ). + wrapped in :class:`acme.util.ComparableX509` ). """ typ = "certificate" @@ -230,8 +230,8 @@ class CertificateRequest(Message): """ACME "certificateRequest" message. :ivar csr: Certificate Signing Request (:class:`M2Crypto.X509.Request` - wrapped in :class:`letsencrypt.acme.util.ComparableX509`. - :ivar signature: Signature (:class:`letsencrypt.acme.other.Signature`). + wrapped in :class:`acme.util.ComparableX509`. + :ivar signature: Signature (:class:`acme.other.Signature`). """ typ = "certificateRequest" @@ -262,7 +262,7 @@ class CertificateRequest(Message): """Verify signature. .. warning:: Caller must check that the public key encoded in the - :attr:`signature`'s :class:`letsencrypt.acme.jose.JWK` object + :attr:`signature`'s :class:`acme.jose.JWK` object is the correct key for a given context. :returns: True iff ``signature`` can be verified, False otherwise. @@ -316,8 +316,8 @@ class RevocationRequest(Message): """ACME "revocationRequest" message. :ivar certificate: Certificate (:class:`M2Crypto.X509.X509` - wrapped in :class:`letsencrypt.acme.util.ComparableX509`). - :ivar signature: Signature (:class:`letsencrypt.acme.other.Signature`). + wrapped in :class:`acme.util.ComparableX509`). + :ivar signature: Signature (:class:`acme.other.Signature`). """ typ = "revocationRequest" @@ -348,7 +348,7 @@ class RevocationRequest(Message): """Verify signature. .. warning:: Caller must check that the public key encoded in the - :attr:`signature`'s :class:`letsencrypt.acme.jose.JWK` object + :attr:`signature`'s :class:`acme.jose.JWK` object is the correct key for a given context. :returns: True iff ``signature`` can be verified, False otherwise. diff --git a/letsencrypt/acme/messages2.py b/acme/messages2.py similarity index 91% rename from letsencrypt/acme/messages2.py rename to acme/messages2.py index a2829ff57..419bb0b4e 100644 --- a/letsencrypt/acme/messages2.py +++ b/acme/messages2.py @@ -1,7 +1,7 @@ """ACME protocol messages.""" -from letsencrypt.acme import challenges -from letsencrypt.acme import fields -from letsencrypt.acme import jose +from acme import challenges +from acme import fields +from acme import jose class Error(jose.JSONObjectWithFields, Exception): @@ -101,7 +101,7 @@ IDENTIFIER_FQDN = IdentifierType('dns') # IdentifierDNS in Boulder class Identifier(jose.JSONObjectWithFields): """ACME identifier. - :ivar letsencrypt.acme.messages2.IdentifierType typ: + :ivar acme.messages2.IdentifierType typ: """ typ = jose.Field('type', decoder=IdentifierType.from_json) @@ -111,7 +111,7 @@ class Identifier(jose.JSONObjectWithFields): class Resource(jose.ImmutableMap): """ACME Resource. - :ivar letsencrypt.acme.messages2.ResourceBody body: Resource body. + :ivar acme.messages2.ResourceBody body: Resource body. :ivar str uri: Location of the resource. """ @@ -125,7 +125,7 @@ class ResourceBody(jose.JSONObjectWithFields): class RegistrationResource(Resource): """Registration Resource. - :ivar letsencrypt.acme.messages2.Registration body: + :ivar acme.messages2.Registration body: :ivar str new_authzr_uri: URI found in the 'next' ``Link`` header :ivar str terms_of_service: URL for the CA TOS. @@ -136,7 +136,7 @@ class RegistrationResource(Resource): class Registration(ResourceBody): """Registration Resource Body. - :ivar letsencrypt.acme.jose.jwk.JWK key: Public key. + :ivar acme.jose.jwk.JWK key: Public key. :ivar tuple contact: Contact information following ACME spec """ @@ -151,7 +151,7 @@ class Registration(ResourceBody): class ChallengeResource(Resource, jose.JSONObjectWithFields): """Challenge Resource. - :ivar letsencrypt.acme.messages2.ChallengeBody body: + :ivar acme.messages2.ChallengeBody body: :ivar str authzr_uri: URI found in the 'up' ``Link`` header. """ @@ -173,10 +173,10 @@ class ChallengeBody(ResourceBody): such as ``challb`` to distinguish instances of this class from ``achall``. - :ivar letsencrypt.acme.challenges.Challenge: Wrapped challenge. + :ivar acme.challenges.Challenge: Wrapped challenge. Conveniently, all challenge fields are proxied, i.e. you can call ``challb.x`` to get ``challb.chall.x`` contents. - :ivar letsencrypt.acme.messages2.Status status: + :ivar acme.messages2.Status status: :ivar datetime.datetime validated: """ @@ -203,7 +203,7 @@ class ChallengeBody(ResourceBody): class AuthorizationResource(Resource): """Authorization Resource. - :ivar letsencrypt.acme.messages2.Authorization body: + :ivar acme.messages2.Authorization body: :ivar str new_cert_uri: URI found in the 'next' ``Link`` header """ @@ -213,13 +213,13 @@ class AuthorizationResource(Resource): class Authorization(ResourceBody): """Authorization Resource Body. - :ivar letsencrypt.acme.messages2.Identifier identifier: + :ivar acme.messages2.Identifier identifier: :ivar list challenges: `list` of `.ChallengeBody` :ivar tuple combinations: Challenge combinations (`tuple` of `tuple` of `int`, as opposed to `list` of `list` from the spec). - :ivar letsencrypt.acme.jose.jwk.JWK key: Public key. + :ivar acme.jose.jwk.JWK key: Public key. :ivar tuple contact: - :ivar letsencrypt.acme.messages2.Status status: + :ivar acme.messages2.Status status: :ivar datetime.datetime expires: """ @@ -252,7 +252,7 @@ class Authorization(ResourceBody): class CertificateRequest(jose.JSONObjectWithFields): """ACME new-cert request. - :ivar letsencrypt.acme.jose.util.ComparableX509 csr: + :ivar acme.jose.util.ComparableX509 csr: `M2Crypto.X509.Request` wrapped in `.ComparableX509` :ivar tuple authorizations: `tuple` of URIs (`str`) @@ -264,7 +264,7 @@ class CertificateRequest(jose.JSONObjectWithFields): class CertificateResource(Resource): """Certificate Resource. - :ivar letsencrypt.acme.jose.util.ComparableX509 body: + :ivar acme.jose.util.ComparableX509 body: `M2Crypto.X509.X509` wrapped in `.ComparableX509` :ivar str cert_chain_uri: URI found in the 'up' ``Link`` header :ivar tuple authzrs: `tuple` of `AuthorizationResource`. diff --git a/letsencrypt/acme/messages2_test.py b/acme/messages2_test.py similarity index 78% rename from letsencrypt/acme/messages2_test.py rename to acme/messages2_test.py index 9e8ef33c8..5f9441b4f 100644 --- a/letsencrypt/acme/messages2_test.py +++ b/acme/messages2_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.messages2.""" +"""Tests for acme.messages2.""" import datetime import os import pkg_resources @@ -8,19 +8,19 @@ import mock import pytz from Crypto.PublicKey import RSA -from letsencrypt.acme import challenges -from letsencrypt.acme import jose +from acme import challenges +from acme import jose KEY = jose.util.HashableRSAKey(RSA.importKey(pkg_resources.resource_string( - 'letsencrypt.acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) + 'acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) class ErrorTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages2.Error.""" + """Tests for acme.messages2.Error.""" def setUp(self): - from letsencrypt.acme.messages2 import Error + from acme.messages2 import Error self.error = Error(detail='foo', typ='malformed') def test_typ_prefix(self): @@ -31,14 +31,14 @@ class ErrorTest(unittest.TestCase): 'malformed', self.error.from_json(self.error.to_partial_json()).typ) def test_typ_decoder_missing_prefix(self): - from letsencrypt.acme.messages2 import Error + from acme.messages2 import Error self.assertRaises(jose.DeserializationError, Error.from_json, {'detail': 'foo', 'type': 'malformed'}) self.assertRaises(jose.DeserializationError, Error.from_json, {'detail': 'foo', 'type': 'not valid bare type'}) def test_typ_decoder_not_recognized(self): - from letsencrypt.acme.messages2 import Error + from acme.messages2 import Error self.assertRaises(jose.DeserializationError, Error.from_json, {'detail': 'foo', 'type': 'urn:acme:error:baz'}) @@ -47,7 +47,7 @@ class ErrorTest(unittest.TestCase): 'The request message was malformed', self.error.description) def test_from_json_hashable(self): - from letsencrypt.acme.messages2 import Error + from acme.messages2 import Error hash(Error.from_json(self.error.to_json())) def test_str(self): @@ -58,10 +58,10 @@ class ErrorTest(unittest.TestCase): class ConstantTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages2._Constant.""" + """Tests for acme.messages2._Constant.""" def setUp(self): - from letsencrypt.acme.messages2 import _Constant + from acme.messages2 import _Constant class MockConstant(_Constant): # pylint: disable=missing-docstring POSSIBLE_NAMES = {} @@ -94,7 +94,7 @@ class ConstantTest(unittest.TestCase): self.assertFalse(self.const_a != const_a_prime) class RegistrationTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages2.Registration.""" + """Tests for acme.messages2.Registration.""" def setUp(self): key = jose.jwk.JWKRSA(key=KEY.publickey()) @@ -102,7 +102,7 @@ class RegistrationTest(unittest.TestCase): recovery_token = 'XYZ' agreement = 'https://letsencrypt.org/terms' - from letsencrypt.acme.messages2 import Registration + from acme.messages2 import Registration self.reg = Registration( key=key, contact=contact, recovery_token=recovery_token, agreement=agreement) @@ -120,31 +120,31 @@ class RegistrationTest(unittest.TestCase): self.assertEqual(self.jobj_to, self.reg.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.messages2 import Registration + from acme.messages2 import Registration self.assertEqual(self.reg, Registration.from_json(self.jobj_from)) def test_from_json_hashable(self): - from letsencrypt.acme.messages2 import Registration + from acme.messages2 import Registration hash(Registration.from_json(self.jobj_from)) class ChallengeResourceTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages2.ChallengeResource.""" + """Tests for acme.messages2.ChallengeResource.""" def test_uri(self): - from letsencrypt.acme.messages2 import ChallengeResource + from acme.messages2 import ChallengeResource self.assertEqual('http://challb', ChallengeResource(body=mock.MagicMock( uri='http://challb'), authzr_uri='http://authz').uri) class ChallengeBodyTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages2.ChallengeBody.""" + """Tests for acme.messages2.ChallengeBody.""" def setUp(self): self.chall = challenges.DNS(token='foo') - from letsencrypt.acme.messages2 import ChallengeBody - from letsencrypt.acme.messages2 import STATUS_VALID + from acme.messages2 import ChallengeBody + from acme.messages2 import STATUS_VALID self.status = STATUS_VALID self.challb = ChallengeBody( uri='http://challb', chall=self.chall, status=self.status) @@ -162,11 +162,11 @@ class ChallengeBodyTest(unittest.TestCase): self.assertEqual(self.jobj_to, self.challb.to_partial_json()) def test_from_json(self): - from letsencrypt.acme.messages2 import ChallengeBody + from acme.messages2 import ChallengeBody self.assertEqual(self.challb, ChallengeBody.from_json(self.jobj_from)) def test_from_json_hashable(self): - from letsencrypt.acme.messages2 import ChallengeBody + from acme.messages2 import ChallengeBody hash(ChallengeBody.from_json(self.jobj_from)) def test_proxy(self): @@ -174,11 +174,11 @@ class ChallengeBodyTest(unittest.TestCase): class AuthorizationTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages2.Authorization.""" + """Tests for acme.messages2.Authorization.""" def setUp(self): - from letsencrypt.acme.messages2 import ChallengeBody - from letsencrypt.acme.messages2 import STATUS_VALID + from acme.messages2 import ChallengeBody + from acme.messages2 import STATUS_VALID self.challbs = ( ChallengeBody( uri='http://challb1', status=STATUS_VALID, @@ -190,9 +190,9 @@ class AuthorizationTest(unittest.TestCase): ) combinations = ((0, 2), (1, 2)) - from letsencrypt.acme.messages2 import Authorization - from letsencrypt.acme.messages2 import Identifier - from letsencrypt.acme.messages2 import IDENTIFIER_FQDN + from acme.messages2 import Authorization + from acme.messages2 import Identifier + from acme.messages2 import IDENTIFIER_FQDN identifier = Identifier(typ=IDENTIFIER_FQDN, value='example.com') self.authz = Authorization( identifier=identifier, combinations=combinations, @@ -205,11 +205,11 @@ class AuthorizationTest(unittest.TestCase): } def test_from_json(self): - from letsencrypt.acme.messages2 import Authorization + from acme.messages2 import Authorization Authorization.from_json(self.jobj_from) def test_from_json_hashable(self): - from letsencrypt.acme.messages2 import Authorization + from acme.messages2 import Authorization hash(Authorization.from_json(self.jobj_from)) def test_resolved_combinations(self): @@ -220,10 +220,10 @@ class AuthorizationTest(unittest.TestCase): class RevocationTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages2.RevocationTest.""" + """Tests for acme.messages2.RevocationTest.""" def setUp(self): - from letsencrypt.acme.messages2 import Revocation + from acme.messages2 import Revocation self.rev_now = Revocation(authorizations=(), revoke=Revocation.NOW) self.rev_date = Revocation(authorizations=(), revoke=datetime.datetime( 2015, 3, 27, tzinfo=pytz.utc)) @@ -232,7 +232,7 @@ class RevocationTest(unittest.TestCase): 'revoke': '2015-03-27T00:00:00Z'} def test_revoke_decoder(self): - from letsencrypt.acme.messages2 import Revocation + from acme.messages2 import Revocation self.assertEqual(self.rev_now, Revocation.from_json(self.jobj_now)) self.assertEqual(self.rev_date, Revocation.from_json(self.jobj_date)) @@ -241,9 +241,9 @@ class RevocationTest(unittest.TestCase): self.assertEqual(self.jobj_date, self.rev_date.to_partial_json()) def test_from_json_hashable(self): - from letsencrypt.acme.messages2 import Revocation + from acme.messages2 import Revocation hash(Revocation.from_json(self.rev_now.to_json())) if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/messages_test.py b/acme/messages_test.py similarity index 85% rename from letsencrypt/acme/messages_test.py rename to acme/messages_test.py index 56781db18..4e0823085 100644 --- a/letsencrypt/acme/messages_test.py +++ b/acme/messages_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.acme.messages.""" +"""Tests for acme.messages.""" import os import pkg_resources import unittest @@ -6,32 +6,32 @@ import unittest import Crypto.PublicKey.RSA import M2Crypto -from letsencrypt.acme import challenges -from letsencrypt.acme import errors -from letsencrypt.acme import jose -from letsencrypt.acme import other +from acme import challenges +from acme import errors +from acme import jose +from acme import other KEY = jose.HashableRSAKey(Crypto.PublicKey.RSA.importKey( pkg_resources.resource_string( - 'letsencrypt.acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) + 'acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) CERT = jose.ComparableX509(M2Crypto.X509.load_cert( pkg_resources.resource_filename( - 'letsencrypt.client.tests', os.path.join('testdata', 'cert.pem')))) + 'letsencrypt.tests', os.path.join('testdata', 'cert.pem')))) CSR = jose.ComparableX509(M2Crypto.X509.load_request( pkg_resources.resource_filename( - 'letsencrypt.client.tests', os.path.join('testdata', 'csr.pem')))) + 'letsencrypt.tests', os.path.join('testdata', 'csr.pem')))) CSR2 = jose.ComparableX509(M2Crypto.X509.load_request( pkg_resources.resource_filename( - 'letsencrypt.acme.jose', os.path.join('testdata', 'csr2.pem')))) + 'acme.jose', os.path.join('testdata', 'csr2.pem')))) class MessageTest(unittest.TestCase): - """Tests for letsencrypt.acme.messages.Message.""" + """Tests for acme.messages.Message.""" def setUp(self): # pylint: disable=missing-docstring,too-few-public-methods - from letsencrypt.acme.messages import Message + from acme.messages import Message class MockParentMessage(Message): # pylint: disable=abstract-method @@ -69,7 +69,7 @@ class ChallengeTest(unittest.TestCase): ) combinations = ((0, 2), (1, 2)) - from letsencrypt.acme.messages import Challenge + from acme.messages import Challenge self.msg = Challenge( session_id='aefoGaavieG9Wihuk2aufai3aeZ5EeW4', nonce='\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9', @@ -107,14 +107,14 @@ class ChallengeTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg_to) def test_from_json(self): - from letsencrypt.acme.messages import Challenge + from acme.messages import Challenge self.assertEqual(Challenge.from_json(self.jmsg_from), self.msg) def test_json_without_optionals(self): del self.jmsg_from['combinations'] del self.jmsg_to['combinations'] - from letsencrypt.acme.messages import Challenge + from acme.messages import Challenge msg = Challenge.from_json(self.jmsg_from) self.assertEqual(msg.combinations, ()) @@ -124,7 +124,7 @@ class ChallengeTest(unittest.TestCase): class ChallengeRequestTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.messages import ChallengeRequest + from acme.messages import ChallengeRequest self.msg = ChallengeRequest(identifier='example.com') self.jmsg = { @@ -136,7 +136,7 @@ class ChallengeRequestTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg) def test_from_json(self): - from letsencrypt.acme.messages import ChallengeRequest + from acme.messages import ChallengeRequest self.assertEqual(ChallengeRequest.from_json(self.jmsg), self.msg) @@ -145,7 +145,7 @@ class AuthorizationTest(unittest.TestCase): def setUp(self): jwk = jose.JWKRSA(key=KEY.publickey()) - from letsencrypt.acme.messages import Authorization + from acme.messages import Authorization self.msg = Authorization(recovery_token='tok', jwk=jwk, identifier='example.com') @@ -162,7 +162,7 @@ class AuthorizationTest(unittest.TestCase): def test_from_json(self): self.jmsg['jwk'] = self.jmsg['jwk'].to_partial_json() - from letsencrypt.acme.messages import Authorization + from acme.messages import Authorization self.assertEqual(Authorization.from_json(self.jmsg), self.msg) def test_json_without_optionals(self): @@ -170,7 +170,7 @@ class AuthorizationTest(unittest.TestCase): del self.jmsg['identifier'] del self.jmsg['jwk'] - from letsencrypt.acme.messages import Authorization + from acme.messages import Authorization msg = Authorization.from_json(self.jmsg) self.assertTrue(msg.recovery_token is None) @@ -196,7 +196,7 @@ class AuthorizationRequestTest(unittest.TestCase): '\x92\xe9\x96\x11\xc2\xefx\x0bR', nonce='\xab?\x08o\xe6\x81$\x9f\xa1\xc9\x025\x1c\x1b\xa5+') - from letsencrypt.acme.messages import AuthorizationRequest + from acme.messages import AuthorizationRequest self.msg = AuthorizationRequest( session_id='aefoGaavieG9Wihuk2aufai3aeZ5EeW4', nonce='\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9', @@ -226,7 +226,7 @@ class AuthorizationRequestTest(unittest.TestCase): } def test_create(self): - from letsencrypt.acme.messages import AuthorizationRequest + from acme.messages import AuthorizationRequest self.assertEqual(self.msg, AuthorizationRequest.create( name='example.com', key=KEY, responses=self.responses, nonce='\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9', @@ -241,7 +241,7 @@ class AuthorizationRequestTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg_to) def test_from_json(self): - from letsencrypt.acme.messages import AuthorizationRequest + from acme.messages import AuthorizationRequest self.assertEqual( self.msg, AuthorizationRequest.from_json(self.jmsg_from)) @@ -249,7 +249,7 @@ class AuthorizationRequestTest(unittest.TestCase): del self.jmsg_from['contact'] del self.jmsg_to['contact'] - from letsencrypt.acme.messages import AuthorizationRequest + from acme.messages import AuthorizationRequest msg = AuthorizationRequest.from_json(self.jmsg_from) self.assertEqual(msg.contact, ()) @@ -261,7 +261,7 @@ class CertificateTest(unittest.TestCase): def setUp(self): refresh = 'https://example.com/refresh/Dr8eAwTVQfSS/' - from letsencrypt.acme.messages import Certificate + from acme.messages import Certificate self.msg = Certificate( certificate=CERT, chain=(CERT,), refresh=refresh) @@ -279,7 +279,7 @@ class CertificateTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg_to) def test_from_json(self): - from letsencrypt.acme.messages import Certificate + from acme.messages import Certificate self.assertEqual(Certificate.from_json(self.jmsg_from), self.msg) def test_json_without_optionals(self): @@ -288,7 +288,7 @@ class CertificateTest(unittest.TestCase): del self.jmsg_to['chain'] del self.jmsg_to['refresh'] - from letsencrypt.acme.messages import Certificate + from acme.messages import Certificate msg = Certificate.from_json(self.jmsg_from) self.assertEqual(msg.chain, ()) @@ -307,7 +307,7 @@ class CertificateRequestTest(unittest.TestCase): 'k\xfe\xee\xb4\xe4\xc8\x05\x9a\x08\xa7', nonce='\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9') - from letsencrypt.acme.messages import CertificateRequest + from acme.messages import CertificateRequest self.msg = CertificateRequest(csr=CSR, signature=signature) self.jmsg_to = { @@ -319,7 +319,7 @@ class CertificateRequestTest(unittest.TestCase): self.jmsg_from['signature'] = self.jmsg_from['signature'].to_json() def test_create(self): - from letsencrypt.acme.messages import CertificateRequest + from acme.messages import CertificateRequest self.assertEqual(self.msg, CertificateRequest.create( csr=CSR, key=KEY, sig_nonce='\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9')) @@ -331,14 +331,14 @@ class CertificateRequestTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg_to) def test_from_json(self): - from letsencrypt.acme.messages import CertificateRequest + from acme.messages import CertificateRequest self.assertEqual(self.msg, CertificateRequest.from_json(self.jmsg_from)) class DeferTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.messages import Defer + from acme.messages import Defer self.msg = Defer( token='O7-s9MNq1siZHlgrMzi9_A', interval=60, message='Warming up the HSM') @@ -354,14 +354,14 @@ class DeferTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg) def test_from_json(self): - from letsencrypt.acme.messages import Defer + from acme.messages import Defer self.assertEqual(Defer.from_json(self.jmsg), self.msg) def test_json_without_optionals(self): del self.jmsg['interval'] del self.jmsg['message'] - from letsencrypt.acme.messages import Defer + from acme.messages import Defer msg = Defer.from_json(self.jmsg) self.assertTrue(msg.interval is None) @@ -372,7 +372,7 @@ class DeferTest(unittest.TestCase): class ErrorTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.messages import Error + from acme.messages import Error self.msg = Error( error='badCSR', message='RSA keys must be at least 2048 bits long', more_info='https://ca.example.com/documentation/csr-requirements') @@ -388,14 +388,14 @@ class ErrorTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg) def test_from_json(self): - from letsencrypt.acme.messages import Error + from acme.messages import Error self.assertEqual(Error.from_json(self.jmsg), self.msg) def test_json_without_optionals(self): del self.jmsg['message'] del self.jmsg['moreInfo'] - from letsencrypt.acme.messages import Error + from acme.messages import Error msg = Error.from_json(self.jmsg) self.assertTrue(msg.message is None) @@ -406,7 +406,7 @@ class ErrorTest(unittest.TestCase): class RevocationTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.messages import Revocation + from acme.messages import Revocation self.msg = Revocation() self.jmsg = {'type': 'revocation'} @@ -414,7 +414,7 @@ class RevocationTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg) def test_from_json(self): - from letsencrypt.acme.messages import Revocation + from acme.messages import Revocation self.assertEqual(Revocation.from_json(self.jmsg), self.msg) @@ -431,7 +431,7 @@ class RevocationRequestTest(unittest.TestCase): 's\xd9\xd0\xe7', nonce=self.sig_nonce) - from letsencrypt.acme.messages import RevocationRequest + from acme.messages import RevocationRequest self.msg = RevocationRequest(certificate=CERT, signature=signature) self.jmsg_to = { @@ -443,7 +443,7 @@ class RevocationRequestTest(unittest.TestCase): self.jmsg_from['signature'] = self.jmsg_from['signature'].to_json() def test_create(self): - from letsencrypt.acme.messages import RevocationRequest + from acme.messages import RevocationRequest self.assertEqual(self.msg, RevocationRequest.create( certificate=CERT, key=KEY, sig_nonce=self.sig_nonce)) @@ -454,14 +454,14 @@ class RevocationRequestTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg_to) def test_from_json(self): - from letsencrypt.acme.messages import RevocationRequest + from acme.messages import RevocationRequest self.assertEqual(self.msg, RevocationRequest.from_json(self.jmsg_from)) class StatusRequestTest(unittest.TestCase): def setUp(self): - from letsencrypt.acme.messages import StatusRequest + from acme.messages import StatusRequest self.msg = StatusRequest(token=u'O7-s9MNq1siZHlgrMzi9_A') self.jmsg = { 'type': 'statusRequest', @@ -472,9 +472,9 @@ class StatusRequestTest(unittest.TestCase): self.assertEqual(self.msg.to_partial_json(), self.jmsg) def test_from_json(self): - from letsencrypt.acme.messages import StatusRequest + from acme.messages import StatusRequest self.assertEqual(StatusRequest.from_json(self.jmsg), self.msg) if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/other.py b/acme/other.py similarity index 98% rename from letsencrypt/acme/other.py rename to acme/other.py index 99a4ec551..d7eb49156 100644 --- a/letsencrypt/acme/other.py +++ b/acme/other.py @@ -5,7 +5,7 @@ import logging import Crypto.Random import Crypto.PublicKey.RSA -from letsencrypt.acme import jose +from acme import jose class Signature(jose.JSONObjectWithFields): diff --git a/letsencrypt/acme/other_test.py b/acme/other_test.py similarity index 87% rename from letsencrypt/acme/other_test.py rename to acme/other_test.py index eefcb2fc5..6da7e0de7 100644 --- a/letsencrypt/acme/other_test.py +++ b/acme/other_test.py @@ -1,21 +1,21 @@ -"""Tests for letsencrypt.acme.sig.""" +"""Tests for acme.sig.""" import os import pkg_resources import unittest import Crypto.PublicKey.RSA -from letsencrypt.acme import jose +from acme import jose KEY = jose.HashableRSAKey(Crypto.PublicKey.RSA.importKey( pkg_resources.resource_string( - 'letsencrypt.acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) + 'acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) class SignatureTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes - """Tests for letsencrypt.acme.sig.Signature.""" + """Tests for acme.sig.Signature.""" def setUp(self): self.msg = 'message' @@ -45,7 +45,7 @@ class SignatureTest(unittest.TestCase): 'sig': b64sig, } - from letsencrypt.acme.other import Signature + from acme.other import Signature self.signature = Signature( alg=self.alg, sig=self.sig, nonce=self.nonce, jwk=self.jwk) @@ -63,7 +63,7 @@ class SignatureTest(unittest.TestCase): @classmethod def _from_msg(cls, *args, **kwargs): - from letsencrypt.acme.other import Signature + from acme.other import Signature return Signature.from_msg(*args, **kwargs) def test_create_from_msg(self): @@ -80,12 +80,12 @@ class SignatureTest(unittest.TestCase): self.assertEqual(self.signature.to_partial_json(), self.jsig_to) def test_from_json(self): - from letsencrypt.acme.other import Signature + from acme.other import Signature self.assertEqual( self.signature, Signature.from_json(self.jsig_from)) def test_from_json_non_schema_errors(self): - from letsencrypt.acme.other import Signature + from acme.other import Signature jwk = self.jwk.to_partial_json() self.assertRaises( jose.DeserializationError, Signature.from_json, { @@ -96,4 +96,4 @@ class SignatureTest(unittest.TestCase): if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/acme/schemata/authorization.json b/acme/schemata/authorization.json similarity index 88% rename from letsencrypt/acme/schemata/authorization.json rename to acme/schemata/authorization.json index 742a9c0d5..122f263e1 100644 --- a/letsencrypt/acme/schemata/authorization.json +++ b/acme/schemata/authorization.json @@ -15,7 +15,7 @@ "type": "string" }, "jwk": { - "$ref": "file:letsencrypt/acme/schemata/jwk.json" + "$ref": "file:acme/schemata/jwk.json" } } } diff --git a/letsencrypt/acme/schemata/authorizationRequest.json b/acme/schemata/authorizationRequest.json similarity index 85% rename from letsencrypt/acme/schemata/authorizationRequest.json rename to acme/schemata/authorizationRequest.json index ee22808bc..2d4371cb8 100644 --- a/letsencrypt/acme/schemata/authorizationRequest.json +++ b/acme/schemata/authorizationRequest.json @@ -15,14 +15,14 @@ "type": "string" }, "signature" : { - "$ref": "file:letsencrypt/acme/schemata/signature.json" + "$ref": "file:acme/schemata/signature.json" }, "responses": { "type": "array", "minItems": 1, "items": { "anyOf": [ - { "$ref": "file:letsencrypt/acme/schemata/responseobject.json" }, + { "$ref": "file:acme/schemata/responseobject.json" }, { "type": "null" } ] } diff --git a/letsencrypt/acme/schemata/certificate.json b/acme/schemata/certificate.json similarity index 100% rename from letsencrypt/acme/schemata/certificate.json rename to acme/schemata/certificate.json diff --git a/letsencrypt/acme/schemata/certificateRequest.json b/acme/schemata/certificateRequest.json similarity index 88% rename from letsencrypt/acme/schemata/certificateRequest.json rename to acme/schemata/certificateRequest.json index c75e93bd9..ef3e18f98 100644 --- a/letsencrypt/acme/schemata/certificateRequest.json +++ b/acme/schemata/certificateRequest.json @@ -13,7 +13,7 @@ "pattern": "^[-_=0-9A-Za-z]+$" }, "signature" : { - "$ref": "file:letsencrypt/acme/schemata/signature.json" + "$ref": "file:acme/schemata/signature.json" } } } diff --git a/letsencrypt/acme/schemata/challenge.json b/acme/schemata/challenge.json similarity index 91% rename from letsencrypt/acme/schemata/challenge.json rename to acme/schemata/challenge.json index b4b2a5205..978fcd4c4 100644 --- a/letsencrypt/acme/schemata/challenge.json +++ b/acme/schemata/challenge.json @@ -18,7 +18,7 @@ "type": "array", "minItems": 1, "items": { - "$ref": "file:letsencrypt/acme/schemata/challengeobject.json" + "$ref": "file:acme/schemata/challengeobject.json" } }, "combinations": { diff --git a/letsencrypt/acme/schemata/challengeRequest.json b/acme/schemata/challengeRequest.json similarity index 100% rename from letsencrypt/acme/schemata/challengeRequest.json rename to acme/schemata/challengeRequest.json diff --git a/letsencrypt/acme/schemata/challengeobject.json b/acme/schemata/challengeobject.json similarity index 100% rename from letsencrypt/acme/schemata/challengeobject.json rename to acme/schemata/challengeobject.json diff --git a/letsencrypt/acme/schemata/defer.json b/acme/schemata/defer.json similarity index 100% rename from letsencrypt/acme/schemata/defer.json rename to acme/schemata/defer.json diff --git a/letsencrypt/acme/schemata/error.json b/acme/schemata/error.json similarity index 100% rename from letsencrypt/acme/schemata/error.json rename to acme/schemata/error.json diff --git a/letsencrypt/acme/schemata/jwk.json b/acme/schemata/jwk.json similarity index 100% rename from letsencrypt/acme/schemata/jwk.json rename to acme/schemata/jwk.json diff --git a/letsencrypt/acme/schemata/responseobject.json b/acme/schemata/responseobject.json similarity index 96% rename from letsencrypt/acme/schemata/responseobject.json rename to acme/schemata/responseobject.json index c6d6c9c1b..5ca6babf1 100644 --- a/letsencrypt/acme/schemata/responseobject.json +++ b/acme/schemata/responseobject.json @@ -59,7 +59,7 @@ "pattern": "^[-_=0-9A-Za-z]+$" }, "signature": { - "$ref": "file:letsencrypt/acme/schemata/signature.json" + "$ref": "file:acme/schemata/signature.json" } } }, diff --git a/letsencrypt/acme/schemata/revocation.json b/acme/schemata/revocation.json similarity index 100% rename from letsencrypt/acme/schemata/revocation.json rename to acme/schemata/revocation.json diff --git a/letsencrypt/acme/schemata/revocationRequest.json b/acme/schemata/revocationRequest.json similarity index 87% rename from letsencrypt/acme/schemata/revocationRequest.json rename to acme/schemata/revocationRequest.json index 5eb604fd9..7559d0ee0 100644 --- a/letsencrypt/acme/schemata/revocationRequest.json +++ b/acme/schemata/revocationRequest.json @@ -12,7 +12,7 @@ "type" : "string" }, "signature" : { - "$ref": "file:letsencrypt/acme/schemata/signature.json" + "$ref": "file:acme/schemata/signature.json" } } } diff --git a/letsencrypt/acme/schemata/signature.json b/acme/schemata/signature.json similarity index 100% rename from letsencrypt/acme/schemata/signature.json rename to acme/schemata/signature.json diff --git a/letsencrypt/acme/schemata/statusRequest.json b/acme/schemata/statusRequest.json similarity index 100% rename from letsencrypt/acme/schemata/statusRequest.json rename to acme/schemata/statusRequest.json diff --git a/letsencrypt/acme/util.py b/acme/util.py similarity index 100% rename from letsencrypt/acme/util.py rename to acme/util.py diff --git a/docs/api/account.rst b/docs/api/account.rst new file mode 100644 index 000000000..16c2061a8 --- /dev/null +++ b/docs/api/account.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.account` +-------------------------- + +.. automodule:: letsencrypt.account + :members: diff --git a/docs/api/achallenges.rst b/docs/api/achallenges.rst new file mode 100644 index 000000000..09cec1702 --- /dev/null +++ b/docs/api/achallenges.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.achallenges` +------------------------------ + +.. automodule:: letsencrypt.achallenges + :members: diff --git a/docs/api/acme/index.rst b/docs/api/acme/index.rst deleted file mode 100644 index 20206183a..000000000 --- a/docs/api/acme/index.rst +++ /dev/null @@ -1,61 +0,0 @@ -:mod:`letsencrypt.acme` -======================= - -.. contents:: - -.. automodule:: letsencrypt.acme - :members: - - -Messages --------- - -v00 -~~~ - -.. automodule:: letsencrypt.acme.messages - :members: - -v02 -~~~ - -.. automodule:: letsencrypt.acme.messages2 - :members: - - -Challenges ----------- - -.. automodule:: letsencrypt.acme.challenges - :members: - - -Other ACME objects ------------------- - -.. automodule:: letsencrypt.acme.other - :members: - - -Fields ------- - -.. automodule:: letsencrypt.acme.fields - :members: - - -Errors ------- - -.. automodule:: letsencrypt.acme.errors - :members: - - - :members: - - -Utilities ---------- - -.. automodule:: letsencrypt.acme.util - :members: diff --git a/docs/api/acme/jose.rst b/docs/api/acme/jose.rst deleted file mode 100644 index 9a64d33d3..000000000 --- a/docs/api/acme/jose.rst +++ /dev/null @@ -1,67 +0,0 @@ -:mod:`letsencrypt.acme.jose` -============================ - -.. contents:: - -.. automodule:: letsencrypt.acme.jose - :members: - - -JSON Web Algorithms -------------------- - -.. automodule:: letsencrypt.acme.jose.jwa - :members: - - -JSON Web Key ------------- - -.. automodule:: letsencrypt.acme.jose.jwk - :members: - - -JSON Web Signature ------------------- - -.. automodule:: letsencrypt.acme.jose.jws - :members: - - -Implementation details ----------------------- - - -Interfaces -~~~~~~~~~~ - -.. automodule:: letsencrypt.acme.jose.interfaces - :members: - - -Errors -~~~~~~ - -.. automodule:: letsencrypt.acme.jose.errors - :members: - - -JSON utilities -~~~~~~~~~~~~~~ - -.. automodule:: letsencrypt.acme.jose.json_util - :members: - - -JOSE Base64 -~~~~~~~~~~~ - -.. automodule:: letsencrypt.acme.jose.b64 - :members: - - -Utilities -~~~~~~~~~ - -.. automodule:: letsencrypt.acme.jose.util - :members: diff --git a/docs/api/augeas_configurator.rst b/docs/api/augeas_configurator.rst new file mode 100644 index 000000000..402eee797 --- /dev/null +++ b/docs/api/augeas_configurator.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.augeas_configurator` +-------------------------------------- + +.. automodule:: letsencrypt.augeas_configurator + :members: diff --git a/docs/api/auth_handler.rst b/docs/api/auth_handler.rst new file mode 100644 index 000000000..3b168faf8 --- /dev/null +++ b/docs/api/auth_handler.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.auth_handler` +------------------------------- + +.. automodule:: letsencrypt.auth_handler + :members: diff --git a/docs/api/client/index.rst b/docs/api/client.rst similarity index 100% rename from docs/api/client/index.rst rename to docs/api/client.rst diff --git a/docs/api/client/account.rst b/docs/api/client/account.rst deleted file mode 100644 index 6fad87556..000000000 --- a/docs/api/client/account.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.account` ---------------------------------- - -.. automodule:: letsencrypt.client.account - :members: diff --git a/docs/api/client/achallenges.rst b/docs/api/client/achallenges.rst deleted file mode 100644 index 46a13ee8b..000000000 --- a/docs/api/client/achallenges.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.achallenges` -------------------------------------- - -.. automodule:: letsencrypt.client.achallenges - :members: diff --git a/docs/api/client/augeas_configurator.rst b/docs/api/client/augeas_configurator.rst deleted file mode 100644 index 6b8ca4d83..000000000 --- a/docs/api/client/augeas_configurator.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.augeas_configurator` ---------------------------------------------- - -.. automodule:: letsencrypt.client.augeas_configurator - :members: diff --git a/docs/api/client/auth_handler.rst b/docs/api/client/auth_handler.rst deleted file mode 100644 index b52006993..000000000 --- a/docs/api/client/auth_handler.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.auth_handler` --------------------------------------- - -.. automodule:: letsencrypt.client.auth_handler - :members: diff --git a/docs/api/client/client.rst b/docs/api/client/client.rst deleted file mode 100644 index ac68c2764..000000000 --- a/docs/api/client/client.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.client` --------------------------------- - -.. automodule:: letsencrypt.client.client - :members: diff --git a/docs/api/client/configuration.rst b/docs/api/client/configuration.rst deleted file mode 100644 index 0bec61480..000000000 --- a/docs/api/client/configuration.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.configuration` ---------------------------------------- - -.. automodule:: letsencrypt.client.configuration - :members: diff --git a/docs/api/client/constants.rst b/docs/api/client/constants.rst deleted file mode 100644 index c901e13c2..000000000 --- a/docs/api/client/constants.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.constants` ------------------------------------ - -.. automodule:: letsencrypt.client.constants - :members: diff --git a/docs/api/client/continuity_auth.rst b/docs/api/client/continuity_auth.rst deleted file mode 100644 index 29f6a3ffb..000000000 --- a/docs/api/client/continuity_auth.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.continuity_auth` ------------------------------------------ - -.. automodule:: letsencrypt.client.continuity_auth - :members: diff --git a/docs/api/client/crypto_util.rst b/docs/api/client/crypto_util.rst deleted file mode 100644 index 1b47cd151..000000000 --- a/docs/api/client/crypto_util.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.crypto_util` -------------------------------------- - -.. automodule:: letsencrypt.client.crypto_util - :members: diff --git a/docs/api/client/display.rst b/docs/api/client/display.rst deleted file mode 100644 index 59f97d18e..000000000 --- a/docs/api/client/display.rst +++ /dev/null @@ -1,29 +0,0 @@ -:mod:`letsencrypt.client.display` ---------------------------------- - -.. automodule:: letsencrypt.client.display - :members: - -:mod:`letsencrypt.client.display.util` -====================================== - -.. automodule:: letsencrypt.client.display.util - :members: - -:mod:`letsencrypt.client.display.ops` -===================================== - -.. automodule:: letsencrypt.client.display.ops - :members: - -:mod:`letsencrypt.client.display.enhancements` -============================================== - -.. automodule:: letsencrypt.client.display.enhancements - :members: - -:mod:`letsencrypt.client.display.revocation` -============================================ - -.. automodule:: letsencrypt.client.display.revocation - :members: diff --git a/docs/api/client/errors.rst b/docs/api/client/errors.rst deleted file mode 100644 index 40f0ae6c7..000000000 --- a/docs/api/client/errors.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.errors` --------------------------------- - -.. automodule:: letsencrypt.client.errors - :members: diff --git a/docs/api/client/interfaces.rst b/docs/api/client/interfaces.rst deleted file mode 100644 index e14daed7f..000000000 --- a/docs/api/client/interfaces.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.interfaces` ------------------------------------- - -.. automodule:: letsencrypt.client.interfaces - :members: diff --git a/docs/api/client/le_util.rst b/docs/api/client/le_util.rst deleted file mode 100644 index 537e90546..000000000 --- a/docs/api/client/le_util.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.le_util` ---------------------------------- - -.. automodule:: letsencrypt.client.le_util - :members: diff --git a/docs/api/client/log.rst b/docs/api/client/log.rst deleted file mode 100644 index 2ccad738b..000000000 --- a/docs/api/client/log.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.log` ------------------------------ - -.. automodule:: letsencrypt.client.log - :members: diff --git a/docs/api/client/network.rst b/docs/api/client/network.rst deleted file mode 100644 index 7b4ec633a..000000000 --- a/docs/api/client/network.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.network` ---------------------------------- - -.. automodule:: letsencrypt.client.network - :members: diff --git a/docs/api/client/network2.rst b/docs/api/client/network2.rst deleted file mode 100644 index b05017551..000000000 --- a/docs/api/client/network2.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.network2` ----------------------------------- - -.. automodule:: letsencrypt.client.network2 - :members: diff --git a/docs/api/client/plugins/apache.rst b/docs/api/client/plugins/apache.rst deleted file mode 100644 index 6e6e6c462..000000000 --- a/docs/api/client/plugins/apache.rst +++ /dev/null @@ -1,29 +0,0 @@ -:mod:`letsencrypt.client.plugins.apache` ----------------------------------------- - -.. automodule:: letsencrypt.client.plugins.apache - :members: - -:mod:`letsencrypt.client.plugins.apache.configurator` -===================================================== - -.. automodule:: letsencrypt.client.plugins.apache.configurator - :members: - -:mod:`letsencrypt.client.plugins.apache.dvsni` -============================================== - -.. automodule:: letsencrypt.client.plugins.apache.dvsni - :members: - -:mod:`letsencrypt.client.plugins.apache.obj` -============================================ - -.. automodule:: letsencrypt.client.plugins.apache.obj - :members: - -:mod:`letsencrypt.client.plugins.apache.parser` -=============================================== - -.. automodule:: letsencrypt.client.plugins.apache.parser - :members: diff --git a/docs/api/client/plugins/common.rst b/docs/api/client/plugins/common.rst deleted file mode 100644 index 9ee3e6b3e..000000000 --- a/docs/api/client/plugins/common.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.plugins.common` ----------------------------------------- - -.. automodule:: letsencrypt.client.plugins.common - :members: diff --git a/docs/api/client/plugins/disco.rst b/docs/api/client/plugins/disco.rst deleted file mode 100644 index 2b877b654..000000000 --- a/docs/api/client/plugins/disco.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.plugins.disco` ---------------------------------------- - -.. automodule:: letsencrypt.client.plugins.disco - :members: diff --git a/docs/api/client/plugins/nginx.rst b/docs/api/client/plugins/nginx.rst deleted file mode 100644 index cd64846bf..000000000 --- a/docs/api/client/plugins/nginx.rst +++ /dev/null @@ -1,35 +0,0 @@ -:mod:`letsencrypt.client.plugins.nginx` ----------------------------------------- - -.. automodule:: letsencrypt.client.plugins.nginx - :members: - -:mod:`letsencrypt.client.plugins.nginx.configurator` -===================================================== - -.. automodule:: letsencrypt.client.plugins.nginx.configurator - :members: - -:mod:`letsencrypt.client.plugins.nginx.dvsni` -============================================== - -.. automodule:: letsencrypt.client.plugins.nginx.dvsni - :members: - -:mod:`letsencrypt.client.plugins.nginx.obj` -============================================ - -.. automodule:: letsencrypt.client.plugins.nginx.obj - :members: - -:mod:`letsencrypt.client.plugins.nginx.parser` -=============================================== - -.. automodule:: letsencrypt.client.plugins.nginx.parser - :members: - -:mod:`letsencrypt.client.plugins.nginx.nginxparser` -==================================================== - -.. automodule:: letsencrypt.client.plugins.nginx.nginxparser - :members: diff --git a/docs/api/client/plugins/standalone.rst b/docs/api/client/plugins/standalone.rst deleted file mode 100644 index 44cf4b8ca..000000000 --- a/docs/api/client/plugins/standalone.rst +++ /dev/null @@ -1,11 +0,0 @@ -:mod:`letsencrypt.client.plugins.standalone` --------------------------------------------- - -.. automodule:: letsencrypt.client.plugins.standalone - :members: - -:mod:`letsencrypt.client.plugins.standalone.authenticator` -========================================================== - -.. automodule:: letsencrypt.client.plugins.standalone.authenticator - :members: diff --git a/docs/api/client/proof_of_possession.rst b/docs/api/client/proof_of_possession.rst new file mode 100644 index 000000000..9f1ea0793 --- /dev/null +++ b/docs/api/client/proof_of_possession.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.client.proof_of_possession` +-------------------------------------------------- + +.. automodule:: letsencrypt.client.proof_of_possession + :members: diff --git a/docs/api/client/recovery_token.rst b/docs/api/client/recovery_token.rst deleted file mode 100644 index cc37e036d..000000000 --- a/docs/api/client/recovery_token.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.recovery_token` --------------------------------------------------- - -.. automodule:: letsencrypt.client.recovery_token - :members: diff --git a/docs/api/client/reverter.rst b/docs/api/client/reverter.rst deleted file mode 100644 index ad2e41437..000000000 --- a/docs/api/client/reverter.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.reverter` ----------------------------------- - -.. automodule:: letsencrypt.client.reverter - :members: diff --git a/docs/api/client/revoker.rst b/docs/api/client/revoker.rst deleted file mode 100644 index e0a7db533..000000000 --- a/docs/api/client/revoker.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.client.revoker` ---------------------------------- - -.. automodule:: letsencrypt.client.revoker - :members: diff --git a/docs/api/configuration.rst b/docs/api/configuration.rst new file mode 100644 index 000000000..e92392b99 --- /dev/null +++ b/docs/api/configuration.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.configuration` +-------------------------------- + +.. automodule:: letsencrypt.configuration + :members: diff --git a/docs/api/constants.rst b/docs/api/constants.rst new file mode 100644 index 000000000..3a2815b5e --- /dev/null +++ b/docs/api/constants.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.constants` +----------------------------------- + +.. automodule:: letsencrypt.constants + :members: diff --git a/docs/api/continuity_auth.rst b/docs/api/continuity_auth.rst new file mode 100644 index 000000000..82869e6f4 --- /dev/null +++ b/docs/api/continuity_auth.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.continuity_auth` +---------------------------------- + +.. automodule:: letsencrypt.continuity_auth + :members: diff --git a/docs/api/crypto_util.rst b/docs/api/crypto_util.rst new file mode 100644 index 000000000..5d4c77538 --- /dev/null +++ b/docs/api/crypto_util.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.crypto_util` +------------------------------ + +.. automodule:: letsencrypt.crypto_util + :members: diff --git a/docs/api/display.rst b/docs/api/display.rst new file mode 100644 index 000000000..b79ef25d7 --- /dev/null +++ b/docs/api/display.rst @@ -0,0 +1,29 @@ +:mod:`letsencrypt.display` +-------------------------- + +.. automodule:: letsencrypt.display + :members: + +:mod:`letsencrypt.display.util` +=============================== + +.. automodule:: letsencrypt.display.util + :members: + +:mod:`letsencrypt.display.ops` +============================== + +.. automodule:: letsencrypt.display.ops + :members: + +:mod:`letsencrypt.display.enhancements` +======================================= + +.. automodule:: letsencrypt.display.enhancements + :members: + +:mod:`letsencrypt.display.revocation` +===================================== + +.. automodule:: letsencrypt.display.revocation + :members: diff --git a/docs/api/errors.rst b/docs/api/errors.rst new file mode 100644 index 000000000..1ad13235c --- /dev/null +++ b/docs/api/errors.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.errors` +------------------------- + +.. automodule:: letsencrypt.errors + :members: diff --git a/docs/api/index.rst b/docs/api/index.rst new file mode 100644 index 000000000..a2475eeae --- /dev/null +++ b/docs/api/index.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt` +------------------ + +.. automodule:: letsencrypt + :members: diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst new file mode 100644 index 000000000..00b0a1e50 --- /dev/null +++ b/docs/api/interfaces.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.interfaces` +----------------------------- + +.. automodule:: letsencrypt.interfaces + :members: diff --git a/docs/api/le_util.rst b/docs/api/le_util.rst new file mode 100644 index 000000000..8c6b717cf --- /dev/null +++ b/docs/api/le_util.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.le_util` +-------------------------- + +.. automodule:: letsencrypt.le_util + :members: diff --git a/docs/api/log.rst b/docs/api/log.rst new file mode 100644 index 000000000..f41c6c4b1 --- /dev/null +++ b/docs/api/log.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.log` +---------------------- + +.. automodule:: letsencrypt.log + :members: diff --git a/docs/api/network.rst b/docs/api/network.rst new file mode 100644 index 000000000..0359379dd --- /dev/null +++ b/docs/api/network.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.network` +-------------------------- + +.. automodule:: letsencrypt.network + :members: diff --git a/docs/api/network2.rst b/docs/api/network2.rst new file mode 100644 index 000000000..a73308e1b --- /dev/null +++ b/docs/api/network2.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.network2` +--------------------------- + +.. automodule:: letsencrypt.network2 + :members: diff --git a/docs/api/plugins/common.rst b/docs/api/plugins/common.rst new file mode 100644 index 000000000..ca55ba8fb --- /dev/null +++ b/docs/api/plugins/common.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.plugins.common` +--------------------------------- + +.. automodule:: letsencrypt.plugins.common + :members: diff --git a/docs/api/plugins/disco.rst b/docs/api/plugins/disco.rst new file mode 100644 index 000000000..7bf2b76b4 --- /dev/null +++ b/docs/api/plugins/disco.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.plugins.disco` +-------------------------------- + +.. automodule:: letsencrypt.plugins.disco + :members: diff --git a/docs/api/plugins/standalone.rst b/docs/api/plugins/standalone.rst new file mode 100644 index 000000000..99e5ea2f8 --- /dev/null +++ b/docs/api/plugins/standalone.rst @@ -0,0 +1,11 @@ +:mod:`letsencrypt.plugins.standalone` +------------------------------------- + +.. automodule:: letsencrypt.plugins.standalone + :members: + +:mod:`letsencrypt.plugins.standalone.authenticator` +=================================================== + +.. automodule:: letsencrypt.plugins.standalone.authenticator + :members: diff --git a/docs/api/recovery_token.rst b/docs/api/recovery_token.rst new file mode 100644 index 000000000..774aa4b3c --- /dev/null +++ b/docs/api/recovery_token.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.recovery_token` +-------------------------------------------------- + +.. automodule:: letsencrypt.recovery_token + :members: diff --git a/docs/api/reverter.rst b/docs/api/reverter.rst new file mode 100644 index 000000000..4c220124f --- /dev/null +++ b/docs/api/reverter.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.reverter` +--------------------------- + +.. automodule:: letsencrypt.reverter + :members: diff --git a/docs/api/revoker.rst b/docs/api/revoker.rst new file mode 100644 index 000000000..a482a138e --- /dev/null +++ b/docs/api/revoker.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt.revoker` +-------------------------- + +.. automodule:: letsencrypt.revoker + :members: diff --git a/docs/contributing.rst b/docs/contributing.rst index d5088705b..da28686a2 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -67,12 +67,10 @@ Support for other Linux distributions coming soon. Code components and layout ========================== -letsencrypt/acme +acme contains all protocol specific code -letsencrypt/client +letsencrypt all client code -letsencrypt/scripts - just the starting point of the code, main.py Plugin-architecture @@ -84,14 +82,14 @@ The interfaces available for plugins to implement are defined in `interfaces.py`_. The most common kind of plugin is a "Configurator", which is likely to -implement the `~letsencrypt.client.interfaces.IAuthenticator` and -`~letsencrypt.client.interfaces.IInstaller` interfaces (though some +implement the `~letsencrypt.interfaces.IAuthenticator` and +`~letsencrypt.interfaces.IInstaller` interfaces (though some Configurators may implement just one of those). -There are also `~letsencrypt.client.interfaces.IDisplay` plugins, +There are also `~letsencrypt.interfaces.IDisplay` plugins, which implement bindings to alternative UI libraries. -.. _interfaces.py: https://github.com/letsencrypt/lets-encrypt-preview/blob/master/letsencrypt/client/interfaces.py +.. _interfaces.py: https://github.com/letsencrypt/lets-encrypt-preview/blob/master/letsencrypt/interfaces.py Authenticators @@ -138,7 +136,7 @@ Installer Development --------------------- There are a few existing classes that may be beneficial while -developing a new `~letsencrypt.client.interfaces.IInstaller`. +developing a new `~letsencrypt.interfaces.IInstaller`. Installers aimed to reconfigure UNIX servers may use Augeas for configuration parsing and can inherit from `~.AugeasConfigurator` class to handle much of the interface. Installers that are unable to use @@ -150,7 +148,7 @@ Display ~~~~~~~ We currently offer a pythondialog and "text" mode for displays. Display -plugins implement the `~letsencrypt.client.interfaces.IDisplay` +plugins implement the `~letsencrypt.interfaces.IDisplay` interface. diff --git a/docs/index.rst b/docs/index.rst index 72be096f9..b076b45c6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ Welcome to the Let's Encrypt client documentation! :maxdepth: 1 api + pkgs Indices and tables diff --git a/docs/pkgs.rst b/docs/pkgs.rst new file mode 100644 index 000000000..8119ffc7e --- /dev/null +++ b/docs/pkgs.rst @@ -0,0 +1,14 @@ +======== +Packages +======== + +.. note:: It is planned to distribute `acme` and plugins separately as + described in `#358`_. For the time being those packages are bundled + together into a single repo, and single documentation. + +.. _`#358`: https://github.com/letsencrypt/lets-encrypt-preview/issues/358 + +.. toctree:: + :glob: + + pkgs/** diff --git a/docs/pkgs/acme/index.rst b/docs/pkgs/acme/index.rst new file mode 100644 index 000000000..9cca3b795 --- /dev/null +++ b/docs/pkgs/acme/index.rst @@ -0,0 +1,61 @@ +:mod:`acme` +=========== + +.. contents:: + +.. automodule:: acme + :members: + + +Messages +-------- + +v00 +~~~ + +.. automodule:: acme.messages + :members: + +v02 +~~~ + +.. automodule:: acme.messages2 + :members: + + +Challenges +---------- + +.. automodule:: acme.challenges + :members: + + +Other ACME objects +------------------ + +.. automodule:: acme.other + :members: + + +Fields +------ + +.. automodule:: acme.fields + :members: + + +Errors +------ + +.. automodule:: acme.errors + :members: + + + :members: + + +Utilities +--------- + +.. automodule:: acme.util + :members: diff --git a/docs/pkgs/acme/jose.rst b/docs/pkgs/acme/jose.rst new file mode 100644 index 000000000..fa3a0e9bb --- /dev/null +++ b/docs/pkgs/acme/jose.rst @@ -0,0 +1,67 @@ +:mod:`acme.jose` +================ + +.. contents:: + +.. automodule:: acme.jose + :members: + + +JSON Web Algorithms +------------------- + +.. automodule:: acme.jose.jwa + :members: + + +JSON Web Key +------------ + +.. automodule:: acme.jose.jwk + :members: + + +JSON Web Signature +------------------ + +.. automodule:: acme.jose.jws + :members: + + +Implementation details +---------------------- + + +Interfaces +~~~~~~~~~~ + +.. automodule:: acme.jose.interfaces + :members: + + +Errors +~~~~~~ + +.. automodule:: acme.jose.errors + :members: + + +JSON utilities +~~~~~~~~~~~~~~ + +.. automodule:: acme.jose.json_util + :members: + + +JOSE Base64 +~~~~~~~~~~~ + +.. automodule:: acme.jose.b64 + :members: + + +Utilities +~~~~~~~~~ + +.. automodule:: acme.jose.util + :members: diff --git a/docs/pkgs/letsencrypt_apache.rst b/docs/pkgs/letsencrypt_apache.rst new file mode 100644 index 000000000..44966cca6 --- /dev/null +++ b/docs/pkgs/letsencrypt_apache.rst @@ -0,0 +1,29 @@ +:mod:`letsencrypt_apache` +------------------------- + +.. automodule:: letsencrypt_apache + :members: + +:mod:`letsencrypt_apache.configurator` +====================================== + +.. automodule:: letsencrypt_apache.configurator + :members: + +:mod:`letsencrypt_apache.dvsni` +=============================== + +.. automodule:: letsencrypt_apache.dvsni + :members: + +:mod:`letsencrypt_apache.obj` +============================= + +.. automodule:: letsencrypt_apache.obj + :members: + +:mod:`letsencrypt_apache.parser` +================================ + +.. automodule:: letsencrypt_apache.parser + :members: diff --git a/docs/pkgs/letsencrypt_nginx.rst b/docs/pkgs/letsencrypt_nginx.rst new file mode 100644 index 000000000..03114b685 --- /dev/null +++ b/docs/pkgs/letsencrypt_nginx.rst @@ -0,0 +1,35 @@ +:mod:`letsencrypt_nginx` +------------------------ + +.. automodule:: letsencrypt_nginx + :members: + +:mod:`letsencrypt_nginx.configurator` +===================================== + +.. automodule:: letsencrypt_nginx.configurator + :members: + +:mod:`letsencrypt_nginx.dvsni` +============================== + +.. automodule:: letsencrypt_nginx.dvsni + :members: + +:mod:`letsencrypt_nginx.obj` +============================ + +.. automodule:: letsencrypt_nginx.obj + :members: + +:mod:`letsencrypt_nginx.parser` +=============================== + +.. automodule:: letsencrypt_nginx.parser + :members: + +:mod:`letsencrypt_nginx.nginxparser` +==================================== + +.. automodule:: letsencrypt_nginx.nginxparser + :members: diff --git a/docs/plugins.rst b/docs/plugins.rst index 0451bfe3f..c4df1180a 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -5,8 +5,8 @@ Plugins Let's Encrypt client supports dynamic discovery of plugins through the `setuptools entry points`_. This way you can, for example, create a custom implementation of -`~letsencrypt.client.interfaces.IAuthenticator` or the -'~letsencrypt.client.interfaces.IInstaller' without having to +`~letsencrypt.interfaces.IAuthenticator` or the +'~letsencrypt.interfaces.IInstaller' without having to merge it with the core upstream source code. An example is provided in ``examples/plugins/`` directory. diff --git a/examples/plugins/letsencrypt_example_plugins.py b/examples/plugins/letsencrypt_example_plugins.py index 7c6d1311c..a364ae905 100644 --- a/examples/plugins/letsencrypt_example_plugins.py +++ b/examples/plugins/letsencrypt_example_plugins.py @@ -1,12 +1,12 @@ """Example Let's Encrypt plugins. -For full examples, see `letsencrypt.client.plugins`. +For full examples, see `letsencrypt.plugins`. """ import zope.interface -from letsencrypt.client import interfaces -from letsencrypt.client.plugins import common +from letsencrypt import interfaces +from letsencrypt.plugins import common class Authenticator(common.Plugin): diff --git a/examples/restified.py b/examples/restified.py index 651ecccd1..c0252c1eb 100644 --- a/examples/restified.py +++ b/examples/restified.py @@ -4,10 +4,10 @@ import pkg_resources import M2Crypto -from letsencrypt.acme import messages2 -from letsencrypt.acme import jose +from acme import messages2 +from acme import jose -from letsencrypt.client import network2 +from letsencrypt import network2 logger = logging.getLogger() @@ -16,7 +16,7 @@ logger.setLevel(logging.DEBUG) NEW_REG_URL = 'https://www.letsencrypt-demo.org/acme/new-reg' key = jose.JWKRSA.load(pkg_resources.resource_string( - 'letsencrypt.acme.jose', os.path.join('testdata', 'rsa512_key.pem'))) + 'acme.jose', os.path.join('testdata', 'rsa512_key.pem'))) net = network2.Network(NEW_REG_URL, key) regr = net.register(contact=( @@ -29,13 +29,13 @@ logging.debug(regr) authzr = net.request_challenges( identifier=messages2.Identifier( typ=messages2.IDENTIFIER_FQDN, value='example1.com'), - regr=regr) + new_authzr_uri=regr.new_authzr_uri) logging.debug(authzr) authzr, authzr_response = net.poll(authzr) csr = M2Crypto.X509.load_request_string(pkg_resources.resource_string( - 'letsencrypt.client.tests', os.path.join('testdata', 'csr.pem'))) + 'letsencrypt.tests', os.path.join('testdata', 'csr.pem'))) try: net.request_issuance(csr, (authzr,)) except messages2.Error as error: diff --git a/letsencrypt/client/.gitignore b/letsencrypt/.gitignore similarity index 100% rename from letsencrypt/client/.gitignore rename to letsencrypt/.gitignore diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index b36747b5f..560191bf1 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,3 +1,4 @@ -"""Let's Encrypt.""" +"""Let's Encrypt client.""" + # version number like 1.2.3a0, must have at least 2 parts, like 1.2 __version__ = "0.1" diff --git a/letsencrypt/client/account.py b/letsencrypt/account.py similarity index 89% rename from letsencrypt/client/account.py rename to letsencrypt/account.py index 6c0ca9262..3f8e3d012 100644 --- a/letsencrypt/client/account.py +++ b/letsencrypt/account.py @@ -6,29 +6,29 @@ import re import configobj import zope.component -from letsencrypt.acme import messages2 +from acme import messages2 -from letsencrypt.client import crypto_util -from letsencrypt.client import errors -from letsencrypt.client import interfaces -from letsencrypt.client import le_util +from letsencrypt import crypto_util +from letsencrypt import errors +from letsencrypt import interfaces +from letsencrypt import le_util -from letsencrypt.client.display import util as display_util +from letsencrypt.display import util as display_util class Account(object): """ACME protocol registration. :ivar config: Client configuration object - :type config: :class:`~letsencrypt.client.interfaces.IConfig` + :type config: :class:`~letsencrypt.interfaces.IConfig` :ivar key: Account/Authorized Key - :type key: :class:`~letsencrypt.client.le_util.Key` + :type key: :class:`~letsencrypt.le_util.Key` :ivar str email: Client's email address :ivar str phone: Client's phone number :ivar regr: Registration Resource - :type regr: :class:`~letsencrypt.acme.messages2.RegistrationResource` + :type regr: :class:`~acme.messages2.RegistrationResource` """ @@ -156,7 +156,7 @@ class Account(object): """Return all current accounts. :param config: Configuration - :type config: :class:`letsencrypt.client.interfaces.IConfig` + :type config: :class:`letsencrypt.interfaces.IConfig` """ try: @@ -178,10 +178,10 @@ class Account(object): """Generate an account from prompted user input. :param config: Configuration - :type config: :class:`letsencrypt.client.interfaces.IConfig` + :type config: :class:`letsencrypt.interfaces.IConfig` :returns: Account or None - :rtype: :class:`letsencrypt.client.account.Account` + :rtype: :class:`letsencrypt.account.Account` """ while True: @@ -201,11 +201,11 @@ class Account(object): """Generate a new account from an email address. :param config: Configuration - :type config: :class:`letsencrypt.client.interfaces.IConfig` + :type config: :class:`letsencrypt.interfaces.IConfig` :param str email: Email address - :raises letsencrypt.client.errors.LetsEncryptClientError: If invalid + :raises letsencrypt.errors.LetsEncryptClientError: If invalid email address is given. """ diff --git a/letsencrypt/client/achallenges.py b/letsencrypt/achallenges.py similarity index 85% rename from letsencrypt/client/achallenges.py rename to letsencrypt/achallenges.py index 1a5cf9c8e..77e362f22 100644 --- a/letsencrypt/client/achallenges.py +++ b/letsencrypt/achallenges.py @@ -1,12 +1,12 @@ """Client annotated ACME challenges. Please use names such as ``achall`` to distiguish from variables "of type" -:class:`letsencrypt.acme.challenges.Challenge` (denoted by ``chall``) +:class:`acme.challenges.Challenge` (denoted by ``chall``) and :class:`.ChallengeBody` (denoted by ``challb``):: - from letsencrypt.acme import challenges - from letsencrypt.acme import messages2 - from letsencrypt.client import achallenges + from acme import challenges + from acme import messages2 + from letsencrypt import achallenges chall = challenges.DNS(token='foo') challb = messages2.ChallengeBody(chall=chall) @@ -17,10 +17,10 @@ Note, that all annotated challenges act as a proxy objects:: achall.token == challb.token """ -from letsencrypt.acme import challenges -from letsencrypt.acme.jose import util as jose_util +from acme import challenges +from acme.jose import util as jose_util -from letsencrypt.client import crypto_util +from letsencrypt import crypto_util # pylint: disable=too-few-public-methods @@ -52,7 +52,7 @@ class DVSNI(AnnotatedChallenge): :returns: ``(cert_pem, response)`` tuple, where ``cert_pem`` is the PEM encoded certificate and ``response`` is an instance - :class:`letsencrypt.acme.challenges.DVSNIResponse`. + :class:`acme.challenges.DVSNIResponse`. :rtype: tuple """ diff --git a/letsencrypt/client/augeas_configurator.py b/letsencrypt/augeas_configurator.py similarity index 96% rename from letsencrypt/client/augeas_configurator.py rename to letsencrypt/augeas_configurator.py index 713d49291..c59d755c2 100644 --- a/letsencrypt/client/augeas_configurator.py +++ b/letsencrypt/augeas_configurator.py @@ -3,22 +3,22 @@ import logging import augeas -from letsencrypt.client import reverter -from letsencrypt.client.plugins import common +from letsencrypt import reverter +from letsencrypt.plugins import common class AugeasConfigurator(common.Plugin): """Base Augeas Configurator class. :ivar config: Configuration. - :type config: :class:`~letsencrypt.client.interfaces.IConfig` + :type config: :class:`~letsencrypt.interfaces.IConfig` :ivar aug: Augeas object :type aug: :class:`augeas.Augeas` :ivar str save_notes: Human-readable configuration change notes :ivar reverter: saves and reverts checkpoints - :type reverter: :class:`letsencrypt.client.reverter.Reverter` + :type reverter: :class:`letsencrypt.reverter.Reverter` """ diff --git a/letsencrypt/client/auth_handler.py b/letsencrypt/auth_handler.py similarity index 90% rename from letsencrypt/client/auth_handler.py rename to letsencrypt/auth_handler.py index 0f2d76653..e83cdd717 100644 --- a/letsencrypt/client/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -3,38 +3,38 @@ import itertools import logging import time -from letsencrypt.acme import challenges -from letsencrypt.acme import messages2 +from acme import challenges +from acme import messages2 -from letsencrypt.client import achallenges -from letsencrypt.client import constants -from letsencrypt.client import errors +from letsencrypt import achallenges +from letsencrypt import constants +from letsencrypt import errors class AuthHandler(object): """ACME Authorization Handler for a client. :ivar dv_auth: Authenticator capable of solving - :class:`~letsencrypt.acme.challenges.DVChallenge` types - :type dv_auth: :class:`letsencrypt.client.interfaces.IAuthenticator` + :class:`~acme.challenges.DVChallenge` types + :type dv_auth: :class:`letsencrypt.interfaces.IAuthenticator` :ivar cont_auth: Authenticator capable of solving - :class:`~letsencrypt.acme.challenges.ContinuityChallenge` types - :type cont_auth: :class:`letsencrypt.client.interfaces.IAuthenticator` + :class:`~acme.challenges.ContinuityChallenge` types + :type cont_auth: :class:`letsencrypt.interfaces.IAuthenticator` :ivar network: Network object for sending and receiving authorization messages - :type network: :class:`letsencrypt.client.network2.Network` + :type network: :class:`letsencrypt.network2.Network` :ivar account: Client's Account - :type account: :class:`letsencrypt.client.account.Account` + :type account: :class:`letsencrypt.account.Account` :ivar dict authzr: ACME Authorization Resource dict where keys are domains - and values are :class:`letsencrypt.acme.messages2.AuthorizationResource` + and values are :class:`acme.messages2.AuthorizationResource` :ivar list dv_c: DV challenges in the form of - :class:`letsencrypt.client.achallenges.AnnotatedChallenge` + :class:`letsencrypt.achallenges.AnnotatedChallenge` :ivar list cont_c: Continuity challenges in the - form of :class:`letsencrypt.client.achallenges.AnnotatedChallenge` + form of :class:`letsencrypt.achallenges.AnnotatedChallenge` """ def __init__(self, dv_auth, cont_auth, network, account): @@ -219,10 +219,10 @@ class AuthHandler(object): each challenge resource. :param authzr: Authorization Resource - :type authzr: :class:`letsencrypt.acme.messages2.AuthorizationResource` + :type authzr: :class:`acme.messages2.AuthorizationResource` :param achall: Annotated challenge for which to get status - :type achall: :class:`letsencrypt.client.achallenges.AnnotatedChallenge` + :type achall: :class:`letsencrypt.achallenges.AnnotatedChallenge` """ for authzr_challb in authzr.body.challenges: @@ -289,9 +289,9 @@ class AuthHandler(object): :param list path: List of indices from `challenges`. :returns: dv_chall, list of DVChallenge type - :class:`letsencrypt.client.achallenges.Indexed` + :class:`letsencrypt.achallenges.Indexed` cont_chall, list of ContinuityChallenge type - :class:`letsencrypt.client.achallenges.Indexed` + :class:`letsencrypt.achallenges.Indexed` :rtype: tuple :raises errors.LetsEncryptClientError: If Challenge type is not @@ -319,15 +319,15 @@ def challb_to_achall(challb, key, domain): """Converts a ChallengeBody object to an AnnotatedChallenge. :param challb: ChallengeBody - :type challb: :class:`letsencrypt.acme.messages2.ChallengeBody` + :type challb: :class:`acme.messages2.ChallengeBody` :param key: Key - :type key: :class:`letsencrypt.client.le_util.Key` + :type key: :class:`letsencrypt.le_util.Key` :param str domain: Domain of the challb :returns: Appropriate AnnotatedChallenge - :rtype: :class:`letsencrypt.client.achallenges.AnnotatedChallenge` + :rtype: :class:`letsencrypt.achallenges.AnnotatedChallenge` """ chall = challb.chall @@ -368,22 +368,22 @@ def gen_challenge_path(challbs, preferences, combinations): .. todo:: This can be possibly be rewritten to use resolved_combinations. :param tuple challbs: A tuple of challenges - (:class:`letsencrypt.acme.messages2.Challenge`) from - :class:`letsencrypt.acme.messages2.AuthorizationResource` to be + (:class:`acme.messages2.Challenge`) from + :class:`acme.messages2.AuthorizationResource` to be fulfilled by the client in order to prove possession of the identifier. :param list preferences: List of challenge preferences for domain - (:class:`letsencrypt.acme.challenges.Challenge` subclasses) + (:class:`acme.challenges.Challenge` subclasses) :param tuple combinations: A collection of sets of challenges from - :class:`letsencrypt.acme.messages.Challenge`, each of which would + :class:`acme.messages.Challenge`, each of which would be sufficient to prove possession of the identifier. :returns: tuple of indices from ``challenges``. :rtype: tuple - :raises letsencrypt.client.errors.AuthorizationError: If a + :raises letsencrypt.errors.AuthorizationError: If a path cannot be created that satisfies the CA given the preferences and combinations. diff --git a/letsencrypt/client/cli.py b/letsencrypt/cli.py similarity index 96% rename from letsencrypt/client/cli.py rename to letsencrypt/cli.py index a83c0f767..be933d891 100644 --- a/letsencrypt/client/cli.py +++ b/letsencrypt/cli.py @@ -12,19 +12,19 @@ import zope.interface.verify import letsencrypt -from letsencrypt.client import account -from letsencrypt.client import configuration -from letsencrypt.client import constants -from letsencrypt.client import client -from letsencrypt.client import errors -from letsencrypt.client import interfaces -from letsencrypt.client import le_util -from letsencrypt.client import log +from letsencrypt import account +from letsencrypt import configuration +from letsencrypt import constants +from letsencrypt import client +from letsencrypt import errors +from letsencrypt import interfaces +from letsencrypt import le_util +from letsencrypt import log -from letsencrypt.client.display import util as display_util -from letsencrypt.client.display import ops as display_ops +from letsencrypt.display import util as display_util +from letsencrypt.display import ops as display_ops -from letsencrypt.client.plugins import disco as plugins_disco +from letsencrypt.plugins import disco as plugins_disco def _account_init(args, config): diff --git a/letsencrypt/client/client.py b/letsencrypt/client.py similarity index 85% rename from letsencrypt/client/client.py rename to letsencrypt/client.py index a764a178c..b92aded4a 100644 --- a/letsencrypt/client/client.py +++ b/letsencrypt/client.py @@ -6,43 +6,43 @@ import pkg_resources import M2Crypto import zope.component -from letsencrypt.acme import jose -from letsencrypt.acme.jose import jwk +from acme import jose +from acme.jose import jwk -from letsencrypt.client import account -from letsencrypt.client import auth_handler -from letsencrypt.client import continuity_auth -from letsencrypt.client import crypto_util -from letsencrypt.client import errors -from letsencrypt.client import interfaces -from letsencrypt.client import le_util -from letsencrypt.client import network2 -from letsencrypt.client import reverter -from letsencrypt.client import revoker +from letsencrypt import account +from letsencrypt import auth_handler +from letsencrypt import continuity_auth +from letsencrypt import crypto_util +from letsencrypt import errors +from letsencrypt import interfaces +from letsencrypt import le_util +from letsencrypt import network2 +from letsencrypt import reverter +from letsencrypt import revoker -from letsencrypt.client.display import ops as display_ops -from letsencrypt.client.display import enhancements +from letsencrypt.display import ops as display_ops +from letsencrypt.display import enhancements class Client(object): """ACME protocol client. :ivar network: Network object for sending and receiving messages - :type network: :class:`letsencrypt.client.network2.Network` + :type network: :class:`letsencrypt.network2.Network` :ivar account: Account object used for registration - :type account: :class:`letsencrypt.client.account.Account` + :type account: :class:`letsencrypt.account.Account` :ivar auth_handler: Object that supports the IAuthenticator interface. auth_handler contains both a dv_authenticator and a continuity_authenticator - :type auth_handler: :class:`letsencrypt.client.auth_handler.AuthHandler` + :type auth_handler: :class:`letsencrypt.auth_handler.AuthHandler` :ivar installer: Object supporting the IInstaller interface. - :type installer: :class:`letsencrypt.client.interfaces.IInstaller` + :type installer: :class:`letsencrypt.interfaces.IInstaller` :ivar config: Configuration. - :type config: :class:`~letsencrypt.client.interfaces.IConfig` + :type config: :class:`~letsencrypt.interfaces.IConfig` """ @@ -50,10 +50,10 @@ class Client(object): """Initialize a client. :param dv_auth: IAuthenticator that can solve the - :const:`letsencrypt.client.constants.DV_CHALLENGES`. - The :meth:`~letsencrypt.client.interfaces.IAuthenticator.prepare` + :const:`letsencrypt.constants.DV_CHALLENGES`. + The :meth:`~letsencrypt.interfaces.IAuthenticator.prepare` must have already been run. - :type dv_auth: :class:`letsencrypt.client.interfaces.IAuthenticator` + :type dv_auth: :class:`letsencrypt.interfaces.IAuthenticator` """ self.account = account_ @@ -67,7 +67,8 @@ class Client(object): self.config = config if dv_auth is not None: - cont_auth = continuity_auth.ContinuityAuthenticator(config) + cont_auth = continuity_auth.ContinuityAuthenticator(config, + installer) self.auth_handler = auth_handler.AuthHandler( dv_auth, cont_auth, self.network, self.account) else: @@ -107,7 +108,7 @@ class Client(object): :type csr: :class:`CSR` :returns: cert_key, cert_path, chain_path - :rtype: `tuple` of (:class:`letsencrypt.client.le_util.Key`, str, str) + :rtype: `tuple` of (:class:`letsencrypt.le_util.Key`, str, str) """ if self.auth_handler is None: @@ -148,7 +149,7 @@ class Client(object): """Saves the certificate received from the ACME server. :param certr: ACME "certificate" resource. - :type certr: :class:`letsencrypt.acme.messages.Certificate` + :type certr: :class:`acme.messages.Certificate` :param str cert_path: Path to attempt to save the cert file :param str chain_path: Path to attempt to save the chain file @@ -196,7 +197,7 @@ class Client(object): :param list domains: list of domains to install the certificate :param privkey: private key for certificate - :type privkey: :class:`letsencrypt.client.le_util.Key` + :type privkey: :class:`letsencrypt.le_util.Key` :param str cert_path: certificate file path :param str chain_path: chain file path @@ -232,7 +233,7 @@ class Client(object): :param redirect: If traffic should be forwarded from HTTP to HTTPS. :type redirect: bool or None - :raises letsencrypt.client.errors.LetsEncryptClientError: if + :raises letsencrypt.errors.LetsEncryptClientError: if no installer is specified in the client. """ @@ -251,7 +252,7 @@ class Client(object): """Redirect all traffic from HTTP to HTTPS :param vhost: list of ssl_vhosts - :type vhost: :class:`letsencrypt.client.interfaces.IInstaller` + :type vhost: :class:`letsencrypt.interfaces.IInstaller` """ for dom in domains: @@ -274,12 +275,12 @@ def validate_key_csr(privkey, csr=None): If csr is left as None, only the key will be validated. :param privkey: Key associated with CSR - :type privkey: :class:`letsencrypt.client.le_util.Key` + :type privkey: :class:`letsencrypt.le_util.Key` :param csr: CSR - :type csr: :class:`letsencrypt.client.le_util.CSR` + :type csr: :class:`letsencrypt.le_util.CSR` - :raises letsencrypt.client.errors.LetsEncryptClientError: when + :raises letsencrypt.errors.LetsEncryptClientError: when validation fails """ @@ -317,10 +318,10 @@ def determine_account(config): Will create an account if necessary. :param config: Configuration object - :type config: :class:`letsencrypt.client.interfaces.IConfig` + :type config: :class:`letsencrypt.interfaces.IConfig` :returns: Account - :rtype: :class:`letsencrypt.client.account.Account` + :rtype: :class:`letsencrypt.account.Account` """ accounts = account.Account.get_accounts(config) @@ -339,7 +340,7 @@ def rollback(default_installer, checkpoints, config, plugins): :param int checkpoints: Number of checkpoints to revert. :param config: Configuration. - :type config: :class:`letsencrypt.client.interfaces.IConfig` + :type config: :class:`letsencrypt.interfaces.IConfig` """ # Misconfigurations are only a slight problems... allow the user to rollback @@ -359,7 +360,7 @@ def revoke(default_installer, config, plugins, no_confirm, cert, authkey): """Revoke certificates. :param config: Configuration. - :type config: :class:`letsencrypt.client.interfaces.IConfig` + :type config: :class:`letsencrypt.interfaces.IConfig` """ installer = display_ops.pick_installer( @@ -382,7 +383,7 @@ def view_config_changes(config): .. note:: This assumes that the installation is using a Reverter object. :param config: Configuration. - :type config: :class:`letsencrypt.client.interfaces.IConfig` + :type config: :class:`letsencrypt.interfaces.IConfig` """ rev = reverter.Reverter(config) diff --git a/letsencrypt/client/__init__.py b/letsencrypt/client/__init__.py deleted file mode 100644 index d6c102964..000000000 --- a/letsencrypt/client/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt client.""" diff --git a/letsencrypt/client/continuity_auth.py b/letsencrypt/client/continuity_auth.py deleted file mode 100644 index 063d3d408..000000000 --- a/letsencrypt/client/continuity_auth.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Continuity Authenticator""" -import zope.interface - -from letsencrypt.acme import challenges - -from letsencrypt.client import achallenges -from letsencrypt.client import errors -from letsencrypt.client import interfaces -from letsencrypt.client import recovery_token - - -class ContinuityAuthenticator(object): - """IAuthenticator for - :const:`~letsencrypt.acme.challenges.ContinuityChallenge` class challenges. - - :ivar rec_token: Performs "recoveryToken" challenges - :type rec_token: :class:`letsencrypt.client.recovery_token.RecoveryToken` - - """ - zope.interface.implements(interfaces.IAuthenticator) - - # This will have an installer soon for get_key/cert purposes - def __init__(self, config): - """Initialize Client Authenticator. - - :param config: Configuration. - :type config: :class:`letsencrypt.client.interfaces.IConfig` - - """ - self.rec_token = recovery_token.RecoveryToken( - config.server, config.rec_token_dir) - - def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use - """Return list of challenge preferences.""" - return [challenges.RecoveryToken] - - def perform(self, achalls): - """Perform client specific challenges for IAuthenticator""" - responses = [] - for achall in achalls: - if isinstance(achall, achallenges.RecoveryToken): - responses.append(self.rec_token.perform(achall)) - else: - raise errors.LetsEncryptContAuthError("Unexpected Challenge") - return responses - - def cleanup(self, achalls): - """Cleanup call for IAuthenticator.""" - for achall in achalls: - if isinstance(achall, achallenges.RecoveryToken): - self.rec_token.cleanup(achall) - else: - raise errors.LetsEncryptContAuthError("Unexpected Challenge") diff --git a/letsencrypt/client/display/__init__.py b/letsencrypt/client/display/__init__.py deleted file mode 100644 index b652c58a9..000000000 --- a/letsencrypt/client/display/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt client.display""" diff --git a/letsencrypt/client/plugins/apache/__init__.py b/letsencrypt/client/plugins/apache/__init__.py deleted file mode 100644 index 70172b06d..000000000 --- a/letsencrypt/client/plugins/apache/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt client.plugins.apache.""" diff --git a/letsencrypt/client/plugins/nginx/__init__.py b/letsencrypt/client/plugins/nginx/__init__.py deleted file mode 100644 index 63728924f..000000000 --- a/letsencrypt/client/plugins/nginx/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Let's Encrypt client.plugins.nginx.""" diff --git a/letsencrypt/client/plugins/nginx/dvsni.py b/letsencrypt/client/plugins/nginx/dvsni.py deleted file mode 100644 index 7233d7c62..000000000 --- a/letsencrypt/client/plugins/nginx/dvsni.py +++ /dev/null @@ -1,63 +0,0 @@ -"""NginxDVSNI""" -import logging - -from letsencrypt.client.plugins.apache.dvsni import ApacheDvsni - - -class NginxDvsni(ApacheDvsni): - """Class performs DVSNI challenges within the Nginx configurator. - - .. todo:: This is basically copied-and-pasted from the Apache equivalent. - It doesn't actually work yet. - - :ivar configurator: NginxConfigurator object - :type configurator: :class:`~nginx.configurator.NginxConfigurator` - - :ivar list achalls: Annotated :class:`~letsencrypt.client.achallenges.DVSNI` - challenges. - - :param list indices: Meant to hold indices of challenges in a - larger array. NginxDvsni is capable of solving many challenges - at once which causes an indexing issue within NginxConfigurator - who must return all responses in order. Imagine NginxConfigurator - maintaining state about where all of the SimpleHTTPS Challenges, - Dvsni Challenges belong in the response array. This is an optional - utility. - - :param str challenge_conf: location of the challenge config file - - """ - - def perform(self): - """Perform a DVSNI challenge on Nginx.""" - if not self.achalls: - return [] - - self.configurator.save() - - addresses = [] - for achall in self.achalls: - vhost = self.configurator.choose_vhost(achall.domain) - if vhost is None: - logging.error( - "No nginx vhost exists with servername or alias of: %s", - achall.domain) - logging.error("No default 443 nginx vhost exists") - logging.error("Please specify servernames in the Nginx config") - return None - else: - addresses.append(list(vhost.addrs)) - - responses = [] - - # Create all of the challenge certs - # for achall in self.achalls: - # responses.append(self._setup_challenge_cert(achall)) - - # Setup the configuration - # self._mod_config(addresses) - - # Save reversible changes - self.configurator.save("SNI Challenge", True) - - return responses diff --git a/letsencrypt/client/plugins/nginx/tests/dvsni_test.py b/letsencrypt/client/plugins/nginx/tests/dvsni_test.py deleted file mode 100644 index bf66367e6..000000000 --- a/letsencrypt/client/plugins/nginx/tests/dvsni_test.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Test for letsencrypt.client.plugins.nginx.dvsni.""" -import pkg_resources -import unittest -import shutil - -import mock - -from letsencrypt.acme import challenges -from letsencrypt.acme import messages2 - -from letsencrypt.client import achallenges -from letsencrypt.client import le_util - -from letsencrypt.client.plugins.nginx.tests import util - - -class DvsniPerformTest(util.NginxTest): - """Test the NginxDVSNI challenge.""" - - def setUp(self): - super(DvsniPerformTest, self).setUp() - - config = util.get_nginx_configurator( - self.config_path, self.config_dir, self.work_dir, - self.ssl_options) - - rsa256_file = pkg_resources.resource_filename( - "letsencrypt.acme.jose", "testdata/rsa256_key.pem") - rsa256_pem = pkg_resources.resource_string( - "letsencrypt.acme.jose", "testdata/rsa256_key.pem") - - auth_key = le_util.Key(rsa256_file, rsa256_pem) - - from letsencrypt.client.plugins.nginx import dvsni - self.sni = dvsni.NginxDvsni(config) - - self.achalls = [ - achallenges.DVSNI( - challb=messages2.ChallengeBody( - chall=challenges.DVSNI( - r="foo", - nonce="bar", - ), - uri="https://letsencrypt-ca.org/chall0_uri", - status=messages2.Status("pending"), - ), domain="www.example.com", key=auth_key), - achallenges.DVSNI( - challb=messages2.ChallengeBody( - chall=challenges.DVSNI( - r="\xba\xa9\xda? 0) @@ -386,7 +382,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): def setup_work_direc(): """Setup directories. - :returns: Mocked :class:`letsencrypt.client.interfaces.IConfig` + :returns: Mocked :class:`letsencrypt.interfaces.IConfig` """ work_dir = tempfile.mkdtemp("work") @@ -445,4 +441,4 @@ def update_file(filename, string): if __name__ == '__main__': - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/client/tests/revoker_test.py b/letsencrypt/tests/revoker_test.py similarity index 83% rename from letsencrypt/client/tests/revoker_test.py rename to letsencrypt/tests/revoker_test.py index 1ceb8ae9a..5f8b5b224 100644 --- a/letsencrypt/client/tests/revoker_test.py +++ b/letsencrypt/tests/revoker_test.py @@ -1,4 +1,4 @@ -"""Test letsencrypt.client.revoker.""" +"""Test letsencrypt.revoker.""" import csv import os import pkg_resources @@ -8,10 +8,11 @@ import unittest import mock -from letsencrypt.client import errors -from letsencrypt.client import le_util -from letsencrypt.client.plugins.apache import configurator -from letsencrypt.client.display import util as display_util +from letsencrypt import errors +from letsencrypt import le_util +from letsencrypt.display import util as display_util + +from letsencrypt_apache import configurator class RevokerBase(unittest.TestCase): # pylint: disable=too-few-public-methods @@ -26,7 +27,7 @@ class RevokerBase(unittest.TestCase): # pylint: disable=too-few-public-methods def _store_certs(self): # pylint: disable=protected-access - from letsencrypt.client.revoker import Revoker + from letsencrypt.revoker import Revoker Revoker.store_cert_key(self.paths[0], self.key_path, self.mock_config) Revoker.store_cert_key(self.paths[1], self.key_path, self.mock_config) @@ -50,7 +51,7 @@ class RevokerBase(unittest.TestCase): # pylint: disable=too-few-public-methods class RevokerTest(RevokerBase): def setUp(self): - from letsencrypt.client.revoker import Revoker + from letsencrypt.revoker import Revoker super(RevokerTest, self).setUp() with open(self.key_path) as key_file: @@ -65,9 +66,8 @@ class RevokerTest(RevokerBase): def tearDown(self): shutil.rmtree(self.backup_dir) - @mock.patch("letsencrypt.client.revoker.network." - "Network.send_and_receive_expected") - @mock.patch("letsencrypt.client.revoker.revocation") + @mock.patch("letsencrypt.revoker.network.Network.send_and_receive_expected") + @mock.patch("letsencrypt.revoker.revocation") def test_revoke_by_key_all(self, mock_display, mock_net): mock_display().confirm_revocation.return_value = True @@ -80,7 +80,7 @@ class RevokerTest(RevokerBase): self.assertEqual(mock_net.call_count, 2) - @mock.patch("letsencrypt.client.revoker.Crypto.PublicKey.RSA.importKey") + @mock.patch("letsencrypt.revoker.Crypto.PublicKey.RSA.importKey") def test_revoke_by_invalid_keys(self, mock_import): mock_import.side_effect = ValueError self.assertRaises(errors.LetsEncryptRevokerError, @@ -92,15 +92,13 @@ class RevokerTest(RevokerBase): self.revoker.revoke_from_key, self.key) - @mock.patch("letsencrypt.client.revoker.network." - "Network.send_and_receive_expected") - @mock.patch("letsencrypt.client.revoker.revocation") + @mock.patch("letsencrypt.revoker.network.Network.send_and_receive_expected") + @mock.patch("letsencrypt.revoker.revocation") def test_revoke_by_wrong_key(self, mock_display, mock_net): mock_display().confirm_revocation.return_value = True key_path = pkg_resources.resource_filename( - "letsencrypt.acme.jose", os.path.join( - "testdata", "rsa256_key.pem")) + "acme.jose", os.path.join("testdata", "rsa256_key.pem")) wrong_key = le_util.Key(key_path, open(key_path).read()) self.revoker.revoke_from_key(wrong_key) @@ -110,9 +108,8 @@ class RevokerTest(RevokerBase): # No revocation went through self.assertEqual(mock_net.call_count, 0) - @mock.patch("letsencrypt.client.revoker.network." - "Network.send_and_receive_expected") - @mock.patch("letsencrypt.client.revoker.revocation") + @mock.patch("letsencrypt.revoker.network.Network.send_and_receive_expected") + @mock.patch("letsencrypt.revoker.revocation") def test_revoke_by_cert(self, mock_display, mock_net): mock_display().confirm_revocation.return_value = True @@ -128,9 +125,8 @@ class RevokerTest(RevokerBase): self.assertEqual(mock_net.call_count, 1) - @mock.patch("letsencrypt.client.revoker.network." - "Network.send_and_receive_expected") - @mock.patch("letsencrypt.client.revoker.revocation") + @mock.patch("letsencrypt.revoker.network.Network.send_and_receive_expected") + @mock.patch("letsencrypt.revoker.revocation") def test_revoke_by_cert_not_found(self, mock_display, mock_net): mock_display().confirm_revocation.return_value = True @@ -148,9 +144,8 @@ class RevokerTest(RevokerBase): self.assertEqual(mock_net.call_count, 1) - @mock.patch("letsencrypt.client.revoker.network." - "Network.send_and_receive_expected") - @mock.patch("letsencrypt.client.revoker.revocation") + @mock.patch("letsencrypt.revoker.network.Network.send_and_receive_expected") + @mock.patch("letsencrypt.revoker.revocation") def test_revoke_by_menu(self, mock_display, mock_net): mock_display().confirm_revocation.return_value = True mock_display.display_certs.side_effect = [ @@ -172,10 +167,9 @@ class RevokerTest(RevokerBase): self.assertEqual(mock_net.call_count, 1) self.assertEqual(mock_display.more_info_cert.call_count, 1) - @mock.patch("letsencrypt.client.revoker.logging") - @mock.patch("letsencrypt.client.revoker.network." - "Network.send_and_receive_expected") - @mock.patch("letsencrypt.client.revoker.revocation") + @mock.patch("letsencrypt.revoker.logging") + @mock.patch("letsencrypt.revoker.network.Network.send_and_receive_expected") + @mock.patch("letsencrypt.revoker.revocation") def test_revoke_by_menu_delete_all(self, mock_display, mock_net, mock_log): mock_display().confirm_revocation.return_value = True mock_display.display_certs.return_value = (display_util.OK, 0) @@ -192,9 +186,9 @@ class RevokerTest(RevokerBase): # Info is called when there aren't any certs left... self.assertTrue(mock_log.info.called) - @mock.patch("letsencrypt.client.revoker.revocation") - @mock.patch("letsencrypt.client.revoker.Revoker._acme_revoke") - @mock.patch("letsencrypt.client.revoker.logging") + @mock.patch("letsencrypt.revoker.revocation") + @mock.patch("letsencrypt.revoker.Revoker._acme_revoke") + @mock.patch("letsencrypt.revoker.logging") def test_safe_revoke_acme_fail(self, mock_log, mock_revoke, mock_display): # pylint: disable=protected-access mock_revoke.side_effect = errors.LetsEncryptClientError @@ -203,7 +197,7 @@ class RevokerTest(RevokerBase): self.revoker._safe_revoke(self.certs) self.assertTrue(mock_log.error.called) - @mock.patch("letsencrypt.client.revoker.Crypto.PublicKey.RSA.importKey") + @mock.patch("letsencrypt.revoker.Crypto.PublicKey.RSA.importKey") def test_acme_revoke_failure(self, mock_crypto): # pylint: disable=protected-access mock_crypto.side_effect = ValueError @@ -213,7 +207,7 @@ class RevokerTest(RevokerBase): def test_remove_certs_from_list_bad_certs(self): # pylint: disable=protected-access - from letsencrypt.client.revoker import Cert + from letsencrypt.revoker import Cert new_cert = Cert(self.paths[0]) @@ -252,7 +246,7 @@ class RevokerInstallerTest(RevokerBase): self._store_certs() def _get_revoker(self, installer): - from letsencrypt.client.revoker import Revoker + from letsencrypt.revoker import Revoker return Revoker(installer, self.mock_config) def test_no_installer_get_installed_locations(self): @@ -274,7 +268,7 @@ class RevokerInstallerTest(RevokerBase): self.assertEqual( sha_vh[cert.get_fingerprint()], self.installs[i]) - @mock.patch("letsencrypt.client.revoker.M2Crypto.X509.load_cert") + @mock.patch("letsencrypt.revoker.M2Crypto.X509.load_cert") def test_get_installed_load_failure(self, mock_m2): mock_installer = mock.MagicMock() mock_installer.get_all_certs_keys.return_value = self.certs_keys @@ -296,11 +290,11 @@ class RevokerClassMethodsTest(RevokerBase): shutil.rmtree(self.backup_dir) def _call(self, cert_path, key_path): - from letsencrypt.client.revoker import Revoker + from letsencrypt.revoker import Revoker Revoker.store_cert_key(cert_path, key_path, self.mock_config) def test_store_two(self): - from letsencrypt.client.revoker import Revoker + from letsencrypt.revoker import Revoker self._call(self.paths[0], self.key_path) self._call(self.paths[1], self.key_path) @@ -318,7 +312,7 @@ class RevokerClassMethodsTest(RevokerBase): self.assertEqual(len(rows), 2) def test_store_one_mixed(self): - from letsencrypt.client.revoker import Revoker + from letsencrypt.revoker import Revoker self._write_rows( [["5", "blank", "blank"], ["18", "dc", "dc"], ["21", "b", "b"]]) self._call(self.paths[0], self.key_path) @@ -338,14 +332,14 @@ class CertTest(unittest.TestCase): self.paths, self.certs, self.key_path = create_revoker_certs() def test_failed_load(self): - from letsencrypt.client.revoker import Cert + from letsencrypt.revoker import Cert self.assertRaises(errors.LetsEncryptRevokerError, Cert, self.key_path) def test_no_row(self): self.assertEqual(self.certs[0].get_row(), None) def test_meta_moved_files(self): - from letsencrypt.client.revoker import Cert + from letsencrypt.revoker import Cert fake_path = "/not/a/real/path/r72d3t6" self.certs[0].add_meta( 0, fake_path, fake_path, self.paths[0], self.key_path) @@ -354,7 +348,7 @@ class CertTest(unittest.TestCase): self.assertEqual(self.certs[0].orig_key.status, Cert.DELETED_MSG) def test_meta_changed_files(self): - from letsencrypt.client.revoker import Cert + from letsencrypt.revoker import Cert self.certs[0].add_meta( 0, self.paths[1], self.paths[1], self.paths[0], self.key_path) @@ -385,9 +379,9 @@ class CertTest(unittest.TestCase): def create_revoker_certs(): """Create a few revoker.Cert objects.""" - from letsencrypt.client.revoker import Cert + from letsencrypt.revoker import Cert - base_package = "letsencrypt.client.tests" + base_package = "letsencrypt.tests" cert0_path = pkg_resources.resource_filename( base_package, os.path.join("testdata", "cert.pem")) @@ -405,4 +399,4 @@ def create_revoker_certs(): if __name__ == "__main__": - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/client/tests/testdata/cert-san.pem b/letsencrypt/tests/testdata/cert-san.pem similarity index 100% rename from letsencrypt/client/tests/testdata/cert-san.pem rename to letsencrypt/tests/testdata/cert-san.pem diff --git a/letsencrypt/client/tests/testdata/cert.b64jose b/letsencrypt/tests/testdata/cert.b64jose similarity index 100% rename from letsencrypt/client/tests/testdata/cert.b64jose rename to letsencrypt/tests/testdata/cert.b64jose diff --git a/letsencrypt/client/tests/testdata/cert.pem b/letsencrypt/tests/testdata/cert.pem similarity index 100% rename from letsencrypt/client/tests/testdata/cert.pem rename to letsencrypt/tests/testdata/cert.pem diff --git a/letsencrypt/client/tests/testdata/csr-san.der b/letsencrypt/tests/testdata/csr-san.der similarity index 100% rename from letsencrypt/client/tests/testdata/csr-san.der rename to letsencrypt/tests/testdata/csr-san.der diff --git a/letsencrypt/client/tests/testdata/csr-san.pem b/letsencrypt/tests/testdata/csr-san.pem similarity index 100% rename from letsencrypt/client/tests/testdata/csr-san.pem rename to letsencrypt/tests/testdata/csr-san.pem diff --git a/letsencrypt/client/tests/testdata/csr.der b/letsencrypt/tests/testdata/csr.der similarity index 100% rename from letsencrypt/client/tests/testdata/csr.der rename to letsencrypt/tests/testdata/csr.der diff --git a/letsencrypt/client/tests/testdata/csr.pem b/letsencrypt/tests/testdata/csr.pem similarity index 100% rename from letsencrypt/client/tests/testdata/csr.pem rename to letsencrypt/tests/testdata/csr.pem diff --git a/letsencrypt/tests/testdata/dsa512_key.pem b/letsencrypt/tests/testdata/dsa512_key.pem new file mode 100644 index 000000000..78e164712 --- /dev/null +++ b/letsencrypt/tests/testdata/dsa512_key.pem @@ -0,0 +1,14 @@ +-----BEGIN DSA PARAMETERS----- +MIGdAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqfn6GC +OixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSPAkEA +qfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xmrfvl +41pgNJpgu99YOYqPpS0g7A== +-----END DSA PARAMETERS----- +-----BEGIN DSA PRIVATE KEY----- +MIH5AgEAAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqf +n6GCOixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSP +AkEAqfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xm +rfvl41pgNJpgu99YOYqPpS0g7AJATQ2LUzjGQSM6UljcPY5I2OD9THkUR9kH2tth +zZd70UoI9btrVaTizgqYShuok94glSQNK0H92JgUk3scJPaAkAIVAMDn61h6vrCE +mNv063So6E+eYaIN +-----END DSA PRIVATE KEY----- diff --git a/letsencrypt/tests/testdata/dsa_cert.pem b/letsencrypt/tests/testdata/dsa_cert.pem new file mode 100644 index 000000000..ef94536e7 --- /dev/null +++ b/letsencrypt/tests/testdata/dsa_cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICuDCCAnWgAwIBAgIJAPjmErVMzwVLMAsGCWCGSAFlAwQDAjB3MQswCQYDVQQG +EwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQBgNVBAcMCUFubiBBcmJvcjErMCkG +A1UECgwiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBhbmQgdGhlIEVGRjEUMBIGA1UE +AwwLZXhhbXBsZS5jb20wHhcNMTUwNTEyMTUzOTQzWhcNMTUwNjExMTUzOTQzWjB3 +MQswCQYDVQQGEwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQBgNVBAcMCUFubiBB +cmJvcjErMCkGA1UECgwiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBhbmQgdGhlIEVG +RjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wgfEwgakGByqGSM44BAEwgZ0CQQDB5sSg +YF+iQpB4AscecBkxDBhTfkgsQF1XyhSbO/uqlJVSgeKHKp+foYI6LEApI/wQlhxO +KUio9sVt8XI4+VsvAhUA2gUcQOJCCScC8qsbvykfMAl1BI8CQQCp+RrkGeX4J4Qy +nNVkas5WpkT8sV1kr15Ppi1aPOq0iR/eHBdRXEmxOcEbjGat++XjWmA0mmC731g5 +io+lLSDsA0MAAkBNDYtTOMZBIzpSWNw9jkjY4P1MeRRH2Qfa22HNl3vRSgj1u2tV +pOLOCphKG6iT3iCVJA0rQf3YmBSTexwk9oCQo1AwTjAdBgNVHQ4EFgQUZ2DlTDGU +PMwTUt0KztM6IyX61BcwHwYDVR0jBBgwFoAUZ2DlTDGUPMwTUt0KztM6IyX61Bcw +DAYDVR0TBAUwAwEB/zALBglghkgBZQMEAwIDMAAwLQIVAIbMgGx+KwBr4rgqZ2Lh +AAO8TegHAhQsuxpIIIphiReoWEtEJk4TqEIz/A== +-----END CERTIFICATE----- diff --git a/letsencrypt/tests/testdata/matching_cert.pem b/letsencrypt/tests/testdata/matching_cert.pem new file mode 100644 index 000000000..fda9cb1f4 --- /dev/null +++ b/letsencrypt/tests/testdata/matching_cert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICNzCCAeGgAwIBAgIJALizm9Y3q620MA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNV +BAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5uIEFyYm9yMSsw +KQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUgRUZGMRQwEgYD +VQQDDAtleGFtcGxlLmNvbTAeFw0xNTA1MDkwMDI0NTJaFw0xNjA1MDgwMDI0NTJa +MHcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5u +IEFyYm9yMSswKQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUg +RUZGMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC +QQD0thFxUTc2v6qV55wRxfwnBUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3Hg +BVy9ddFc8RX4vNZaR+ROXNEzAgMBAAGjUDBOMB0GA1UdDgQWBBRJieHEVSHKmBk0 +mTExx1erzlylCjAfBgNVHSMEGDAWgBRJieHEVSHKmBk0mTExx1erzlylCjAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EABT/nlpqOaanFSLZmWIrKv0zt63k4 +bmWNMA8fYT45KYpLomsW8qXdpC82IlVKfNk7fW0UYT3HOeDSJRcycxNCTQ== +-----END CERTIFICATE----- diff --git a/letsencrypt/client/tests/testdata/rsa512_key.pem b/letsencrypt/tests/testdata/rsa512_key.pem similarity index 100% rename from letsencrypt/client/tests/testdata/rsa512_key.pem rename to letsencrypt/tests/testdata/rsa512_key.pem diff --git a/letsencrypt_apache/__init__.py b/letsencrypt_apache/__init__.py new file mode 100644 index 000000000..c0d1e0d52 --- /dev/null +++ b/letsencrypt_apache/__init__.py @@ -0,0 +1 @@ +"""Let's Encrypt Apache plugin.""" diff --git a/letsencrypt/client/plugins/apache/configurator.py b/letsencrypt_apache/configurator.py similarity index 94% rename from letsencrypt/client/plugins/apache/configurator.py rename to letsencrypt_apache/configurator.py index 82d6f323c..102718e13 100644 --- a/letsencrypt/client/plugins/apache/configurator.py +++ b/letsencrypt_apache/configurator.py @@ -9,19 +9,19 @@ import sys import zope.interface -from letsencrypt.acme import challenges +from acme import challenges -from letsencrypt.client import achallenges -from letsencrypt.client import augeas_configurator -from letsencrypt.client import constants as core_constants -from letsencrypt.client import errors -from letsencrypt.client import interfaces -from letsencrypt.client import le_util +from letsencrypt import achallenges +from letsencrypt import augeas_configurator +from letsencrypt import constants as core_constants +from letsencrypt import errors +from letsencrypt import interfaces +from letsencrypt import le_util -from letsencrypt.client.plugins.apache import constants -from letsencrypt.client.plugins.apache import dvsni -from letsencrypt.client.plugins.apache import obj -from letsencrypt.client.plugins.apache import parser +from letsencrypt_apache import constants +from letsencrypt_apache import dvsni +from letsencrypt_apache import obj +from letsencrypt_apache import parser # TODO: Augeas sections ie. , beginning and closing @@ -66,15 +66,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): needs of clients are clarified with the new and developing protocol. :ivar config: Configuration. - :type config: :class:`~letsencrypt.client.interfaces.IConfig` + :type config: :class:`~letsencrypt.interfaces.IConfig` :ivar parser: Handles low level parsing - :type parser: :class:`~letsencrypt.client.plugins.apache.parser` + :type parser: :class:`~letsencrypt_apache.parser` :ivar tup version: version of Apache :ivar list vhosts: All vhosts found in the configuration - (:class:`list` of - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost`) + (:class:`list` of :class:`~letsencrypt_apache.obj.VirtualHost`) :ivar dict assoc: Mapping between domains and vhosts @@ -222,7 +221,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str target_name: domain name :returns: ssl vhost associated with name - :rtype: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` """ # Allows for domain names to be associated with a virtual host @@ -263,7 +262,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str domain: domain name to associate :param vhost: virtual host to associate with domain - :type vhost: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ self.assoc[domain] = vhost @@ -300,7 +299,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Helper function for get_virtual_hosts(). :param host: In progress vhost whose names will be added - :type host: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type host: :class:`~letsencrypt_apache.obj.VirtualHost` """ name_match = self.aug.match(("%s//*[self::directive=~regexp('%s')] | " @@ -321,7 +320,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str path: Augeas path to virtual host :returns: newly created vhost - :rtype: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` """ addrs = set() @@ -344,9 +343,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def get_virtual_hosts(self): """Returns list of virtual hosts found in the Apache configuration. - :returns: List of - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` objects - found in configuration + :returns: List of :class:`~letsencrypt_apache.obj.VirtualHost` + objects found in configuration :rtype: list """ @@ -423,7 +421,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Checks to see if the server is ready for SNI challenges. :param vhost: VirtualHost to check SNI compatibility - :type vhost: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :param str default_addr: TODO - investigate function further @@ -455,11 +453,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. note:: This function saves the configuration :param nonssl_vhost: Valid VH that doesn't have SSLEngine on - :type nonssl_vhost: - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type nonssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :returns: SSL vhost - :rtype: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` """ avail_fp = nonssl_vhost.filep @@ -550,9 +547,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str domain: domain to enhance :param str enhancement: enhancement type defined in - :const:`~letsencrypt.client.constants.ENHANCEMENTS` + :const:`~letsencrypt.constants.ENHANCEMENTS` :param options: options for the enhancement - See :const:`~letsencrypt.client.constants.ENHANCEMENTS` + See :const:`~letsencrypt.constants.ENHANCEMENTS` documentation for appropriate parameter. """ @@ -579,15 +576,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. note:: This function saves the configuration :param ssl_vhost: Destination of traffic, an ssl enabled vhost - :type ssl_vhost: - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :param unused_options: Not currently used :type unused_options: Not Available :returns: Success, general_vhost (HTTP vhost) - :rtype: (bool, - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost`) + :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) """ if not mod_loaded("rewrite_module", self.conf('ctl')): @@ -638,7 +633,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): -1 is also returned in case of no redirection/rewrite directives :param vhost: vhost to check - :type vhost: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :returns: Success, code value... see documentation :rtype: bool, int @@ -670,12 +665,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Creates an http_vhost specifically to redirect for the ssl_vhost. :param ssl_vhost: ssl vhost - :type ssl_vhost: - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :returns: tuple of the form - (`success`, - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost`) + (`success`, :class:`~letsencrypt_apache.obj.VirtualHost`) :rtype: tuple """ @@ -758,8 +751,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if not conflict: returns space separated list of new host addrs :param ssl_vhost: SSL Vhost to check for possible port 80 redirection - :type ssl_vhost: - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :returns: TODO :rtype: TODO @@ -792,12 +784,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): Consider changing this into a dict check :param ssl_vhost: ssl vhost to check - :type ssl_vhost: - :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :returns: HTTP vhost or None if unsuccessful - :rtype: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` - or None + :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` or ``None`` """ # _default_:443 check @@ -887,7 +877,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. todo:: Make sure link is not broken... :param vhost: vhost to enable - :type vhost: :class:`~letsencrypt.client.plugins.apache.obj.VirtualHost` + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :returns: Success :rtype: bool diff --git a/letsencrypt/client/plugins/apache/constants.py b/letsencrypt_apache/constants.py similarity index 90% rename from letsencrypt/client/plugins/apache/constants.py rename to letsencrypt_apache/constants.py index d9f2a0b9d..b40e2ac65 100644 --- a/letsencrypt/client/plugins/apache/constants.py +++ b/letsencrypt_apache/constants.py @@ -13,7 +13,7 @@ CLI_DEFAULTS = dict( MOD_SSL_CONF = pkg_resources.resource_filename( - "letsencrypt.client.plugins.apache", "options-ssl.conf") + "letsencrypt_apache", "options-ssl.conf") """Path to the Apache mod_ssl config file found in the Let's Encrypt distribution.""" diff --git a/letsencrypt/client/plugins/apache/dvsni.py b/letsencrypt_apache/dvsni.py similarity index 94% rename from letsencrypt/client/plugins/apache/dvsni.py rename to letsencrypt_apache/dvsni.py index 7755658e7..ed7a216bb 100644 --- a/letsencrypt/client/plugins/apache/dvsni.py +++ b/letsencrypt_apache/dvsni.py @@ -2,7 +2,7 @@ import logging import os -from letsencrypt.client.plugins.apache import parser +from letsencrypt_apache import parser class ApacheDvsni(object): @@ -11,7 +11,7 @@ class ApacheDvsni(object): :ivar configurator: ApacheConfigurator object :type configurator: :class:`~apache.configurator.ApacheConfigurator` - :ivar list achalls: Annotated :class:`~letsencrypt.client.achallenges.DVSNI` + :ivar list achalls: Annotated :class:`~letsencrypt.achallenges.DVSNI` challenges. :param list indices: Meant to hold indices of challenges in a @@ -54,7 +54,7 @@ class ApacheDvsni(object): """Add challenge to DVSNI object to perform at once. :param achall: Annotated DVSNI challenge. - :type achall: :class:`letsencrypt.client.achallenges.DVSNI` + :type achall: :class:`letsencrypt.achallenges.DVSNI` :param int idx: index to challenge in a larger array @@ -128,7 +128,7 @@ class ApacheDvsni(object): Result: Apache config includes virtual servers for issued challs :param list ll_addrs: list of list of - :class:`letsencrypt.client.plugins.apache.obj.Addr` to apply + :class:`letsencrypt.plugins.apache.obj.Addr` to apply """ # TODO: Use ip address of existing vhost instead of relying on FQDN @@ -164,7 +164,7 @@ class ApacheDvsni(object): """Chocolate virtual server configuration text :param achall: Annotated DVSNI challenge. - :type achall: :class:`letsencrypt.client.achallenges.DVSNI` + :type achall: :class:`letsencrypt.achallenges.DVSNI` :param list ip_addrs: addresses of challenged domain :class:`list` of type :class:`~apache.obj.Addr` @@ -191,7 +191,7 @@ class ApacheDvsni(object): """Returns standardized name for challenge certificate. :param achall: Annotated DVSNI challenge. - :type achall: :class:`letsencrypt.client.achallenges.DVSNI` + :type achall: :class:`letsencrypt.achallenges.DVSNI` :returns: certificate file name :rtype: str diff --git a/letsencrypt/client/plugins/apache/obj.py b/letsencrypt_apache/obj.py similarity index 100% rename from letsencrypt/client/plugins/apache/obj.py rename to letsencrypt_apache/obj.py diff --git a/letsencrypt/client/plugins/apache/options-ssl.conf b/letsencrypt_apache/options-ssl.conf similarity index 100% rename from letsencrypt/client/plugins/apache/options-ssl.conf rename to letsencrypt_apache/options-ssl.conf diff --git a/letsencrypt/client/plugins/apache/parser.py b/letsencrypt_apache/parser.py similarity index 99% rename from letsencrypt/client/plugins/apache/parser.py rename to letsencrypt_apache/parser.py index b713c8f6a..9e6e9efe6 100644 --- a/letsencrypt/client/plugins/apache/parser.py +++ b/letsencrypt_apache/parser.py @@ -2,7 +2,7 @@ import os import re -from letsencrypt.client import errors +from letsencrypt import errors class ApacheParser(object): diff --git a/letsencrypt/client/plugins/apache/tests/__init__.py b/letsencrypt_apache/tests/__init__.py similarity index 100% rename from letsencrypt/client/plugins/apache/tests/__init__.py rename to letsencrypt_apache/tests/__init__.py diff --git a/letsencrypt/client/plugins/apache/tests/configurator_test.py b/letsencrypt_apache/tests/configurator_test.py similarity index 89% rename from letsencrypt/client/plugins/apache/tests/configurator_test.py rename to letsencrypt_apache/tests/configurator_test.py index ae2097b3e..11b88f9e5 100644 --- a/letsencrypt/client/plugins/apache/tests/configurator_test.py +++ b/letsencrypt_apache/tests/configurator_test.py @@ -1,4 +1,4 @@ -"""Test for letsencrypt.client.plugins.apache.configurator.""" +"""Test for letsencrypt_apache.configurator.""" import os import re import shutil @@ -6,19 +6,19 @@ import unittest import mock -from letsencrypt.acme import challenges +from acme import challenges -from letsencrypt.client import achallenges -from letsencrypt.client import errors -from letsencrypt.client import le_util +from letsencrypt import achallenges +from letsencrypt import errors +from letsencrypt import le_util -from letsencrypt.client.plugins.apache import configurator -from letsencrypt.client.plugins.apache import obj -from letsencrypt.client.plugins.apache import parser +from letsencrypt.tests import acme_util -from letsencrypt.client.plugins.apache.tests import util +from letsencrypt_apache import configurator +from letsencrypt_apache import obj +from letsencrypt_apache import parser -from letsencrypt.client.tests import acme_util +from letsencrypt_apache.tests import util class TwoVhost80Test(util.ApacheTest): @@ -27,7 +27,7 @@ class TwoVhost80Test(util.ApacheTest): def setUp(self): super(TwoVhost80Test, self).setUp() - with mock.patch("letsencrypt.client.plugins.apache.configurator." + with mock.patch("letsencrypt_apache.configurator." "mod_loaded") as mock_load: mock_load.return_value = True self.config = util.get_apache_configurator( @@ -150,10 +150,8 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(len(self.config.vhosts), 5) - @mock.patch("letsencrypt.client.plugins.apache.configurator." - "dvsni.ApacheDvsni.perform") - @mock.patch("letsencrypt.client.plugins.apache.configurator." - "ApacheConfigurator.restart") + @mock.patch("letsencrypt_apache.configurator.dvsni.ApacheDvsni.perform") + @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") def test_perform(self, mock_restart, mock_dvsni_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded @@ -186,8 +184,7 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(mock_restart.call_count, 1) - @mock.patch("letsencrypt.client.plugins.apache.configurator." - "subprocess.Popen") + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") def test_get_version(self, mock_popen): mock_popen().communicate.return_value = ( "Server Version: Apache/2.4.2 (Debian)", "") @@ -213,4 +210,4 @@ class TwoVhost80Test(util.ApacheTest): if __name__ == "__main__": - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/client/plugins/apache/tests/dvsni_test.py b/letsencrypt_apache/tests/dvsni_test.py similarity index 87% rename from letsencrypt/client/plugins/apache/tests/dvsni_test.py rename to letsencrypt_apache/tests/dvsni_test.py index 2780749b5..321dce42c 100644 --- a/letsencrypt/client/plugins/apache/tests/dvsni_test.py +++ b/letsencrypt_apache/tests/dvsni_test.py @@ -1,20 +1,19 @@ -"""Test for letsencrypt.client.plugins.apache.dvsni.""" +"""Test for letsencrypt_apache.dvsni.""" import pkg_resources import unittest import shutil import mock -from letsencrypt.acme import challenges +from acme import challenges -from letsencrypt.client import achallenges -from letsencrypt.client import le_util +from letsencrypt import achallenges +from letsencrypt import le_util -from letsencrypt.client.plugins.apache.obj import Addr +from letsencrypt.tests import acme_util -from letsencrypt.client.plugins.apache.tests import util - -from letsencrypt.client.tests import acme_util +from letsencrypt_apache import obj +from letsencrypt_apache.tests import util class DvsniPerformTest(util.ApacheTest): @@ -23,20 +22,20 @@ class DvsniPerformTest(util.ApacheTest): def setUp(self): super(DvsniPerformTest, self).setUp() - with mock.patch("letsencrypt.client.plugins.apache.configurator." + with mock.patch("letsencrypt_apache.configurator." "mod_loaded") as mock_load: mock_load.return_value = True config = util.get_apache_configurator( self.config_path, self.config_dir, self.work_dir, self.ssl_options) - from letsencrypt.client.plugins.apache import dvsni + from letsencrypt_apache import dvsni self.sni = dvsni.ApacheDvsni(config) rsa256_file = pkg_resources.resource_filename( - "letsencrypt.acme.jose", "testdata/rsa256_key.pem") + "acme.jose", "testdata/rsa256_key.pem") rsa256_pem = pkg_resources.resource_string( - "letsencrypt.acme.jose", "testdata/rsa256_key.pem") + "acme.jose", "testdata/rsa256_key.pem") auth_key = le_util.Key(rsa256_file, rsa256_pem) self.achalls = [ @@ -80,8 +79,7 @@ class DvsniPerformTest(util.ApacheTest): nonce_domain=self.achalls[0].nonce_domain) achall.gen_cert_and_response.return_value = ("pem", response) - with mock.patch("letsencrypt.client.plugins.apache.dvsni.open", - m_open, create=True): + with mock.patch("letsencrypt_apache.dvsni.open", m_open, create=True): # pylint: disable=protected-access self.assertEqual(response, self.sni._setup_challenge_cert( achall, "randomS1")) @@ -142,8 +140,8 @@ class DvsniPerformTest(util.ApacheTest): def test_mod_config(self): for achall in self.achalls: self.sni.add_chall(achall) - v_addr1 = [Addr(("1.2.3.4", "443")), Addr(("5.6.7.8", "443"))] - v_addr2 = [Addr(("127.0.0.1", "443"))] + v_addr1 = [obj.Addr(("1.2.3.4", "443")), obj.Addr(("5.6.7.8", "443"))] + v_addr2 = [obj.Addr(("127.0.0.1", "443"))] ll_addr = [] ll_addr.append(v_addr1) ll_addr.append(v_addr2) @@ -173,4 +171,4 @@ class DvsniPerformTest(util.ApacheTest): if __name__ == "__main__": - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/client/plugins/apache/tests/obj_test.py b/letsencrypt_apache/tests/obj_test.py similarity index 81% rename from letsencrypt/client/plugins/apache/tests/obj_test.py rename to letsencrypt_apache/tests/obj_test.py index b0c65eadb..9387d6d63 100644 --- a/letsencrypt/client/plugins/apache/tests/obj_test.py +++ b/letsencrypt_apache/tests/obj_test.py @@ -1,11 +1,11 @@ -"""Test the helper objects in letsencrypt.client.plugins.apache.obj.""" +"""Test the helper objects in letsencrypt_apache.obj.""" import unittest class AddrTest(unittest.TestCase): """Test the Addr class.""" def setUp(self): - from letsencrypt.client.plugins.apache.obj import Addr + from letsencrypt_apache.obj import Addr self.addr1 = Addr.fromstring("192.168.1.1") self.addr2 = Addr.fromstring("192.168.1.1:*") self.addr3 = Addr.fromstring("192.168.1.1:80") @@ -34,7 +34,7 @@ class AddrTest(unittest.TestCase): self.assertFalse(self.addr1 == 3333) def test_set_inclusion(self): - from letsencrypt.client.plugins.apache.obj import Addr + from letsencrypt_apache.obj import Addr set_a = set([self.addr1, self.addr2]) addr1b = Addr.fromstring("192.168.1.1") addr2b = Addr.fromstring("192.168.1.1:*") @@ -46,15 +46,15 @@ class AddrTest(unittest.TestCase): class VirtualHostTest(unittest.TestCase): """Test the VirtualHost class.""" def setUp(self): - from letsencrypt.client.plugins.apache.obj import VirtualHost - from letsencrypt.client.plugins.apache.obj import Addr + from letsencrypt_apache.obj import VirtualHost + from letsencrypt_apache.obj import Addr self.vhost1 = VirtualHost( "filep", "vh_path", set([Addr.fromstring("localhost")]), False, False) def test_eq(self): - from letsencrypt.client.plugins.apache.obj import Addr - from letsencrypt.client.plugins.apache.obj import VirtualHost + from letsencrypt_apache.obj import Addr + from letsencrypt_apache.obj import VirtualHost vhost1b = VirtualHost( "filep", "vh_path", set([Addr.fromstring("localhost")]), False, False) @@ -65,4 +65,4 @@ class VirtualHostTest(unittest.TestCase): if __name__ == "__main__": - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/client/plugins/apache/tests/parser_test.py b/letsencrypt_apache/tests/parser_test.py similarity index 81% rename from letsencrypt/client/plugins/apache/tests/parser_test.py rename to letsencrypt_apache/tests/parser_test.py index 1696841f8..06bb20e2a 100644 --- a/letsencrypt/client/plugins/apache/tests/parser_test.py +++ b/letsencrypt_apache/tests/parser_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.client.plugins.apache.parser.""" +"""Tests for letsencrypt_apache.parser.""" import os import shutil import sys @@ -8,10 +8,10 @@ import augeas import mock import zope.component -from letsencrypt.client import errors -from letsencrypt.client.display import util as display_util +from letsencrypt import errors +from letsencrypt.display import util as display_util -from letsencrypt.client.plugins.apache.tests import util +from letsencrypt_apache.tests import util class ApacheParserTest(util.ApacheTest): @@ -22,7 +22,7 @@ class ApacheParserTest(util.ApacheTest): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) - from letsencrypt.client.plugins.apache.parser import ApacheParser + from letsencrypt_apache.parser import ApacheParser self.aug = augeas.Augeas(flags=augeas.Augeas.NONE) self.parser = ApacheParser(self.aug, self.config_path, self.ssl_options) @@ -32,19 +32,19 @@ class ApacheParserTest(util.ApacheTest): shutil.rmtree(self.work_dir) def test_root_normalized(self): - from letsencrypt.client.plugins.apache.parser import ApacheParser + from letsencrypt_apache.parser import ApacheParser path = os.path.join(self.temp_dir, "debian_apache_2_4/////" "two_vhost_80/../two_vhost_80/apache2") parser = ApacheParser(self.aug, path, None) self.assertEqual(parser.root, self.config_path) def test_root_absolute(self): - from letsencrypt.client.plugins.apache.parser import ApacheParser + from letsencrypt_apache.parser import ApacheParser parser = ApacheParser(self.aug, os.path.relpath(self.config_path), None) self.assertEqual(parser.root, self.config_path) def test_root_no_trailing_slash(self): - from letsencrypt.client.plugins.apache.parser import ApacheParser + from letsencrypt_apache.parser import ApacheParser parser = ApacheParser(self.aug, self.config_path + os.path.sep, None) self.assertEqual(parser.root, self.config_path) @@ -67,7 +67,7 @@ class ApacheParserTest(util.ApacheTest): self.assertTrue(matches) def test_find_dir(self): - from letsencrypt.client.plugins.apache.parser import case_i + from letsencrypt_apache.parser import case_i test = self.parser.find_dir(case_i("Listen"), "443") # This will only look in enabled hosts test2 = self.parser.find_dir(case_i("documentroot")) @@ -92,7 +92,7 @@ class ApacheParserTest(util.ApacheTest): Path must be valid before attempting to add to augeas """ - from letsencrypt.client.plugins.apache.parser import get_aug_path + from letsencrypt_apache.parser import get_aug_path self.parser.add_dir_to_ifmodssl( get_aug_path(self.parser.loc["default"]), "FakeDirective", "123") @@ -103,12 +103,11 @@ class ApacheParserTest(util.ApacheTest): self.assertTrue("IfModule" in matches[0]) def test_get_aug_path(self): - from letsencrypt.client.plugins.apache.parser import get_aug_path + from letsencrypt_apache.parser import get_aug_path self.assertEqual("/files/etc/apache", get_aug_path("/etc/apache")) def test_set_locations(self): - with mock.patch("letsencrypt.client.plugins.apache.parser." - "os.path") as mock_path: + with mock.patch("letsencrypt_apache.parser.os.path") as mock_path: mock_path.isfile.return_value = False @@ -126,4 +125,4 @@ class ApacheParserTest(util.ApacheTest): if __name__ == "__main__": - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/sites b/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/sites similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/default_vhost/sites rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/sites diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf diff --git a/letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/sites b/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/sites similarity index 100% rename from letsencrypt/client/plugins/apache/tests/testdata/debian_apache_2_4/two_vhost_80/sites rename to letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/sites diff --git a/letsencrypt/client/plugins/apache/tests/util.py b/letsencrypt_apache/tests/util.py similarity index 88% rename from letsencrypt/client/plugins/apache/tests/util.py rename to letsencrypt_apache/tests/util.py index 618b0975a..12e3a5140 100644 --- a/letsencrypt/client/plugins/apache/tests/util.py +++ b/letsencrypt_apache/tests/util.py @@ -1,4 +1,4 @@ -"""Common utilities for letsencrypt.client.plugins.apache.""" +"""Common utilities for letsencrypt_apache.""" import os import pkg_resources import shutil @@ -7,9 +7,9 @@ import unittest import mock -from letsencrypt.client.plugins.apache import configurator -from letsencrypt.client.plugins.apache import constants -from letsencrypt.client.plugins.apache import obj +from letsencrypt_apache import configurator +from letsencrypt_apache import constants +from letsencrypt_apache import obj class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods @@ -26,9 +26,9 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods self.temp_dir, "debian_apache_2_4/two_vhost_80/apache2") self.rsa256_file = pkg_resources.resource_filename( - "letsencrypt.acme.jose", "testdata/rsa256_key.pem") + "acme.jose", "testdata/rsa256_key.pem") self.rsa256_pem = pkg_resources.resource_string( - "letsencrypt.acme.jose", "testdata/rsa256_key.pem") + "acme.jose", "testdata/rsa256_key.pem") def dir_setup(test_dir="debian_apache_2_4/two_vhost_80"): @@ -38,7 +38,7 @@ def dir_setup(test_dir="debian_apache_2_4/two_vhost_80"): work_dir = tempfile.mkdtemp("work") test_configs = pkg_resources.resource_filename( - "letsencrypt.client.plugins.apache.tests", "testdata/%s" % test_dir) + "letsencrypt_apache.tests", "testdata/%s" % test_dir) shutil.copytree( test_configs, os.path.join(temp_dir, test_dir), symlinks=True) @@ -59,7 +59,7 @@ def get_apache_configurator( backups = os.path.join(work_dir, "backups") - with mock.patch("letsencrypt.client.plugins.apache.configurator." + with mock.patch("letsencrypt_apache.configurator." "subprocess.Popen") as mock_popen: # This just states that the ssl module is already loaded mock_popen().communicate.return_value = ("ssl_module", "") diff --git a/letsencrypt_nginx/__init__.py b/letsencrypt_nginx/__init__.py new file mode 100644 index 000000000..34db9673d --- /dev/null +++ b/letsencrypt_nginx/__init__.py @@ -0,0 +1 @@ +"""Let's Encrypt nginx plugin.""" diff --git a/letsencrypt/client/plugins/nginx/configurator.py b/letsencrypt_nginx/configurator.py similarity index 87% rename from letsencrypt/client/plugins/nginx/configurator.py rename to letsencrypt_nginx/configurator.py index 49d5a6dd0..ffb9bd3b2 100644 --- a/letsencrypt/client/plugins/nginx/configurator.py +++ b/letsencrypt_nginx/configurator.py @@ -9,41 +9,40 @@ import sys import zope.interface -from letsencrypt.acme import challenges +from acme import challenges -from letsencrypt.client import achallenges -from letsencrypt.client import constants as core_constants -from letsencrypt.client import errors -from letsencrypt.client import interfaces -from letsencrypt.client import le_util -from letsencrypt.client import reverter +from letsencrypt import achallenges +from letsencrypt import constants as core_constants +from letsencrypt import errors +from letsencrypt import interfaces +from letsencrypt import le_util +from letsencrypt import reverter -from letsencrypt.client.plugins import common +from letsencrypt.plugins import common -from letsencrypt.client.plugins.nginx import constants -from letsencrypt.client.plugins.nginx import dvsni -from letsencrypt.client.plugins.nginx import parser +from letsencrypt_nginx import constants +from letsencrypt_nginx import dvsni +from letsencrypt_nginx import obj +from letsencrypt_nginx import parser class NginxConfigurator(common.Plugin): # pylint: disable=too-many-instance-attributes,too-many-public-methods """Nginx configurator. - .. warning:: This plugin is a stub, does not support DVSNI yet! - .. todo:: Add proper support for comments in the config. Currently, config files modified by the configurator will lose all their comments. :ivar config: Configuration. - :type config: :class:`~letsencrypt.client.interfaces.IConfig` + :type config: :class:`~letsencrypt.interfaces.IConfig` :ivar parser: Handles low level parsing - :type parser: :class:`~letsencrypt.client.plugins.nginx.parser` + :type parser: :class:`~letsencrypt_nginx.parser` :ivar str save_notes: Human-readable config change notes :ivar reverter: saves and reverts checkpoints - :type reverter: :class:`letsencrypt.client.reverter.Reverter` + :type reverter: :class:`letsencrypt.reverter.Reverter` :ivar tup version: version of Nginx @@ -166,15 +165,19 @@ class NginxConfigurator(common.Plugin): :param str target_name: domain name :returns: ssl vhost associated with name - :rtype: :class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost` + :rtype: :class:`~letsencrypt_nginx.obj.VirtualHost` """ vhost = None matches = self._get_ranked_matches(target_name) if not matches: - # No matches at all :'( - pass + # No matches. Create a new vhost with this name in nginx.conf. + filep = self.parser.loc["root"] + new_block = [['server'], [['server_name', target_name]]] + self.parser.add_http_directives(filep, new_block) + vhost = obj.VirtualHost(filep, set([]), False, True, + set([target_name]), list(new_block[1])) elif matches[0]['rank'] in xrange(2, 6): # Wildcard match - need to find the longest one rank = matches[0]['rank'] @@ -185,7 +188,7 @@ class NginxConfigurator(common.Plugin): if vhost is not None: if not vhost.ssl: - self._make_server_ssl(vhost.filep, vhost.names) + self._make_server_ssl(vhost) return vhost @@ -260,23 +263,28 @@ class NginxConfigurator(common.Plugin): return all_names - def _make_server_ssl(self, filename, names): + def _make_server_ssl(self, vhost): """Makes a server SSL based on server_name and filename by adding a 'listen 443 ssl' directive to the server block. .. todo:: Maybe this should create a new block instead of modifying - the existing one? + the existing one? - :param str filename: The absolute filename of the config file. - :param set names: The server names of the block to add SSL in + :param vhost: The vhost to add SSL to. + :type vhost: :class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost` """ + ssl_block = [['listen', '443 ssl'], + ['ssl_certificate', + '/etc/ssl/certs/ssl-cert-snakeoil.pem'], + ['ssl_certificate_key', + '/etc/ssl/private/ssl-cert-snakeoil.key'], + ['include', self.parser.loc["ssl_options"]]] self.parser.add_server_directives( - filename, names, - [['listen', '443 ssl'], - ['ssl_certificate', '/etc/ssl/certs/ssl-cert-snakeoil.pem'], - ['ssl_certificate_key', '/etc/ssl/private/ssl-cert-snakeoil.key'], - ['include', self.parser.loc["ssl_options"]]]) + vhost.filep, vhost.names, ssl_block) + vhost.ssl = True + vhost.raw.extend(ssl_block) + vhost.addrs.add(obj.Addr('', '443', True, False)) def get_all_certs_keys(self): """Find all existing keys, certs from configuration. @@ -302,9 +310,9 @@ class NginxConfigurator(common.Plugin): :param str domain: domain to enhance :param str enhancement: enhancement type defined in - :const:`~letsencrypt.client.constants.ENHANCEMENTS` + :const:`~letsencrypt.constants.ENHANCEMENTS` :param options: options for the enhancement - See :const:`~letsencrypt.client.constants.ENHANCEMENTS` + See :const:`~letsencrypt.constants.ENHANCEMENTS` documentation for appropriate parameter. """ @@ -414,10 +422,11 @@ class NginxConfigurator(common.Plugin): nginx_version = tuple([int(i) for i in version_matches[0].split(".")]) - # nginx < 0.8.21 doesn't use default_server - if nginx_version < (0, 8, 21): + # nginx < 0.8.48 uses machine hostname as default server_name instead of + # the empty string + if nginx_version < (0, 8, 48): raise errors.LetsEncryptConfiguratorError( - "Nginx version must be 0.8.21+") + "Nginx version must be 0.8.48+") return nginx_version @@ -543,6 +552,10 @@ class NginxConfigurator(common.Plugin): def nginx_restart(nginx_ctl): """Restarts the Nginx Server. + .. todo:: Nginx restart is fatal if the configuration references + non-existent SSL cert/key files. Remove references to /etc/letsencrypt + before restart. + :param str nginx_ctl: Path to the Nginx binary. """ @@ -553,11 +566,18 @@ def nginx_restart(nginx_ctl): stdout, stderr = proc.communicate() if proc.returncode != 0: - # Enter recovery routine... - logging.error("Nginx Restart Failed!") - logging.error(stdout) - logging.error(stderr) - return False + # Maybe Nginx isn't running + nginx_proc = subprocess.Popen([nginx_ctl], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = nginx_proc.communicate() + + if nginx_proc.returncode != 0: + # Enter recovery routine... + logging.error("Nginx Restart Failed!") + logging.error(stdout) + logging.error(stderr) + return False except (OSError, ValueError): logging.fatal( diff --git a/letsencrypt/client/plugins/nginx/constants.py b/letsencrypt_nginx/constants.py similarity index 84% rename from letsencrypt/client/plugins/nginx/constants.py rename to letsencrypt_nginx/constants.py index 17d05f438..6c15b1664 100644 --- a/letsencrypt/client/plugins/nginx/constants.py +++ b/letsencrypt_nginx/constants.py @@ -11,6 +11,6 @@ CLI_DEFAULTS = dict( MOD_SSL_CONF = pkg_resources.resource_filename( - "letsencrypt.client.plugins.nginx", "options-ssl.conf") + "letsencrypt_nginx", "options-ssl.conf") """Path to the Nginx mod_ssl config file found in the Let's Encrypt distribution.""" diff --git a/letsencrypt_nginx/dvsni.py b/letsencrypt_nginx/dvsni.py new file mode 100644 index 000000000..534c5a8d3 --- /dev/null +++ b/letsencrypt_nginx/dvsni.py @@ -0,0 +1,138 @@ +"""NginxDVSNI""" +import itertools +import logging +import os + +from letsencrypt import errors + +from letsencrypt_apache.dvsni import ApacheDvsni + +from letsencrypt_nginx import obj +from letsencrypt_nginx import nginxparser + + +class NginxDvsni(ApacheDvsni): + """Class performs DVSNI challenges within the Nginx configurator. + + :ivar configurator: NginxConfigurator object + :type configurator: :class:`~nginx.configurator.NginxConfigurator` + + :ivar list achalls: Annotated :class:`~letsencrypt.achallenges.DVSNI` + challenges. + + :param list indices: Meant to hold indices of challenges in a + larger array. NginxDvsni is capable of solving many challenges + at once which causes an indexing issue within NginxConfigurator + who must return all responses in order. Imagine NginxConfigurator + maintaining state about where all of the SimpleHTTPS Challenges, + Dvsni Challenges belong in the response array. This is an optional + utility. + + :param str challenge_conf: location of the challenge config file + + """ + + def perform(self): + """Perform a DVSNI challenge on Nginx. + + :returns: list of :class:`letsencrypt.acme.challenges.DVSNIResponse` + :rtype: list + + """ + if not self.achalls: + return [] + + self.configurator.save() + + addresses = [] + default_addr = "443 default_server ssl" + + for achall in self.achalls: + vhost = self.configurator.choose_vhost(achall.domain) + if vhost is None: + logging.error( + "No nginx vhost exists with server_name matching: %s", + achall.domain) + logging.error("Please specify server_names in the Nginx config") + return None + + for addr in vhost.addrs: + if addr.default: + addresses.append([obj.Addr.fromstring(default_addr)]) + break + else: + addresses.append(list(vhost.addrs)) + + # Create challenge certs + responses = [self._setup_challenge_cert(x) for x in self.achalls] + + # Set up the configuration + self._mod_config(addresses) + + # Save reversible changes + self.configurator.save("SNI Challenge", True) + + return responses + + def _mod_config(self, ll_addrs): + """Modifies Nginx config to include challenge server blocks. + + :param list ll_addrs: list of lists of + :class:`letsencrypt.client.plugins.apache.obj.Addr` to apply + + :raises errors.LetsEncryptMisconfigurationError: + Unable to find a suitable HTTP block to include DVSNI hosts. + + """ + # Add the 'include' statement for the challenges if it doesn't exist + # already in the main config + included = False + directive = ['include', self.challenge_conf] + root = self.configurator.parser.loc["root"] + main = self.configurator.parser.parsed[root] + for entry in main: + if entry[0] == ['http']: + body = entry[1] + if directive not in body: + body.append(directive) + included = True + break + if not included: + raise errors.LetsEncryptMisconfigurationError( + 'LetsEncrypt could not find an HTTP block to include DVSNI ' + 'challenges in %s.' % root) + + config = [self._make_server_block(pair[0], pair[1]) + for pair in itertools.izip(self.achalls, ll_addrs)] + + self.configurator.reverter.register_file_creation( + True, self.challenge_conf) + + with open(self.challenge_conf, "w") as new_conf: + nginxparser.dump(config, new_conf) + + def _make_server_block(self, achall, addrs): + """Creates a server block for a DVSNI challenge. + + :param achall: Annotated DVSNI challenge. + :type achall: :class:`letsencrypt.client.achallenges.DVSNI` + + :param list addrs: addresses of challenged domain + :class:`list` of type :class:`~nginx.obj.Addr` + + :returns: server block for the challenge host + :rtype: list + + """ + document_root = os.path.join( + self.configurator.config.config_dir, "dvsni_page") + + block = [['listen', str(addr)] for addr in addrs] + + block.extend([['server_name', achall.nonce_domain], + ['include', self.configurator.parser.loc["ssl_options"]], + ['ssl_certificate', self.get_cert_file(achall)], + ['ssl_certificate_key', achall.key.file], + [['location', '/'], [['root', document_root]]]]) + + return [['server'], block] diff --git a/letsencrypt/client/plugins/nginx/nginxparser.py b/letsencrypt_nginx/nginxparser.py similarity index 100% rename from letsencrypt/client/plugins/nginx/nginxparser.py rename to letsencrypt_nginx/nginxparser.py diff --git a/letsencrypt/client/plugins/nginx/obj.py b/letsencrypt_nginx/obj.py similarity index 91% rename from letsencrypt/client/plugins/nginx/obj.py rename to letsencrypt_nginx/obj.py index acaacb3b0..9b3fa2e42 100644 --- a/letsencrypt/client/plugins/nginx/obj.py +++ b/letsencrypt_nginx/obj.py @@ -1,7 +1,7 @@ """Module contains classes used by the Nginx Configurator.""" import re -from letsencrypt.client.plugins.apache.obj import Addr as ApacheAddr +from letsencrypt_apache.obj import Addr as ApacheAddr class Addr(ApacheAddr): @@ -67,12 +67,20 @@ class Addr(ApacheAddr): return cls(host, port, ssl, default) def __str__(self): + parts = '' if self.tup[0] and self.tup[1]: - return "%s:%s" % self.tup + parts = "%s:%s" % self.tup elif self.tup[0]: - return self.tup[0] + parts = self.tup[0] else: - return self.tup[1] + parts = self.tup[1] + + if self.default: + parts += ' default_server' + if self.ssl: + parts += ' ssl' + + return parts def __eq__(self, other): if isinstance(other, self.__class__): @@ -89,7 +97,7 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods :ivar set addrs: Virtual Host addresses (:class:`set` of :class:`Addr`) :ivar set names: Server names/aliases of vhost (:class:`list` of :class:`str`) - :ivar array raw: The raw form of the parsed server block + :ivar list raw: The raw form of the parsed server block :ivar bool ssl: SSLEngine on in vhost :ivar bool enabled: Virtual host is enabled diff --git a/letsencrypt/client/plugins/nginx/options-ssl.conf b/letsencrypt_nginx/options-ssl.conf similarity index 100% rename from letsencrypt/client/plugins/nginx/options-ssl.conf rename to letsencrypt_nginx/options-ssl.conf diff --git a/letsencrypt/client/plugins/nginx/parser.py b/letsencrypt_nginx/parser.py similarity index 88% rename from letsencrypt/client/plugins/nginx/parser.py rename to letsencrypt_nginx/parser.py index 55a0b01e8..b25471ef3 100644 --- a/letsencrypt/client/plugins/nginx/parser.py +++ b/letsencrypt_nginx/parser.py @@ -5,9 +5,10 @@ import os import pyparsing import re -from letsencrypt.client import errors -from letsencrypt.client.plugins.nginx import obj -from letsencrypt.client.plugins.nginx.nginxparser import dump, load +from letsencrypt import errors + +from letsencrypt_nginx import obj +from letsencrypt_nginx import nginxparser class NginxParser(object): @@ -33,6 +34,7 @@ class NginxParser(object): """Loads Nginx files into a parsed tree. """ + self.parsed = {} self._parse_recursively(self.loc["root"]) def _parse_recursively(self, filepath): @@ -40,9 +42,6 @@ class NginxParser(object): directives inside 'http' and 'server' blocks. Note that this only reads Nginx files that potentially declare a virtual host. - .. todo:: Can Nginx 'virtual hosts' be defined somewhere other than in - the server context? - :param str filepath: The path to the files to parse, as a glob """ @@ -85,9 +84,8 @@ class NginxParser(object): Technically this is a misnomer because Nginx does not have virtual hosts, it has 'server blocks'. - :returns: List of - :class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost` objects - found in configuration + :returns: List of :class:`~letsencrypt_nginx.obj.VirtualHost` + objects found in configuration :rtype: list """ @@ -159,13 +157,13 @@ class NginxParser(object): continue try: with open(item) as _file: - parsed = load(_file) + parsed = nginxparser.load(_file) self.parsed[item] = parsed trees.append(parsed) except IOError: logging.warn("Could not open file: %s", item) except pyparsing.ParseException: - logging.warn("Could not parse file: %s", item) + logging.debug("Could not parse file: %s", item) return trees def _set_locations(self, ssl_options): @@ -213,7 +211,7 @@ class NginxParser(object): filename = filename + os.path.extsep + ext try: with open(filename, 'w') as _file: - dump(tree, _file) + nginxparser.dump(tree, _file) except IOError: logging.error("Could not open file for writing: %s", filename) @@ -252,8 +250,9 @@ class NginxParser(object): def add_server_directives(self, filename, names, directives, replace=False): - """Add or replace directives in server blocks whose server_name set - is 'names'. If replace is True, this raises a misconfiguration error + """Add or replace directives in the first server block with names. + + ..note :: If replace is True, this raises a misconfiguration error if the directive does not already exist. ..todo :: Doesn't match server blocks whose server_name directives are @@ -265,14 +264,20 @@ class NginxParser(object): :param bool replace: Whether to only replace existing directives """ - if replace: - _do_for_subarray(self.parsed[filename], - lambda x: self._has_server_names(x, names), - lambda x: _replace_directives(x, directives)) - else: - _do_for_subarray(self.parsed[filename], - lambda x: self._has_server_names(x, names), - lambda x: x.extend(directives)) + _do_for_subarray(self.parsed[filename], + lambda x: self._has_server_names(x, names), + lambda x: _add_directives(x, directives, replace)) + + def add_http_directives(self, filename, directives): + """Adds directives to the first encountered HTTP block in filename. + + :param str filename: The absolute filename of the config file + :param list directives: The directives to add + + """ + _do_for_subarray(self.parsed[filename], + lambda x: x[0] == ['http'], + lambda x: _add_directives(x[1], [directives], False)) def get_all_certs_keys(self): """Gets all certs and keys in the nginx config. @@ -461,24 +466,28 @@ def _parse_server(server): return parsed_server -def _replace_directives(block, directives): - """Replaces directives in a block. If the directive doesn't exist in +def _add_directives(block, directives, replace=False): + """Adds or replaces directives in a block. If the directive doesn't exist in the entry already, raises a misconfiguration error. ..todo :: Find directives that are in included files. :param list block: The block to replace in :param list directives: The new directives. + """ - for directive in directives: - changed = False - if len(directive) == 0: - continue - for index, line in enumerate(block): - if len(line) > 0 and line[0] == directive[0]: - block[index] = directive - changed = True - if not changed: - raise errors.LetsEncryptMisconfigurationError( - 'LetsEncrypt expected directive for %s in the Nginx config ' - 'but did not find it.' % directive[0]) + if replace: + for directive in directives: + changed = False + if len(directive) == 0: + continue + for index, line in enumerate(block): + if len(line) > 0 and line[0] == directive[0]: + block[index] = directive + changed = True + if not changed: + raise errors.LetsEncryptMisconfigurationError( + 'LetsEncrypt expected directive for %s in the Nginx ' + 'config but did not find it.' % directive[0]) + else: + block.extend(directives) diff --git a/letsencrypt/client/plugins/nginx/tests/__init__.py b/letsencrypt_nginx/tests/__init__.py similarity index 100% rename from letsencrypt/client/plugins/nginx/tests/__init__.py rename to letsencrypt_nginx/tests/__init__.py diff --git a/letsencrypt/client/plugins/nginx/tests/configurator_test.py b/letsencrypt_nginx/tests/configurator_test.py similarity index 89% rename from letsencrypt/client/plugins/nginx/tests/configurator_test.py rename to letsencrypt_nginx/tests/configurator_test.py index cb5fef6bf..82b80b9d2 100644 --- a/letsencrypt/client/plugins/nginx/tests/configurator_test.py +++ b/letsencrypt_nginx/tests/configurator_test.py @@ -1,17 +1,17 @@ -"""Test for letsencrypt.client.plugins.nginx.configurator.""" +"""Test for letsencrypt_nginx.configurator.""" import shutil import unittest import mock -from letsencrypt.acme import challenges -from letsencrypt.acme import messages2 +from acme import challenges +from acme import messages2 -from letsencrypt.client import achallenges -from letsencrypt.client import errors -from letsencrypt.client import le_util +from letsencrypt import achallenges +from letsencrypt import errors +from letsencrypt import le_util -from letsencrypt.client.plugins.nginx.tests import util +from letsencrypt_nginx.tests import util class NginxConfiguratorTest(util.NginxTest): @@ -91,7 +91,7 @@ class NginxConfiguratorTest(util.NginxTest): self.assertEqual(results[name], self.config.choose_vhost(name).names) for name in bad_results: - self.assertEqual(None, self.config.choose_vhost(name)) + self.assertEqual(set([name]), self.config.choose_vhost(name).names) def test_more_info(self): self.assertTrue('nginx.conf' in self.config.more_info()) @@ -158,10 +158,8 @@ class NginxConfiguratorTest(util.NginxTest): ('/etc/nginx/cert.pem', '/etc/nginx/key.pem', nginx_conf), ]), self.config.get_all_certs_keys()) - @mock.patch("letsencrypt.client.plugins.nginx.configurator." - "dvsni.NginxDvsni.perform") - @mock.patch("letsencrypt.client.plugins.nginx.configurator." - "NginxConfigurator.restart") + @mock.patch("letsencrypt_nginx.configurator.dvsni.NginxDvsni.perform") + @mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.restart") def test_perform(self, mock_restart, mock_dvsni_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded @@ -195,8 +193,7 @@ class NginxConfiguratorTest(util.NginxTest): self.assertEqual(responses, dvsni_ret_val) self.assertEqual(mock_restart.call_count, 1) - @mock.patch("letsencrypt.client.plugins.nginx.configurator." - "subprocess.Popen") + @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") def test_get_version(self, mock_popen): mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.4.2", @@ -251,21 +248,32 @@ class NginxConfiguratorTest(util.NginxTest): self.assertRaises( errors.LetsEncryptConfiguratorError, self.config.get_version) - @mock.patch("letsencrypt.client.plugins.nginx.configurator." - "subprocess.Popen") + @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") def test_nginx_restart(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 0 self.assertTrue(self.config.restart()) - @mock.patch("letsencrypt.client.plugins.nginx.configurator." - "subprocess.Popen") + @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + def test_nginx_restart_fail(self, mock_popen): + mocked = mock_popen() + mocked.communicate.return_value = ('', '') + mocked.returncode = 1 + self.assertFalse(self.config.restart()) + + @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + def test_no_nginx_start(self, mock_popen): + mock_popen.side_effect = OSError("Can't find program") + self.assertRaises(SystemExit, self.config.restart) + + @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") def test_config_test(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 0 self.assertTrue(self.config.config_test()) + if __name__ == "__main__": - unittest.main() + unittest.main() # pragma: no cover diff --git a/letsencrypt_nginx/tests/dvsni_test.py b/letsencrypt_nginx/tests/dvsni_test.py new file mode 100644 index 000000000..1ea7793cc --- /dev/null +++ b/letsencrypt_nginx/tests/dvsni_test.py @@ -0,0 +1,176 @@ +"""Test for letsencrypt_nginx.dvsni.""" +import pkg_resources +import unittest +import shutil + +import mock + +from acme import challenges + +from letsencrypt import achallenges +from letsencrypt import errors +from letsencrypt import le_util +from letsencrypt.tests import acme_util + +from letsencrypt_nginx import obj +from letsencrypt_nginx.tests import util + + +class DvsniPerformTest(util.NginxTest): + """Test the NginxDVSNI challenge.""" + + def setUp(self): + super(DvsniPerformTest, self).setUp() + + config = util.get_nginx_configurator( + self.config_path, self.config_dir, self.work_dir, + self.ssl_options) + + rsa256_file = pkg_resources.resource_filename( + "acme.jose", "testdata/rsa256_key.pem") + rsa256_pem = pkg_resources.resource_string( + "acme.jose", "testdata/rsa256_key.pem") + + auth_key = le_util.Key(rsa256_file, rsa256_pem) + + from letsencrypt_nginx import dvsni + self.sni = dvsni.NginxDvsni(config) + + self.achalls = [ + achallenges.DVSNI( + challb=acme_util.chall_to_challb( + challenges.DVSNI( + r="foo", + nonce="bar" + ), "pending"), + domain="www.example.com", key=auth_key), + achallenges.DVSNI( + challb=acme_util.chall_to_challb( + challenges.DVSNI( + r="\xba\xa9\xda?