Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/acme/acme
diff options
context:
space:
mode:
Diffstat (limited to 'acme/acme')
-rw-r--r--acme/acme/challenges.py3
-rw-r--r--acme/acme/client.py3
-rw-r--r--acme/acme/messages.py13
-rw-r--r--acme/acme/mixins.py65
4 files changed, 77 insertions, 7 deletions
diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py
index 0b112be00..b9c6b7eb2 100644
--- a/acme/acme/challenges.py
+++ b/acme/acme/challenges.py
@@ -16,6 +16,7 @@ from OpenSSL import crypto
from acme import crypto_util
from acme import errors
from acme import fields
+from acme.mixins import ResourceMixin, TypeMixin
logger = logging.getLogger(__name__)
@@ -34,7 +35,7 @@ class Challenge(jose.TypedJSONObjectWithFields):
return UnrecognizedChallenge.from_json(jobj)
-class ChallengeResponse(jose.TypedJSONObjectWithFields):
+class ChallengeResponse(ResourceMixin, TypeMixin, jose.TypedJSONObjectWithFields):
# _fields_to_partial_json
"""ACME challenge response."""
TYPES = {} # type: dict
diff --git a/acme/acme/client.py b/acme/acme/client.py
index cecb727c7..cbe543f91 100644
--- a/acme/acme/client.py
+++ b/acme/acme/client.py
@@ -25,6 +25,7 @@ from acme.magic_typing import Dict
from acme.magic_typing import List
from acme.magic_typing import Set
from acme.magic_typing import Text
+from acme.mixins import VersionedLEACMEMixin
logger = logging.getLogger(__name__)
@@ -987,6 +988,8 @@ class ClientNetwork(object):
:rtype: `josepy.JWS`
"""
+ if isinstance(obj, VersionedLEACMEMixin):
+ obj.le_acme_version = acme_version
jobj = obj.json_dumps(indent=2).encode() if obj else b''
logger.debug('JWS payload:\n%s', jobj)
kwargs = {
diff --git a/acme/acme/messages.py b/acme/acme/messages.py
index f8f4bfbe7..90059a6fb 100644
--- a/acme/acme/messages.py
+++ b/acme/acme/messages.py
@@ -9,6 +9,7 @@ from acme import errors
from acme import fields
from acme import jws
from acme import util
+from acme.mixins import ResourceMixin
try:
from collections.abc import Hashable
@@ -356,13 +357,13 @@ class Registration(ResourceBody):
@Directory.register
-class NewRegistration(Registration):
+class NewRegistration(ResourceMixin, Registration):
"""New registration."""
resource_type = 'new-reg'
resource = fields.Resource(resource_type)
-class UpdateRegistration(Registration):
+class UpdateRegistration(ResourceMixin, Registration):
"""Update registration."""
resource_type = 'reg'
resource = fields.Resource(resource_type)
@@ -498,13 +499,13 @@ class Authorization(ResourceBody):
@Directory.register
-class NewAuthorization(Authorization):
+class NewAuthorization(ResourceMixin, Authorization):
"""New authorization."""
resource_type = 'new-authz'
resource = fields.Resource(resource_type)
-class UpdateAuthorization(Authorization):
+class UpdateAuthorization(ResourceMixin, Authorization):
"""Update authorization."""
resource_type = 'authz'
resource = fields.Resource(resource_type)
@@ -522,7 +523,7 @@ class AuthorizationResource(ResourceWithURI):
@Directory.register
-class CertificateRequest(jose.JSONObjectWithFields):
+class CertificateRequest(ResourceMixin, jose.JSONObjectWithFields):
"""ACME new-cert request.
:ivar josepy.util.ComparableX509 csr:
@@ -548,7 +549,7 @@ class CertificateResource(ResourceWithURI):
@Directory.register
-class Revocation(jose.JSONObjectWithFields):
+class Revocation(ResourceMixin, jose.JSONObjectWithFields):
"""Revocation message.
:ivar .ComparableX509 certificate: `OpenSSL.crypto.X509` wrapped in
diff --git a/acme/acme/mixins.py b/acme/acme/mixins.py
new file mode 100644
index 000000000..1cd050ccc
--- /dev/null
+++ b/acme/acme/mixins.py
@@ -0,0 +1,65 @@
+"""Useful mixins for Challenge and Resource objects"""
+
+
+class VersionedLEACMEMixin(object):
+ """This mixin stores the version of Let's Encrypt's endpoint being used."""
+ @property
+ def le_acme_version(self):
+ """Define the version of ACME protocol to use"""
+ return getattr(self, '_le_acme_version', 1)
+
+ @le_acme_version.setter
+ def le_acme_version(self, version):
+ # We need to use object.__setattr__ to not depend on the specific implementation of
+ # __setattr__ in current class (eg. jose.TypedJSONObjectWithFields raises AttributeError
+ # for any attempt to set an attribute to make objects immutable).
+ object.__setattr__(self, '_le_acme_version', version)
+
+ def __setattr__(self, key, value):
+ if key == 'le_acme_version':
+ # Required for @property to operate properly. See comment above.
+ object.__setattr__(self, key, value)
+ else:
+ super(VersionedLEACMEMixin, self).__setattr__(key, value) # pragma: no cover
+
+
+class ResourceMixin(VersionedLEACMEMixin):
+ """
+ This mixin generates a RFC8555 compliant JWS payload
+ by removing the `resource` field if needed (eg. ACME v2 protocol).
+ """
+ def to_partial_json(self):
+ """See josepy.JSONDeserializable.to_partial_json()"""
+ return _safe_jobj_compliance(super(ResourceMixin, self),
+ 'to_partial_json', 'resource')
+
+ def fields_to_partial_json(self):
+ """See josepy.JSONObjectWithFields.fields_to_partial_json()"""
+ return _safe_jobj_compliance(super(ResourceMixin, self),
+ 'fields_to_partial_json', 'resource')
+
+
+class TypeMixin(VersionedLEACMEMixin):
+ """
+ This mixin allows generation of a RFC8555 compliant JWS payload
+ by removing the `type` field if needed (eg. ACME v2 protocol).
+ """
+ def to_partial_json(self):
+ """See josepy.JSONDeserializable.to_partial_json()"""
+ return _safe_jobj_compliance(super(TypeMixin, self),
+ 'to_partial_json', 'type')
+
+ def fields_to_partial_json(self):
+ """See josepy.JSONObjectWithFields.fields_to_partial_json()"""
+ return _safe_jobj_compliance(super(TypeMixin, self),
+ 'fields_to_partial_json', 'type')
+
+
+def _safe_jobj_compliance(instance, jobj_method, uncompliant_field):
+ if hasattr(instance, jobj_method):
+ jobj = getattr(instance, jobj_method)()
+ if instance.le_acme_version == 2:
+ jobj.pop(uncompliant_field, None)
+ return jobj
+
+ raise AttributeError('Method {0}() is not implemented.'.format(jobj_method)) # pragma: no cover