From a19c840060e54b7ab7dae3cce38c0e9b67dd1e80 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Tue, 11 Nov 2025 19:48:05 -0800 Subject: [PATCH] More tests --- .../bdd/features/pki/acme/challenge.feature | 110 +++++++++--------- backend/bdd/features/steps/pki_acme.py | 59 ++++++++++ 2 files changed, 113 insertions(+), 56 deletions(-) diff --git a/backend/bdd/features/pki/acme/challenge.feature b/backend/bdd/features/pki/acme/challenge.feature index 78065a22d..543e4cea3 100644 --- a/backend/bdd/features/pki/acme/challenge.feature +++ b/backend/bdd/features/pki/acme/challenge.feature @@ -115,59 +115,57 @@ Feature: Challenge Then the value response with jq ".type" should be equal to "urn:ietf:params:acme:error:orderNotReady" Then the value response with jq ".detail" should be equal to "ACME order is not ready" -# Scenario: CSR names mismatch with order identifier -# Given I have an ACME cert profile as "acme_profile" -# When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" -# Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account -# When I create certificate signing request as csr -# Then I add names to certificate signing request csr -# """ -# { -# "COMMON_NAME": "example.com" -# } -# """ -# And I create a RSA private key pair as cert_key -# And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format -# Then I peak and memorize the next nonce as nonce -# When I send a raw ACME request to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order" -# """ -# { -# "protected": { -# "alg": "RS256", -# "nonce": "{nonce}", -# "url": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order", -# "kid": "{acme_account.uri}" -# }, -# "payload": { -# "identifiers": [ -# { "type": "dns", "value": "localhost" }, -# { "type": "dns", "value": "infisical.com" } -# ] -# } -# } -# """ -# Then the value response.status_code should be equal to 201 -# And I memorize response with jq ".finalize" as finalize_url -# And I memorize response.headers with jq ".["replay-nonce"]" as nonce -# And I memorize response as order -# And I select challenge with type http-01 for domain localhost from order in order as challenge -# And I serve challenge response for challenge at localhost -# And I tell ACME server that challenge is ready to be verified -# When I send a raw ACME request to "{finalize_url}" -# """ -# { -# "protected": { -# "alg": "RS256", -# "nonce": "{nonce}", -# "url": "{finalize_url}", -# "kid": "{acme_account.uri}" -# }, -# "payload": { -# "csr": "{csr_pem}" -# } -# } -# """ -# Then the value response.status_code should be equal to 400 -# Then the value response with jq ".status" should be equal to 400 -# Then the value response with jq ".type" should be equal to "urn:ietf:params:acme:error:malformed" -# Then the value response with jq ".detail" should be equal to "" + Scenario: CSR names mismatch with order identifier + Given I have an ACME cert profile as "acme_profile" + When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" + Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account + When I create certificate signing request as csr + Then I add names to certificate signing request csr + """ + { + "COMMON_NAME": "example.com" + } + """ + And I create a RSA private key pair as cert_key + And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format + Then I peak and memorize the next nonce as nonce + When I send a raw ACME request to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order" + """ + { + "protected": { + "alg": "RS256", + "nonce": "{nonce}", + "url": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order", + "kid": "{acme_account.uri}" + }, + "payload": { + "identifiers": [ + { "type": "dns", "value": "localhost" }, + { "type": "dns", "value": "infisical.com" } + ] + } + } + """ + Then the value response.status_code should be equal to 201 + And I memorize response with jq ".finalize" as finalize_url + And I memorize response.headers with jq ".["replay-nonce"]" as nonce + And I memorize response as order + And I pass all challenges with type http-01 for order in order + When I send a raw ACME request to "{finalize_url}" + """ + { + "protected": { + "alg": "RS256", + "nonce": "{nonce}", + "url": "{finalize_url}", + "kid": "{acme_account.uri}" + }, + "payload": { + "csr": "{csr_pem}" + } + } + """ + Then the value response.status_code should be equal to 400 + And the value response with jq ".status" should be equal to 400 + And the value response with jq ".type" should be equal to "urn:ietf:params:acme:error:malformed" + And the value response with jq ".detail" should be equal to "" diff --git a/backend/bdd/features/steps/pki_acme.py b/backend/bdd/features/steps/pki_acme.py index 170ada1de..f8e6da300 100644 --- a/backend/bdd/features/steps/pki_acme.py +++ b/backend/bdd/features/steps/pki_acme.py @@ -623,6 +623,9 @@ def serve_challenge( context: Context, challenge: messages.ChallengeBody, ): + if hasattr(context, "web_server"): + context.web_server.shutdown_and_server_close() + response, validation = challenge.response_and_validation( context.acme_client.net.key ) @@ -660,6 +663,62 @@ def step_impl( context.vars[challenge_var] = challenge +@then("I pass all challenges with type {challenge_type} for order in {order_var_path}") +def step_impl( + context: Context, + challenge_type: str, + order_var_path: str, +): + acme_client = context.acme_client + order = eval_var(context, order_var_path, as_json=False) + if isinstance(order, dict): + order_body = messages.Order.from_json(order) + order = messages.OrderResource( + body=order_body, + authorizations=[ + acme_client._authzr_from_response( + acme_client._post_as_get(url), uri=url + ) + for url in order_body.authorizations + ], + ) + if not isinstance(order, messages.OrderResource): + raise ValueError( + f"Expected OrderResource but got {type(order)!r} at {order_var_path!r}" + ) + + for domain in order.body.identifiers: + logger.info( + "Selecting challenge for domain %s with type %s ...", + domain.value, + challenge_type, + ) + challenge = select_challenge( + context=context, + challenge_type=challenge_type, + domain=domain.value, + order_var_path=order_var_path, + ) + logger.info( + "Found challenge for domain %s with type %s, challenge=%s", + domain.value, + challenge_type, + challenge.uri, + ) + + logger.info( + "Serving challenge for domain %s with type %s ...", + domain.value, + challenge_type, + ) + serve_challenge(context=context, challenge=challenge) + + logger.info( + "Notifying challenge for domain %s with type %s ...", domain, challenge_type + ) + notify_challenge_ready(context=context, challenge=challenge) + + @then("I serve challenge response for {var_path} at {hostname}") def step_impl(context: Context, var_path: str, hostname: str): challenge = eval_var(context, var_path, as_json=False)