From 8e28e36178ec6092ddbdc5f507c23452e01d3146 Mon Sep 17 00:00:00 2001 From: Anna Glasgall Date: Wed, 22 Mar 2023 20:09:14 -0400 Subject: [PATCH] Add async interface for finalization to acme.client.ClientV2 (#9622) * Add async interface for finalization to acme.client.ClientV2 Add `begin_order_finalization()`/`poll_finalization()` to `acme.client.ClientV2`, which are directly analogous to `answer_challenge()`/`poll_authorizations()`. This allows us to finalize an order and then later poll for its completion as separate steps. * Address code review feedback Rename `begin_order_finalization` -> `begin_finalization` and tweak wording of changelog entry --- acme/acme/client.py | 44 ++++++++++++++++++++++++++++++++++++-------- certbot/CHANGELOG.md | 3 +++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index ee31f58a7..ee1dd86ab 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -210,23 +210,35 @@ class ClientV2: raise errors.ValidationError(failed) return orderr.update(authorizations=responses) - def finalize_order(self, orderr: messages.OrderResource, deadline: datetime.datetime, - fetch_alternative_chains: bool = False) -> messages.OrderResource: - """Finalize an order and obtain a certificate. + def begin_finalization(self, orderr: messages.OrderResource + ) -> messages.OrderResource: + """Start the process of finalizing an order. :param messages.OrderResource orderr: order to finalize :param datetime.datetime deadline: when to stop polling and timeout - :param bool fetch_alternative_chains: whether to also fetch alternative - certificate chains - :returns: finalized order + :returns: updated order :rtype: messages.OrderResource - """ csr = OpenSSL.crypto.load_certificate_request( OpenSSL.crypto.FILETYPE_PEM, orderr.csr_pem) wrapped_csr = messages.CertificateRequest(csr=jose.ComparableX509(csr)) - self._post(orderr.body.finalize, wrapped_csr) + res = self._post(orderr.body.finalize, wrapped_csr) + orderr = orderr.update(body=messages.Order.from_json(res.json())) + return orderr + + def poll_finalization(self, orderr: messages.OrderResource, + deadline: datetime.datetime, + fetch_alternative_chains: bool = False + ) -> messages.OrderResource: + """ + Poll an order that has been finalized for its status. + If it becomes valid, obtain the certificate. + + :returns: finalized order (with certificate) + :rtype: messages.OrderResource + """ + while datetime.datetime.now() < deadline: time.sleep(1) response = self._post_as_get(orderr.uri) @@ -247,6 +259,22 @@ class ClientV2: return orderr raise errors.TimeoutError() + def finalize_order(self, orderr: messages.OrderResource, deadline: datetime.datetime, + fetch_alternative_chains: bool = False) -> messages.OrderResource: + """Finalize an order and obtain a certificate. + + :param messages.OrderResource orderr: order to finalize + :param datetime.datetime deadline: when to stop polling and timeout + :param bool fetch_alternative_chains: whether to also fetch alternative + certificate chains + + :returns: finalized order + :rtype: messages.OrderResource + + """ + self.begin_finalization(orderr) + return self.poll_finalization(orderr, deadline, fetch_alternative_chains) + def revoke(self, cert: jose.ComparableX509, rsn: int) -> None: """Revoke certificate. diff --git a/certbot/CHANGELOG.md b/certbot/CHANGELOG.md index 6d7e46715..8cde9c3f5 100644 --- a/certbot/CHANGELOG.md +++ b/certbot/CHANGELOG.md @@ -8,6 +8,9 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). * `acme.messages.OrderResource` now supports being round-tripped through JSON +* acme.client.ClientV2 now provides separate `begin_finalization` + and `poll_finalization` methods, in addition to the existing + `finalize_order` method. ### Changed