From 8c4f016b2d4524387ce2ddddf0284118eae455b7 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 22 Nov 2017 13:00:29 -0800 Subject: [PATCH] In ACMEv2, challenges have "url" instead of "uri". To handle this smoothly, Challenge's uri field becomes private (_uri), and is joined by _url. Serialization and deserialization will preserve whichever one was set. The uri name is taken over by an @property that returns whichever of the two is set. I chose not to enforce that they shouldn't both be present because it would just add unnecessary code and brittleness with no stability benefit. * Make url a virtual field. * Add @property annotation. --- acme/acme/client_test.py | 2 +- acme/acme/messages.py | 28 +++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/acme/acme/client_test.py b/acme/acme/client_test.py index 4bd762865..3aac9c874 100644 --- a/acme/acme/client_test.py +++ b/acme/acme/client_test.py @@ -181,7 +181,7 @@ class ClientTest(unittest.TestCase): # TODO: split here and separate test self.assertRaises(errors.UnexpectedUpdate, self.client.answer_challenge, - self.challr.body.update(uri='foo'), chall_response) + self.challr.body.update(_uri='foo'), chall_response) def test_answer_challenge_missing_next(self): self.assertRaises( diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 4b4fa5003..4dee96c58 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -325,13 +325,26 @@ class ChallengeBody(ResourceBody): """ __slots__ = ('chall',) - uri = jose.Field('uri') + # ACMEv1 has a "uri" field in challenges. ACMEv2 has a "url" field. This + # challenge object supports either one. In Client.answer_challenge, + # whichever one is set will be used. + _uri = jose.Field('uri', omitempty=True, default=None) + _url = jose.Field('url', omitempty=True, default=None) status = jose.Field('status', decoder=Status.from_json, omitempty=True, default=STATUS_PENDING) validated = fields.RFC3339Field('validated', omitempty=True) error = jose.Field('error', decoder=Error.from_json, omitempty=True, default=None) + def __init__(self, **kwargs): + new_kwargs = {} + for k, v in kwargs.items(): + if k in ('uri', 'url',): + k = '_' + k + new_kwargs[k] = v + # pylint: disable=star-args + super(ChallengeBody, self).__init__(**new_kwargs) + def to_partial_json(self): jobj = super(ChallengeBody, self).to_partial_json() jobj.update(self.chall.to_partial_json()) @@ -343,6 +356,11 @@ class ChallengeBody(ResourceBody): jobj_fields['chall'] = challenges.Challenge.from_json(jobj) return jobj_fields + @property + def uri(self): + """The URL of this challenge.""" + return self._url or self._uri + def __getattr__(self, name): return getattr(self.chall, name) @@ -358,10 +376,10 @@ class ChallengeResource(Resource): authzr_uri = jose.Field('authzr_uri') @property - def uri(self): # pylint: disable=missing-docstring,no-self-argument - # bug? 'method already defined line None' - # pylint: disable=function-redefined - return self.body.uri # pylint: disable=no-member + def uri(self): + """The URL of the challenge body.""" + # pylint: disable=function-redefined,no-member + return self.body.uri class Authorization(ResourceBody):