diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 81711e605..fbb2e7418 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -34,6 +34,11 @@ class DVChallenge(Challenge): # pylint: disable=abstract-method """Domain validation challenges.""" +class UnrecognizedChallenge(Challenge): + """Unrecognized challenge.""" + typ = "unknown" + + class ChallengeResponse(jose.TypedJSONObjectWithFields): # _fields_to_partial_json | pylint: disable=abstract-method """ACME challenge response.""" diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 02ae24c8f..d6e9952c3 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -373,7 +373,19 @@ class Authorization(ResourceBody): @challenges.decoder def challenges(value): # pylint: disable=missing-docstring,no-self-argument - return tuple(ChallengeBody.from_json(chall) for chall in value) + # The from_json method raises errors.UnrecognizedTypeError when a + # challenge of unknown type is encountered. We want to ignore this + # case. This forces us to do an explicit iteration, since list + # comprehensions can't handle exceptions. + challs = [] + for chall in value: + try: + challs.append(ChallengeBody.from_json(chall)) + except jose.UnrecognizedTypeError: + challs.append(ChallengeBody( + uri="UNKNOWN", chall=challenges.UnrecognizedChallenge, + status=STATUS_UNKNOWN)) + return tuple(challs) @property def resolved_combinations(self): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index 25f07018c..d7bbdb0e4 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -274,6 +274,7 @@ class AuthorizationTest(unittest.TestCase): def setUp(self): from acme.messages import ChallengeBody from acme.messages import STATUS_VALID + self.challbs = ( ChallengeBody( uri='http://challb1', status=STATUS_VALID, @@ -300,6 +301,19 @@ class AuthorizationTest(unittest.TestCase): 'combinations': combinations, } + # For unknown challenge types + self.jmsg_unknown_chall = { + 'resource': 'challenge', + 'uri': 'random_uri', + 'type': 'unknown', + 'tls': True, + } + + self.jobj_from_unknown = { + 'identifier': identifier.to_json(), + 'challenges': [self.jmsg_unknown_chall], + } + def test_from_json(self): from acme.messages import Authorization Authorization.from_json(self.jobj_from) @@ -314,6 +328,11 @@ class AuthorizationTest(unittest.TestCase): (self.challbs[1], self.challbs[2]), )) + def test_unknown_chall_type(self): + """Just make sure an error isn't thrown.""" + from acme.messages import Authorization + Authorization.from_json(self.jobj_from_unknown) + class AuthorizationResourceTest(unittest.TestCase): """Tests for acme.messages.AuthorizationResource."""