From 7f5abba83e809c23d74704dce68047852ac09109 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Thu, 11 Jun 2015 16:04:46 +0000 Subject: [PATCH 1/4] Rename SimpleHTTPS to SimpleHTTP. --- acme/challenges.py | 14 +++++------ acme/challenges_test.py | 32 +++++++++++++------------- acme/messages2_test.py | 2 +- acme/messages_test.py | 6 ++--- acme/schemata/challengeobject.json | 2 +- acme/schemata/responseobject.json | 2 +- letsencrypt/achallenges.py | 6 ++--- letsencrypt/auth_handler.py | 6 ++--- letsencrypt/constants.py | 2 +- letsencrypt/tests/acme_util.py | 2 +- letsencrypt/tests/auth_handler_test.py | 10 ++++---- letsencrypt_apache/dvsni.py | 2 +- letsencrypt_nginx/dvsni.py | 2 +- 13 files changed, 44 insertions(+), 44 deletions(-) diff --git a/acme/challenges.py b/acme/challenges.py index 11a1c9a60..36c29d6c6 100644 --- a/acme/challenges.py +++ b/acme/challenges.py @@ -42,16 +42,16 @@ class ChallengeResponse(jose.TypedJSONObjectWithFields): @Challenge.register -class SimpleHTTPS(DVChallenge): - """ACME "simpleHttps" challenge.""" - typ = "simpleHttps" +class SimpleHTTP(DVChallenge): + """ACME "simpleHttp" challenge.""" + typ = "simpleHttp" token = jose.Field("token") @ChallengeResponse.register -class SimpleHTTPSResponse(ChallengeResponse): - """ACME "simpleHttps" challenge response.""" - typ = "simpleHttps" +class SimpleHTTPResponse(ChallengeResponse): + """ACME "simpleHttp" challenge response.""" + typ = "simpleHttp" path = jose.Field("path") URI_TEMPLATE = "https://{domain}/.well-known/acme-challenge/{path}" @@ -61,7 +61,7 @@ class SimpleHTTPSResponse(ChallengeResponse): """Create an URI to the provisioned resource. Forms an URI to the HTTPS server provisioned resource (containing - :attr:`~SimpleHTTPS.token`) by populating the :attr:`URI_TEMPLATE`. + :attr:`~SimpleHTTP.token`) by populating the :attr:`URI_TEMPLATE`. :param str domain: Domain name being verified. diff --git a/acme/challenges_test.py b/acme/challenges_test.py index 0669dd581..7018b8e2e 100644 --- a/acme/challenges_test.py +++ b/acme/challenges_test.py @@ -18,14 +18,14 @@ KEY = jose.HashableRSAKey(Crypto.PublicKey.RSA.importKey( 'acme.jose', os.path.join('testdata', 'rsa512_key.pem')))) -class SimpleHTTPSTest(unittest.TestCase): +class SimpleHTTPTest(unittest.TestCase): def setUp(self): - from acme.challenges import SimpleHTTPS - self.msg = SimpleHTTPS( + from acme.challenges import SimpleHTTP + self.msg = SimpleHTTP( token='evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA') self.jmsg = { - 'type': 'simpleHttps', + 'type': 'simpleHttp', 'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA', } @@ -33,21 +33,21 @@ class SimpleHTTPSTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from acme.challenges import SimpleHTTPS - self.assertEqual(self.msg, SimpleHTTPS.from_json(self.jmsg)) + from acme.challenges import SimpleHTTP + self.assertEqual(self.msg, SimpleHTTP.from_json(self.jmsg)) def test_from_json_hashable(self): - from acme.challenges import SimpleHTTPS - hash(SimpleHTTPS.from_json(self.jmsg)) + from acme.challenges import SimpleHTTP + hash(SimpleHTTP.from_json(self.jmsg)) -class SimpleHTTPSResponseTest(unittest.TestCase): +class SimpleHTTPResponseTest(unittest.TestCase): def setUp(self): - from acme.challenges import SimpleHTTPSResponse - self.msg = SimpleHTTPSResponse(path='6tbIMBC5Anhl5bOlWT5ZFA') + from acme.challenges import SimpleHTTPResponse + self.msg = SimpleHTTPResponse(path='6tbIMBC5Anhl5bOlWT5ZFA') self.jmsg = { - 'type': 'simpleHttps', + 'type': 'simpleHttp', 'path': '6tbIMBC5Anhl5bOlWT5ZFA', } @@ -59,13 +59,13 @@ class SimpleHTTPSResponseTest(unittest.TestCase): self.assertEqual(self.jmsg, self.msg.to_partial_json()) def test_from_json(self): - from acme.challenges import SimpleHTTPSResponse + from acme.challenges import SimpleHTTPResponse self.assertEqual( - self.msg, SimpleHTTPSResponse.from_json(self.jmsg)) + self.msg, SimpleHTTPResponse.from_json(self.jmsg)) def test_from_json_hashable(self): - from acme.challenges import SimpleHTTPSResponse - hash(SimpleHTTPSResponse.from_json(self.jmsg)) + from acme.challenges import SimpleHTTPResponse + hash(SimpleHTTPResponse.from_json(self.jmsg)) class DVSNITest(unittest.TestCase): diff --git a/acme/messages2_test.py b/acme/messages2_test.py index c1521e2c3..72ffc954a 100644 --- a/acme/messages2_test.py +++ b/acme/messages2_test.py @@ -183,7 +183,7 @@ class AuthorizationTest(unittest.TestCase): self.challbs = ( ChallengeBody( uri='http://challb1', status=STATUS_VALID, - chall=challenges.SimpleHTTPS(token='IlirfxKKXAsHtmzK29Pj8A')), + chall=challenges.SimpleHTTP(token='IlirfxKKXAsHtmzK29Pj8A')), ChallengeBody(uri='http://challb2', status=STATUS_VALID, chall=challenges.DNS(token='DGyRejmCefe7v4NfDGDKfA')), ChallengeBody(uri='http://challb3', status=STATUS_VALID, diff --git a/acme/messages_test.py b/acme/messages_test.py index 4e0823085..baff2a21a 100644 --- a/acme/messages_test.py +++ b/acme/messages_test.py @@ -63,7 +63,7 @@ class ChallengeTest(unittest.TestCase): def setUp(self): challs = ( - challenges.SimpleHTTPS(token='IlirfxKKXAsHtmzK29Pj8A'), + challenges.SimpleHTTP(token='IlirfxKKXAsHtmzK29Pj8A'), challenges.DNS(token='DGyRejmCefe7v4NfDGDKfA'), challenges.RecoveryToken(), ) @@ -94,7 +94,7 @@ class ChallengeTest(unittest.TestCase): def test_resolved_combinations(self): self.assertEqual(self.msg.resolved_combinations, ( ( - challenges.SimpleHTTPS(token='IlirfxKKXAsHtmzK29Pj8A'), + challenges.SimpleHTTP(token='IlirfxKKXAsHtmzK29Pj8A'), challenges.RecoveryToken() ), ( @@ -183,7 +183,7 @@ class AuthorizationRequestTest(unittest.TestCase): def setUp(self): self.responses = ( - challenges.SimpleHTTPSResponse(path='Hf5GrX4Q7EBax9hc2jJnfw'), + challenges.SimpleHTTPResponse(path='Hf5GrX4Q7EBax9hc2jJnfw'), None, # null challenges.RecoveryTokenResponse(token='23029d88d9e123e'), ) diff --git a/acme/schemata/challengeobject.json b/acme/schemata/challengeobject.json index 5641b407e..7709f315d 100644 --- a/acme/schemata/challengeobject.json +++ b/acme/schemata/challengeobject.json @@ -7,7 +7,7 @@ "required": ["type", "token"], "properties": { "type": { - "enum" : [ "simpleHttps" ] + "enum" : [ "simpleHttp" ] }, "token": { "type": "string" diff --git a/acme/schemata/responseobject.json b/acme/schemata/responseobject.json index 5ca6babf1..5773f3a73 100644 --- a/acme/schemata/responseobject.json +++ b/acme/schemata/responseobject.json @@ -7,7 +7,7 @@ "required": ["type", "path"], "properties": { "type": { - "enum" : [ "simpleHttps" ] + "enum" : [ "simpleHttp" ] }, "path": { "type": "string" diff --git a/letsencrypt/achallenges.py b/letsencrypt/achallenges.py index 77e362f22..46ef167e0 100644 --- a/letsencrypt/achallenges.py +++ b/letsencrypt/achallenges.py @@ -62,10 +62,10 @@ class DVSNI(AnnotatedChallenge): return cert_pem, response -class SimpleHTTPS(AnnotatedChallenge): - """Client annotated "simpleHttps" ACME challenge.""" +class SimpleHTTP(AnnotatedChallenge): + """Client annotated "simpleHttp" ACME challenge.""" __slots__ = ('challb', 'domain', 'key') - acme_type = challenges.SimpleHTTPS + acme_type = challenges.SimpleHTTP class DNS(AnnotatedChallenge): diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 37d818dbe..5f9d29e6e 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -336,9 +336,9 @@ def challb_to_achall(challb, key, domain): logging.info(" DVSNI challenge for %s.", domain) return achallenges.DVSNI( challb=challb, domain=domain, key=key) - elif isinstance(chall, challenges.SimpleHTTPS): - logging.info(" SimpleHTTPS challenge for %s.", domain) - return achallenges.SimpleHTTPS( + elif isinstance(chall, challenges.SimpleHTTP): + logging.info(" SimpleHTTP challenge for %s.", domain) + return achallenges.SimpleHTTP( challb=challb, domain=domain, key=key) elif isinstance(chall, challenges.DNS): logging.info(" DNS challenge for %s.", domain) diff --git a/letsencrypt/constants.py b/letsencrypt/constants.py index dacbe9040..47539615d 100644 --- a/letsencrypt/constants.py +++ b/letsencrypt/constants.py @@ -41,7 +41,7 @@ RENEWER_DEFAULTS = dict( EXCLUSIVE_CHALLENGES = frozenset([frozenset([ - challenges.DVSNI, challenges.SimpleHTTPS])]) + challenges.DVSNI, challenges.SimpleHTTP])]) """Mutually exclusive challenges.""" diff --git a/letsencrypt/tests/acme_util.py b/letsencrypt/tests/acme_util.py index 8780e8095..51bb3cfbb 100644 --- a/letsencrypt/tests/acme_util.py +++ b/letsencrypt/tests/acme_util.py @@ -16,7 +16,7 @@ KEY = jose.HashableRSAKey(Crypto.PublicKey.RSA.importKey( "acme.jose", os.path.join("testdata", "rsa512_key.pem")))) # Challenges -SIMPLE_HTTPS = challenges.SimpleHTTPS( +SIMPLE_HTTPS = challenges.SimpleHTTP( token="evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA") DVSNI = challenges.DVSNI( r="O*\xb4-\xad\xec\x95>\xed\xa9\r0\x94\xe8\x97\x9c&6\xbf'\xb3" diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 85bcfe8cf..d7fd2c093 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -17,7 +17,7 @@ from letsencrypt.tests import acme_util TRANSLATE = { "dvsni": "DVSNI", - "simpleHttps": "SimpleHTTPS", + "simpleHttp": "SimpleHTTP", "dns": "DNS", "recoveryToken": "RecoveryToken", "recoveryContact": "RecoveryContact", @@ -299,7 +299,7 @@ class GenChallengePathTest(unittest.TestCase): return gen_challenge_path(challbs, preferences, combinations) def test_common_case(self): - """Given DVSNI and SimpleHTTPS with appropriate combos.""" + """Given DVSNI and SimpleHTTP with appropriate combos.""" challbs = (acme_util.DVSNI_P, acme_util.SIMPLE_HTTPS_P) prefs = [challenges.DVSNI] combos = ((0,), (1,)) @@ -334,7 +334,7 @@ class GenChallengePathTest(unittest.TestCase): # Attempted to make the order realistic prefs = [challenges.RecoveryToken, challenges.ProofOfPossession, - challenges.SimpleHTTPS, + challenges.SimpleHTTP, challenges.DVSNI, challenges.RecoveryContact] combos = acme_util.gen_combos(challbs) @@ -403,8 +403,8 @@ class IsPreferredTest(unittest.TestCase): def _call(cls, chall, satisfied): from letsencrypt.auth_handler import is_preferred return is_preferred(chall, satisfied, exclusive_groups=frozenset([ - frozenset([challenges.DVSNI, challenges.SimpleHTTPS]), - frozenset([challenges.DNS, challenges.SimpleHTTPS]), + frozenset([challenges.DVSNI, challenges.SimpleHTTP]), + frozenset([challenges.DNS, challenges.SimpleHTTP]), ])) def test_empty_satisfied(self): diff --git a/letsencrypt_apache/dvsni.py b/letsencrypt_apache/dvsni.py index ed7a216bb..6865afe26 100644 --- a/letsencrypt_apache/dvsni.py +++ b/letsencrypt_apache/dvsni.py @@ -18,7 +18,7 @@ class ApacheDvsni(object): larger array. ApacheDvsni is capable of solving many challenges at once which causes an indexing issue within ApacheConfigurator who must return all responses in order. Imagine ApacheConfigurator - maintaining state about where all of the SimpleHTTPS Challenges, + maintaining state about where all of the SimpleHTTP Challenges, Dvsni Challenges belong in the response array. This is an optional utility. diff --git a/letsencrypt_nginx/dvsni.py b/letsencrypt_nginx/dvsni.py index 5c188099c..3792230d3 100644 --- a/letsencrypt_nginx/dvsni.py +++ b/letsencrypt_nginx/dvsni.py @@ -24,7 +24,7 @@ class NginxDvsni(ApacheDvsni): 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, + maintaining state about where all of the SimpleHTTP Challenges, Dvsni Challenges belong in the response array. This is an optional utility. From bc9373929a4c0f21cf855eaffadfeb7b465b1d2e Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Thu, 11 Jun 2015 16:19:41 +0000 Subject: [PATCH 2/4] Add SimpleHTTP.tls --- acme/challenges.py | 1 + 1 file changed, 1 insertion(+) diff --git a/acme/challenges.py b/acme/challenges.py index 36c29d6c6..26f71a2e3 100644 --- a/acme/challenges.py +++ b/acme/challenges.py @@ -46,6 +46,7 @@ class SimpleHTTP(DVChallenge): """ACME "simpleHttp" challenge.""" typ = "simpleHttp" token = jose.Field("token") + tls = jose.Field("tls", default=True, omitempty=True) @ChallengeResponse.register From 8883bd76fd8beb44de6e1f7fe0addc551b4bc78e Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Thu, 11 Jun 2015 18:05:00 +0000 Subject: [PATCH 3/4] Add --no-simple-http-tls. --- letsencrypt/cli.py | 3 +++ letsencrypt/interfaces.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 4b0e271f7..3bdf2bfc6 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -252,6 +252,9 @@ def create_parser(plugins): add("-t", "--text", dest="text_mode", action="store_true", help="Use the text output instead of the curses UI.") + add("--no-simple-http-tls", action="store_true", + help=config_help("no_simple_http_tls")) + testing_group = parser.add_argument_group( "testing", description="The following flags are meant for " "testing purposes only! Do NOT change them, unless you " diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index e47eea6cc..c0d44a134 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -188,6 +188,10 @@ class IConfig(zope.interface.Interface): "Port number to perform DVSNI challenge. " "Boulder in testing mode defaults to 5001.") + # TODO: not implemented + no_simple_http_tls = zope.interface.Attribute( + "Do not use TLS when solving SimpleHTTP challenges.") + class IInstaller(IPlugin): """Generic Let's Encrypt Installer Interface. From d53120f25f2ab8ffe4300dab4b47085a6b8e9da1 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Fri, 12 Jun 2015 09:21:30 +0000 Subject: [PATCH 4/4] Fix SimpleHTTP tests and omitempty bug. --- acme/challenges_test.py | 9 +++++++++ acme/jose/json_util.py | 2 +- acme/jose/json_util_test.py | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/acme/challenges_test.py b/acme/challenges_test.py index 7018b8e2e..beeec6f73 100644 --- a/acme/challenges_test.py +++ b/acme/challenges_test.py @@ -27,8 +27,17 @@ class SimpleHTTPTest(unittest.TestCase): self.jmsg = { 'type': 'simpleHttp', 'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA', + 'tls': True, } + def test_no_tls(self): + from acme.challenges import SimpleHTTP + self.assertEqual(SimpleHTTP(token='tok', tls=False).to_json(), { + 'tls': False, + 'token': 'tok', + 'type': 'simpleHttp', + }) + def test_to_partial_json(self): self.assertEqual(self.jmsg, self.msg.to_partial_json()) diff --git a/acme/jose/json_util.py b/acme/jose/json_util.py index c7698ed8d..a08145459 100644 --- a/acme/jose/json_util.py +++ b/acme/jose/json_util.py @@ -62,7 +62,7 @@ class Field(object): definition of being empty, e.g. for some more exotic data types. """ - return not value + return not isinstance(value, bool) and not value def omit(self, value): """Omit the value in output?""" diff --git a/acme/jose/json_util_test.py b/acme/jose/json_util_test.py index a37ac08de..242e37589 100644 --- a/acme/jose/json_util_test.py +++ b/acme/jose/json_util_test.py @@ -1,4 +1,5 @@ """Tests for acme.jose.json_util.""" +import itertools import os import pkg_resources import unittest @@ -20,6 +21,13 @@ CSR = M2Crypto.X509.load_request(pkg_resources.resource_filename( class FieldTest(unittest.TestCase): """Tests for acme.jose.json_util.Field.""" + def test_no_omit_boolean(self): + from acme.jose.json_util import Field + for default, omitempty, value in itertools.product( + [True, False], [True, False], [True, False]): + self.assertFalse( + Field("foo", default=default, omitempty=omitempty).omit(value)) + def test_descriptors(self): mock_value = mock.MagicMock()