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
diff options
context:
space:
mode:
authorBlake Griffith <blake.a.griffith@gmail.com>2016-10-13 00:46:02 +0300
committerPeter Eckersley <pde@users.noreply.github.com>2016-10-13 00:46:02 +0300
commit777356833221669ce578b53dc81a31100e0511aa (patch)
tree3d0f61f61d5477edcf069a0ec7818ff88b4cc03f /acme
parentf008fd0af99c609a62250ff121c7aeb9eb813553 (diff)
Update ACME error namespace to match the new draft. (#3469)
* Update error namespace in acme package. * Use new error namespace in certbot. * fix lint and py26 errors. * Update with_code docstring. * @pde's suggestions
Diffstat (limited to 'acme')
-rw-r--r--acme/acme/messages.py82
-rw-r--r--acme/acme/messages_test.py29
2 files changed, 83 insertions, 28 deletions
diff --git a/acme/acme/messages.py b/acme/acme/messages.py
index 563f80627..a7c86a10c 100644
--- a/acme/acme/messages.py
+++ b/acme/acme/messages.py
@@ -7,6 +7,37 @@ from acme import fields
from acme import jose
from acme import util
+OLD_ERROR_PREFIX = "urn:acme:error:"
+ERROR_PREFIX = "urn:ietf:params:acme:error:"
+
+ERROR_CODES = {
+ 'badCSR': 'The CSR is unacceptable (e.g., due to a short key)',
+ 'badNonce': 'The client sent an unacceptable anti-replay nonce',
+ 'connection': ('The server could not connect to the client to verify the'
+ ' domain'),
+ 'dnssec': 'The server could not validate a DNSSEC signed domain',
+ # deprecate invalidEmail
+ 'invalidEmail': 'The provided email for a registration was invalid',
+ 'invalidContact': 'The provided contact URI was invalid',
+ 'malformed': 'The request message was malformed',
+ 'rateLimited': 'There were too many requests of a given type',
+ 'serverInternal': 'The server experienced an internal error',
+ 'tls': 'The server experienced a TLS error during domain verification',
+ 'unauthorized': 'The client lacks sufficient authorization',
+ 'unknownHost': 'The server could not resolve a domain name',
+}
+
+ERROR_TYPE_DESCRIPTIONS = dict(
+ (ERROR_PREFIX + name, desc) for name, desc in ERROR_CODES.items())
+
+ERROR_TYPE_DESCRIPTIONS.update(dict( # add errors with old prefix, deprecate me
+ (OLD_ERROR_PREFIX + name, desc) for name, desc in ERROR_CODES.items()))
+
+
+def is_acme_error(err):
+ """Check if argument is an ACME error."""
+ return (ERROR_PREFIX in str(err)) or (OLD_ERROR_PREFIX in str(err))
+
class Error(jose.JSONObjectWithFields, errors.Error):
"""ACME error.
@@ -18,31 +49,24 @@ class Error(jose.JSONObjectWithFields, errors.Error):
:ivar unicode detail:
"""
- ERROR_TYPE_DESCRIPTIONS = dict(
- ('urn:acme:error:' + name, description) for name, description in (
- ('badCSR', 'The CSR is unacceptable (e.g., due to a short key)'),
- ('badNonce', 'The client sent an unacceptable anti-replay nonce'),
- ('connection', 'The server could not connect to the client to '
- 'verify the domain'),
- ('dnssec', 'The server could not validate a DNSSEC signed domain'),
- ('invalidEmail',
- 'The provided email for a registration was invalid'),
- ('invalidContact',
- 'The provided contact URI was invalid'),
- ('malformed', 'The request message was malformed'),
- ('rateLimited', 'There were too many requests of a given type'),
- ('serverInternal', 'The server experienced an internal error'),
- ('tls', 'The server experienced a TLS error during domain '
- 'verification'),
- ('unauthorized', 'The client lacks sufficient authorization'),
- ('unknownHost', 'The server could not resolve a domain name'),
- )
- )
-
typ = jose.Field('type', omitempty=True, default='about:blank')
title = jose.Field('title', omitempty=True)
detail = jose.Field('detail', omitempty=True)
+ @classmethod
+ def with_code(cls, code, **kwargs):
+ """Create an Error instance with an ACME Error code.
+
+ :unicode code: An ACME error code, like 'dnssec'.
+ :kwargs: kwargs to pass to Error.
+
+ """
+ if code not in ERROR_CODES:
+ raise ValueError("The supplied code: %s is not a known ACME error"
+ " code" % code)
+ typ = ERROR_PREFIX + code
+ return cls(typ=typ, **kwargs)
+
@property
def description(self):
"""Hardcoded error description based on its type.
@@ -51,7 +75,21 @@ class Error(jose.JSONObjectWithFields, errors.Error):
:rtype: unicode
"""
- return self.ERROR_TYPE_DESCRIPTIONS.get(self.typ)
+ return ERROR_TYPE_DESCRIPTIONS.get(self.typ)
+
+ @property
+ def code(self):
+ """ACME error code.
+
+ Basically self.typ without the ERROR_PREFIX.
+
+ :returns: error code if standard ACME code or ``None``.
+ :rtype: unicode
+
+ """
+ code = str(self.typ).split(':')[-1]
+ if code in ERROR_CODES:
+ return code
def __str__(self):
return ' :: '.join(
diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py
index 36d0dd618..a0322968c 100644
--- a/acme/acme/messages_test.py
+++ b/acme/acme/messages_test.py
@@ -17,13 +17,13 @@ class ErrorTest(unittest.TestCase):
"""Tests for acme.messages.Error."""
def setUp(self):
- from acme.messages import Error
+ from acme.messages import Error, ERROR_PREFIX
self.error = Error(
- detail='foo', typ='urn:acme:error:malformed', title='title')
+ detail='foo', typ=ERROR_PREFIX + 'malformed', title='title')
self.jobj = {
'detail': 'foo',
'title': 'some title',
- 'type': 'urn:acme:error:malformed',
+ 'type': ERROR_PREFIX + 'malformed',
}
self.error_custom = Error(typ='custom', detail='bar')
self.jobj_cusom = {'type': 'custom', 'detail': 'bar'}
@@ -47,10 +47,27 @@ class ErrorTest(unittest.TestCase):
def test_str(self):
self.assertEqual(
- 'urn:acme:error:malformed :: The request message was '
+ 'urn:ietf:params:acme:error:malformed :: The request message was '
'malformed :: foo :: title', str(self.error))
self.assertEqual('custom :: bar', str(self.error_custom))
+ def test_code(self):
+ from acme.messages import Error
+ self.assertEqual('malformed', self.error.code)
+ self.assertEqual(None, self.error_custom.code)
+ self.assertEqual(None, Error().code)
+
+ def test_is_acme_error(self):
+ from acme.messages import is_acme_error
+ self.assertTrue(is_acme_error(self.error))
+ self.assertTrue(is_acme_error(str(self.error)))
+ self.assertFalse(is_acme_error(self.error_custom))
+
+ def test_with_code(self):
+ from acme.messages import Error, is_acme_error
+ self.assertTrue(is_acme_error(Error.with_code('badCSR')))
+ self.assertRaises(ValueError, Error.with_code, 'not an ACME error code')
+
class ConstantTest(unittest.TestCase):
"""Tests for acme.messages._Constant."""
@@ -240,7 +257,7 @@ class ChallengeBodyTest(unittest.TestCase):
from acme.messages import Error
from acme.messages import STATUS_INVALID
self.status = STATUS_INVALID
- error = Error(typ='urn:acme:error:serverInternal',
+ error = Error(typ='urn:ietf:params:acme:error:serverInternal',
detail='Unable to communicate with DNS server')
self.challb = ChallengeBody(
uri='http://challb', chall=self.chall, status=self.status,
@@ -256,7 +273,7 @@ class ChallengeBodyTest(unittest.TestCase):
self.jobj_from = self.jobj_to.copy()
self.jobj_from['status'] = 'invalid'
self.jobj_from['error'] = {
- 'type': 'urn:acme:error:serverInternal',
+ 'type': 'urn:ietf:params:acme:error:serverInternal',
'detail': 'Unable to communicate with DNS server',
}