Repository: certbot/certbot

Issue:
Ignore unknown ACME challenge types
As ACME provides general framework for challenges, instead of throwing

`Deserialization error: Could not decode 'challenges' ... http-00 was not recognized`

unrecognized challenge types should be (silently) ignored.

The idea is to deserialize them to:

``` python
class UnrecognizedChallenge(Challenge):
    """Unrecognized challenge.

    :ivar jobj: Original JSON decoded object.

    """
```



Discussion:

Resolving patch:
diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py
--- 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
--- 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):


Test patch:
diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py
--- 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 @@ def setUp(self):
             '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 @@ def test_resolved_combinations(self):
             (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."""