mirror of
https://github.com/certbot/certbot.git
synced 2026-06-04 22:33:00 -04:00
Merge branch 'acme-v2' into v2-orders
This commit is contained in:
commit
89a73d6e26
2 changed files with 55 additions and 30 deletions
|
|
@ -10,6 +10,7 @@ import time
|
|||
import six
|
||||
from six.moves import http_client # pylint: disable=import-error
|
||||
|
||||
import crypto_util
|
||||
import josepy as jose
|
||||
import OpenSSL
|
||||
import re
|
||||
|
|
@ -199,26 +200,6 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
|
|||
response, authzr.body.identifier, authzr.uri)
|
||||
return updated_authzr, response
|
||||
|
||||
def revoke(self, cert, rsn):
|
||||
"""Revoke certificate.
|
||||
|
||||
:param .ComparableX509 cert: `OpenSSL.crypto.X509` wrapped in
|
||||
`.ComparableX509`
|
||||
|
||||
:param int rsn: Reason code for certificate revocation.
|
||||
|
||||
:raises .ClientError: If revocation is unsuccessful.
|
||||
|
||||
"""
|
||||
response = self.net.post(self.directory[messages.Revocation],
|
||||
messages.Revocation(
|
||||
certificate=cert,
|
||||
reason=rsn),
|
||||
content_type=None)
|
||||
if response.status_code != http_client.OK:
|
||||
raise errors.ClientError(
|
||||
'Successful revocation must return HTTP OK status')
|
||||
|
||||
class Client(ClientBase):
|
||||
"""ACME client for a v1 API.
|
||||
|
||||
|
|
@ -563,9 +544,14 @@ class ClientV2(ClientBase):
|
|||
:rtype: `list` of `.AuthorizationResource`
|
||||
"""
|
||||
csr = OpenSSL.crypto.load_certificate_request(OpenSSL.crypto.FILETYPE_PEM, csr_pem)
|
||||
order = messages.NewOrder(csr=jose.ComparableX509(csr))
|
||||
wrapped_csr = jose.ComparableX509(csr)
|
||||
identifiers = []
|
||||
for name in crypto_util._pyopenssl_cert_or_req_san(csr):
|
||||
identifiers.append(messages.Identifier(typ=messages.IDENTIFIER_FQDN,
|
||||
value=name))
|
||||
order = messages.NewOrder(identifiers=identifiers)
|
||||
response = self.net.post(self.directory.new_order, order)
|
||||
order_response = self._order_resource_from_response(response)
|
||||
order_response = self._order_resource_from_response(response, csr=wrapped_csr)
|
||||
return order_response
|
||||
|
||||
def poll_order_and_request_issuance(self, orderr):
|
||||
|
|
@ -600,7 +586,7 @@ class ClientV2(ClientBase):
|
|||
latest = self._order_resource_from_response(response, uri=orderr.uri)
|
||||
return latest
|
||||
|
||||
def _order_resource_from_response(self, response, uri=None):
|
||||
def _order_resource_from_response(self, response, uri=None, csr=None):
|
||||
body = messages.Order.from_json(response.json())
|
||||
authorizations = []
|
||||
for url in body.authorizations:
|
||||
|
|
@ -614,7 +600,35 @@ class ClientV2(ClientBase):
|
|||
body=body,
|
||||
uri=response.headers.get('Location', uri),
|
||||
fullchain_pem=fullchain_pem,
|
||||
authorizations=authorizations)
|
||||
authorizations=authorizations,
|
||||
csr=csr)
|
||||
|
||||
def poll_order_and_request_issuance(self, orderr, max_time=datetime.timedelta(seconds=90)):
|
||||
"""Poll Order Resource for status.
|
||||
responses = []
|
||||
deadline = datetime.datetime.now() + max_time
|
||||
for url in orderr.body.authorizations:
|
||||
while datetime.datetime.now() < deadline:
|
||||
time.sleep(1)
|
||||
authzr = self._authzr_from_response(self.net.get(url), uri=url)
|
||||
if authzr.body.status != messages.STATUS_PENDING:
|
||||
responses.append(authzr)
|
||||
break
|
||||
for authzr in responses:
|
||||
if authzr.body.status != messages.STATUS_VALID:
|
||||
for chall in authzr.body.challenges:
|
||||
if chall.error != None:
|
||||
raise Exception("failed challenge for %s: %s" %
|
||||
(authzr.body.identifier.value, chall.error))
|
||||
raise Exception("failed authorization: %s" % authzr.body)
|
||||
latest = self._order_resource_from_response(self.net.get(orderr.uri), uri=orderr.uri)
|
||||
self.net.post(latest.body.finalize, messages.CertificateRequest(csr=orderr.csr))
|
||||
while datetime.datetime.now() < deadline:
|
||||
time.sleep(1)
|
||||
latest = self._order_resource_from_response(self.net.get(orderr.uri), uri=orderr.uri)
|
||||
if latest.fullchain_pem is not None:
|
||||
return latest
|
||||
return None
|
||||
|
||||
|
||||
class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""ACME protocol messages."""
|
||||
import collections
|
||||
import re
|
||||
import six
|
||||
|
||||
import josepy as jose
|
||||
|
|
@ -171,9 +172,9 @@ class Directory(jose.JSONDeSerializable):
|
|||
|
||||
class Meta(jose.JSONObjectWithFields):
|
||||
"""Directory Meta."""
|
||||
terms_of_service = jose.Field('terms-of-service', omitempty=True)
|
||||
terms_of_service = jose.Field('termsOfService', omitempty=True)
|
||||
website = jose.Field('website', omitempty=True)
|
||||
caa_identities = jose.Field('caa-identities', omitempty=True)
|
||||
caa_identities = jose.Field('caaIdentities', omitempty=True)
|
||||
|
||||
@classmethod
|
||||
def _canon_key(cls, key):
|
||||
|
|
@ -193,11 +194,19 @@ class Directory(jose.JSONDeSerializable):
|
|||
# not clear on that
|
||||
self._jobj = canon_jobj
|
||||
|
||||
def _camelCase(self, name):
|
||||
"""Convert a snake_case name to camelCase."""
|
||||
return re.sub('_([a-z])', lambda x: x.group(1).upper(), name)
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self[name.replace('_', '-')]
|
||||
except KeyError as error:
|
||||
raise AttributeError(str(error) + ': ' + name)
|
||||
try:
|
||||
print self._camelCase(name)
|
||||
return self[self._camelCase(name)]
|
||||
except KeyError as error:
|
||||
raise AttributeError(str(error) + ': ' + name)
|
||||
|
||||
def __getitem__(self, name):
|
||||
try:
|
||||
|
|
@ -251,7 +260,7 @@ class Registration(ResourceBody):
|
|||
contact = jose.Field('contact', omitempty=True, default=())
|
||||
agreement = jose.Field('agreement', omitempty=True)
|
||||
status = jose.Field('status', omitempty=True)
|
||||
terms_of_service_agreed = jose.Field('terms-of-service-agreed', omitempty=True)
|
||||
terms_of_service_agreed = jose.Field('termsOfServiceAgreed', omitempty=True)
|
||||
|
||||
phone_prefix = 'tel:'
|
||||
email_prefix = 'mailto:'
|
||||
|
|
@ -494,10 +503,11 @@ class Order(ResourceBody):
|
|||
:ivar datetime.datetime expires:
|
||||
|
||||
"""
|
||||
csr = jose.Field('csr', decoder=jose.decode_csr, encoder=jose.encode_csr)
|
||||
status = jose.Field('status', omitempty=True)
|
||||
identifiers = jose.Field('identifiers', omitempty=True)
|
||||
status = jose.Field('status', omitempty=True, default=None)
|
||||
authorizations = jose.Field('authorizations', omitempty=True)
|
||||
certificate = jose.Field('certificate', omitempty=True)
|
||||
finalize = jose.Field('finalize', omitempty=True)
|
||||
expires = fields.RFC3339Field('expires', omitempty=True)
|
||||
|
||||
class OrderResource(ResourceWithURI):
|
||||
|
|
@ -507,6 +517,7 @@ class OrderResource(ResourceWithURI):
|
|||
|
||||
"""
|
||||
body = jose.Field('body', decoder=Order.from_json)
|
||||
csr = jose.Field('csr', omitempty=True)
|
||||
authorizations = jose.Field('authorizations')
|
||||
fullchain_pem = jose.Field('fullchain_pem', omitempty=True)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue