ACM: DomainValidationOptions should be the same for validated certificates (#7233)

This commit is contained in:
Bert Blommers 2024-01-19 23:11:19 +00:00 committed by GitHub
parent e379afe04e
commit 5de7d4df38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 40 additions and 20 deletions

View File

@ -33,4 +33,9 @@ jobs:
cd other_langs/terraform/${{ matrix.service }} cd other_langs/terraform/${{ matrix.service }}
terraform init terraform init
terraform apply --auto-approve terraform apply --auto-approve
echo "Verify nothing changes when ACM certificates are validated"
sleep 30
terraform plan -detailed-exitcode
sleep 30
terraform plan -detailed-exitcode
terraform apply -destroy --auto-approve terraform apply -destroy --auto-approve

View File

@ -343,24 +343,22 @@ class CertBundle(BaseModel):
domain_names = set(sans + [self.common_name]) domain_names = set(sans + [self.common_name])
validation_options = [] validation_options = []
if self.status == "PENDING_VALIDATION": for san in domain_names:
for san in domain_names: resource_record = {
resource_record = { "Name": f"_d930b28be6c5927595552b219965053e.{san}.",
"Name": f"_d930b28be6c5927595552b219965053e.{san}.", "Type": "CNAME",
"Type": "CNAME", "Value": "_c9edd76ee4a0e2a74388032f3861cc50.ykybfrwcxw.acm-validations.aws.",
"Value": "_c9edd76ee4a0e2a74388032f3861cc50.ykybfrwcxw.acm-validations.aws.", }
validation_options.append(
{
"DomainName": san,
"ValidationDomain": san,
"ValidationStatus": self.status,
"ValidationMethod": "DNS",
"ResourceRecord": resource_record,
} }
validation_options.append( )
{
"DomainName": san,
"ValidationDomain": san,
"ValidationStatus": self.status,
"ValidationMethod": "DNS",
"ResourceRecord": resource_record,
}
)
else:
validation_options = [{"DomainName": name} for name in domain_names]
result["Certificate"]["DomainValidationOptions"] = validation_options result["Certificate"]["DomainValidationOptions"] = validation_options
if self.type == "IMPORTED": if self.type == "IMPORTED":

View File

@ -208,6 +208,7 @@ class RecordSet(CloudFormationModel):
self.alias_target = kwargs.get("AliasTarget", []) self.alias_target = kwargs.get("AliasTarget", [])
self.failover = kwargs.get("Failover", []) self.failover = kwargs.get("Failover", [])
self.geo_location = kwargs.get("GeoLocation", []) self.geo_location = kwargs.get("GeoLocation", [])
self.multi_value = kwargs.get("MultiValueAnswer")
@staticmethod @staticmethod
def cloudformation_name_type() -> str: def cloudformation_name_type() -> str:

View File

@ -562,6 +562,9 @@ LIST_RRSET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
{% if record.failover %} {% if record.failover %}
<Failover>{{ record.failover }}</Failover> <Failover>{{ record.failover }}</Failover>
{% endif %} {% endif %}
{% if record.multi_value %}
<MultiValueAnswer>{{ record.multi_value }}</MultiValueAnswer>
{% endif %}
{% if record.geo_location %} {% if record.geo_location %}
<GeoLocation> <GeoLocation>
{% for geo_key in ['ContinentCode','CountryCode','SubdivisionCode'] %} {% for geo_key in ['ContinentCode','CountryCode','SubdivisionCode'] %}

View File

@ -1,5 +1,6 @@
import os import os
import uuid import uuid
from time import sleep
from unittest import SkipTest, mock from unittest import SkipTest, mock
import boto3 import boto3
@ -180,7 +181,6 @@ def test_describe_certificate():
assert len(resp["Certificate"]["DomainValidationOptions"]) == 1 assert len(resp["Certificate"]["DomainValidationOptions"]) == 1
validation_option = resp["Certificate"]["DomainValidationOptions"][0] validation_option = resp["Certificate"]["DomainValidationOptions"][0]
assert validation_option["DomainName"] == SERVER_COMMON_NAME assert validation_option["DomainName"] == SERVER_COMMON_NAME
assert "ValidationDomain" not in validation_option
@mock_acm @mock_acm
@ -388,7 +388,10 @@ def test_request_certificate():
@mock_acm @mock_acm
@mock.patch("moto.settings.ACM_VALIDATION_WAIT", 1)
def test_request_certificate_with_optional_arguments(): def test_request_certificate_with_optional_arguments():
if not settings.TEST_DECORATOR_MODE:
raise SkipTest("Can only change setting in DecoratorMode")
client = boto3.client("acm", region_name="eu-central-1") client = boto3.client("acm", region_name="eu-central-1")
token = str(uuid.uuid4()) token = str(uuid.uuid4())
@ -406,12 +409,21 @@ def test_request_certificate_with_optional_arguments():
arn_1 = resp["CertificateArn"] arn_1 = resp["CertificateArn"]
cert = client.describe_certificate(CertificateArn=arn_1)["Certificate"] cert = client.describe_certificate(CertificateArn=arn_1)["Certificate"]
assert cert["Status"] == "PENDING_VALIDATION"
assert len(cert["SubjectAlternativeNames"]) == 3 assert len(cert["SubjectAlternativeNames"]) == 3
assert len(cert["DomainValidationOptions"]) == 3 validation_options = cert["DomainValidationOptions"].copy()
assert {option["DomainName"] for option in cert["DomainValidationOptions"]} == set( assert len(validation_options) == 3
assert {option["DomainName"] for option in validation_options} == set(
cert["SubjectAlternativeNames"] cert["SubjectAlternativeNames"]
) )
# Verify SAN's are still the same, even after the Certificate is validated
sleep(2)
for opt in validation_options:
opt["ValidationStatus"] = "ISSUED"
cert = client.describe_certificate(CertificateArn=arn_1)["Certificate"]
assert cert["DomainValidationOptions"] == validation_options
resp = client.list_tags_for_certificate(CertificateArn=arn_1) resp = client.list_tags_for_certificate(CertificateArn=arn_1)
tags = {item["Key"]: item.get("Value", "__NONE__") for item in resp["Tags"]} tags = {item["Key"]: item.get("Value", "__NONE__") for item in resp["Tags"]}
assert len(tags) == 2 assert len(tags) == 2

View File

@ -809,6 +809,7 @@ def test_change_resource_record_sets_crud_valid_with_special_xml_chars(
assert cname_record_detail["ResourceRecords"] == [ assert cname_record_detail["ResourceRecords"] == [
{"Value": "SomeInitialValue&NewValue"} {"Value": "SomeInitialValue&NewValue"}
] ]
assert cname_record_detail.get("MultiValueAnswer") == multi_value_answer
# Delete record. # Delete record.
delete_payload = { delete_payload = {