-
Notifications
You must be signed in to change notification settings - Fork 992
Implement host and CN resolution #6802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| /* | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"). | ||
| * You may not use this file except in compliance with the License. | ||
| * A copy of the License is located at | ||
| * | ||
| * http://aws.amazon.com/apache2.0 | ||
| * | ||
| * or in the "license" file accompanying this file. This file is distributed | ||
| * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
| * express or implied. See the License for the specific language governing | ||
| * permissions and limitations under the License. | ||
| */ | ||
|
|
||
| package software.amazon.awssdk.messagemanager.sns.internal; | ||
|
|
||
| import java.net.URI; | ||
| import software.amazon.awssdk.annotations.SdkInternalApi; | ||
| import software.amazon.awssdk.annotations.SdkTestInternalApi; | ||
| import software.amazon.awssdk.annotations.ThreadSafe; | ||
| import software.amazon.awssdk.core.exception.SdkClientException; | ||
| import software.amazon.awssdk.endpoints.Endpoint; | ||
| import software.amazon.awssdk.regions.PartitionMetadata; | ||
| import software.amazon.awssdk.regions.Region; | ||
| import software.amazon.awssdk.services.sns.endpoints.SnsEndpointParams; | ||
| import software.amazon.awssdk.services.sns.endpoints.SnsEndpointProvider; | ||
| import software.amazon.awssdk.utils.CompletableFutureUtils; | ||
| import software.amazon.awssdk.utils.Logger; | ||
|
|
||
| /** | ||
| * Utility class for determining both the regional endpoint that SNS certificates are expected to be hosted from, as well as the | ||
| * expected common name (CN) that the certificate from that endpoint must have. | ||
| */ | ||
| @SdkInternalApi | ||
| @ThreadSafe | ||
| public class SnsHostProvider { | ||
| private static final Logger LOG = Logger.loggerFor(SnsHostProvider.class); | ||
|
|
||
| private final Region region; | ||
| private final SnsEndpointProvider endpointProvider; | ||
|
|
||
| public SnsHostProvider(Region region) { | ||
| this(region, SnsEndpointProvider.defaultProvider()); | ||
| } | ||
|
|
||
| @SdkTestInternalApi | ||
| SnsHostProvider(Region region, SnsEndpointProvider endpointProvider) { | ||
| this.region = region; | ||
| this.endpointProvider = endpointProvider; | ||
| } | ||
|
|
||
| public URI regionalEndpoint() { | ||
| SnsEndpointParams params = SnsEndpointParams.builder().region(region).build(); | ||
| try { | ||
| Endpoint endpoint = CompletableFutureUtils.joinLikeSync(endpointProvider.resolveEndpoint(params)); | ||
| URI url = endpoint.url(); | ||
| LOG.debug(() -> String.format("Resolved endpoint %s for region %s", url, region)); | ||
| return url; | ||
| } catch (SdkClientException e) { | ||
| throw SdkClientException.create("Unable to resolve SNS endpoint for region " + region, e); | ||
| } | ||
| } | ||
|
|
||
| public String signingCertCommonName() { | ||
| String commonName = signingCertCommonNameInternal(); | ||
| LOG.debug(() -> String.format("Resolved common name %s for region %s", commonName, region)); | ||
| return commonName; | ||
| } | ||
|
|
||
| private String signingCertCommonNameInternal() { | ||
| // If we don't know about this region, try to guess common name | ||
| if (!Region.regions().contains(region)) { | ||
| // Find the partition where it belongs by checking the region against the published pattern for known partitions. | ||
| // e.g. 'us-gov-west-3' would match the 'aws-us-gov' partition. | ||
| // This will return the 'aws' partition if it fails to match any partition. | ||
| PartitionMetadata partitionMetadata = PartitionMetadata.of(region); | ||
| return "sns." + partitionMetadata.dnsSuffix(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If new regions is added and not updated it here (for example we forget), this would silently produce "sns.amazonaws.com". Is this expected?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean by added?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a region is added in Region.regions()
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if the region is in the list, it wouldn't enter this code block, it would go through the switch statement and produce
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. Yeh I meant to ask the behavior in the switch statement, commented on the wrong line number. |
||
| } | ||
|
|
||
| String regionId = region.id(); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw there's a TODO comment in v1:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK they have implemented that pattern (same for opt-in regions) so it's not necessary. |
||
| switch (regionId) { | ||
| case "cn-north-1": | ||
| return "sns-cn-north-1.amazonaws.com.cn"; | ||
| case "cn-northwest-1": | ||
| return "sns-cn-northwest-1.amazonaws.com.cn"; | ||
| case "us-gov-west-1": | ||
| case "us-gov-east-1": | ||
| return "sns-us-gov-west-1.amazonaws.com"; | ||
| case "us-iso-east-1": | ||
| return "sns-us-iso-east-1.c2s.ic.gov"; | ||
| case "us-isob-east-1": | ||
| return "sns-us-isob-east-1.sc2s.sgov.gov"; | ||
| case "us-isof-east-1": | ||
| return "sns-signing.us-isof-east-1.csp.hci.ic.gov"; | ||
| case "us-isof-south-1": | ||
| return "sns-signing.us-isof-south-1.csp.hci.ic.gov"; | ||
| case "eu-isoe-west-1": | ||
| return "sns-signing.eu-isoe-west-1.cloud.adc-e.uk"; | ||
| case "eusc-de-east-1": | ||
| return "sns-signing.eusc-de-east-1.amazonaws.eu"; | ||
| case "ap-east-1": | ||
| case "ap-east-2": | ||
| case "ap-south-2": | ||
| case "ap-southeast-5": | ||
| case "ap-southeast-6": | ||
| case "ap-southeast-7": | ||
| case "me-south-1": | ||
| case "me-central-1": | ||
| case "eu-south-1": | ||
| case "eu-south-2": | ||
| case "eu-central-2": | ||
| case "af-south-1": | ||
| case "ap-southeast-3": | ||
| case "ap-southeast-4": | ||
| case "il-central-1": | ||
| case "ca-west-1": | ||
| case "mx-central-1": | ||
| return "sns-signing." + regionId + ".amazonaws.com"; | ||
| default: | ||
| return "sns.amazonaws.com"; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| /* | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"). | ||
| * You may not use this file except in compliance with the License. | ||
| * A copy of the License is located at | ||
| * | ||
| * http://aws.amazon.com/apache2.0 | ||
| * | ||
| * or in the "license" file accompanying this file. This file is distributed | ||
| * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
| * express or implied. See the License for the specific language governing | ||
| * permissions and limitations under the License. | ||
| */ | ||
|
|
||
| package software.amazon.awssdk.messagemanager.sns.internal; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
| import static org.mockito.ArgumentMatchers.any; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.verify; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| import java.util.stream.Stream; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
| import org.mockito.ArgumentCaptor; | ||
| import software.amazon.awssdk.regions.Region; | ||
| import software.amazon.awssdk.services.sns.endpoints.SnsEndpointParams; | ||
| import software.amazon.awssdk.services.sns.endpoints.SnsEndpointProvider; | ||
|
|
||
| public class SnsHostProviderTest { | ||
|
Check warning on line 33 in services-custom/sns-message-manager/src/test/java/software/amazon/awssdk/messagemanager/sns/internal/SnsHostProviderTest.java
|
||
|
|
||
| @ParameterizedTest | ||
| @MethodSource("commonNameTestCases") | ||
| void signingCertCommonName_returnsCorrectNameForRegion(CommonNameTestCase tc) { | ||
| SnsHostProvider hostProvider = new SnsHostProvider(Region.of(tc.region)); | ||
| assertThat(hostProvider.signingCertCommonName()).isEqualTo(tc.expectedCommonName); | ||
| } | ||
|
|
||
| @Test | ||
| void regionalEndpoint_delegatesToEndpointProvider() { | ||
| SnsEndpointProvider mockProvider = mock(SnsEndpointProvider.class); | ||
| SnsEndpointProvider realProvider = SnsEndpointProvider.defaultProvider(); | ||
|
|
||
| when(mockProvider.resolveEndpoint(any(SnsEndpointParams.class))).thenAnswer( | ||
| i -> realProvider.resolveEndpoint(i.getArgument(0, SnsEndpointParams.class))); | ||
|
|
||
| ArgumentCaptor<SnsEndpointParams> paramsCaptor = ArgumentCaptor.forClass(SnsEndpointParams.class); | ||
|
|
||
| Region region = Region.US_WEST_2; | ||
| SnsHostProvider hostProvider = new SnsHostProvider(region, mockProvider); | ||
| hostProvider.regionalEndpoint(); | ||
|
|
||
| verify(mockProvider).resolveEndpoint(paramsCaptor.capture()); | ||
| assertThat(paramsCaptor.getValue().region()).isEqualTo(region); | ||
| } | ||
|
|
||
| private static Stream<CommonNameTestCase> commonNameTestCases() { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ported from the v1 manager tests. |
||
| return Stream.of( | ||
| // gov regions | ||
| new CommonNameTestCase("us-gov-west-1", "sns-us-gov-west-1.amazonaws.com"), | ||
| new CommonNameTestCase("us-gov-east-1", "sns-us-gov-west-1.amazonaws.com"), | ||
|
|
||
| // cn regions | ||
| new CommonNameTestCase("cn-north-1", "sns-cn-north-1.amazonaws.com.cn"), | ||
| new CommonNameTestCase("cn-northwest-1", "sns-cn-northwest-1.amazonaws.com.cn"), | ||
|
|
||
| // opt-in regions | ||
| new CommonNameTestCase("me-south-1", "sns-signing.me-south-1.amazonaws.com"), | ||
| new CommonNameTestCase("ap-east-1", "sns-signing.ap-east-1.amazonaws.com"), | ||
| new CommonNameTestCase("me-south-1", "sns-signing.me-south-1.amazonaws.com"), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: seems to be dupilcate test case with line 71
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! I double checked the v1 test that I copied this from and it's duplicated there too. |
||
| new CommonNameTestCase("ap-east-2", "sns-signing.ap-east-2.amazonaws.com"), | ||
| new CommonNameTestCase("ap-southeast-5", "sns-signing.ap-southeast-5.amazonaws.com"), | ||
| new CommonNameTestCase("ap-southeast-6", "sns-signing.ap-southeast-6.amazonaws.com"), | ||
| new CommonNameTestCase("ap-southeast-7", "sns-signing.ap-southeast-7.amazonaws.com"), | ||
| new CommonNameTestCase("mx-central-1", "sns-signing.mx-central-1.amazonaws.com"), | ||
|
|
||
| // iso regions | ||
| new CommonNameTestCase("us-iso-east-1", "sns-us-iso-east-1.c2s.ic.gov"), | ||
| new CommonNameTestCase("us-isob-east-1", "sns-us-isob-east-1.sc2s.sgov.gov"), | ||
| new CommonNameTestCase("us-isof-east-1", "sns-signing.us-isof-east-1.csp.hci.ic.gov"), | ||
| new CommonNameTestCase("us-isof-south-1", "sns-signing.us-isof-south-1.csp.hci.ic.gov"), | ||
| new CommonNameTestCase("eu-isoe-west-1", "sns-signing.eu-isoe-west-1.cloud.adc-e.uk"), | ||
|
|
||
| //eusc | ||
| new CommonNameTestCase("eusc-de-east-1", "sns-signing.eusc-de-east-1.amazonaws.eu"), | ||
|
|
||
| // other regions | ||
| new CommonNameTestCase("us-east-1", "sns.amazonaws.com"), | ||
| new CommonNameTestCase("us-west-1", "sns.amazonaws.com"), | ||
|
|
||
| // unknown regions | ||
| new CommonNameTestCase("us-east-9", "sns.amazonaws.com"), | ||
| new CommonNameTestCase("foo-bar-1", "sns.amazonaws.com"), | ||
| new CommonNameTestCase("cn-northwest-9", "sns.amazonaws.com.cn") | ||
|
|
||
|
|
||
| ); | ||
| } | ||
|
|
||
| private static class CommonNameTestCase { | ||
| private String region; | ||
| private String expectedCommonName; | ||
|
|
||
| CommonNameTestCase(String region, String expectedCommonName) { | ||
| this.region = region; | ||
| this.expectedCommonName = expectedCommonName; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return region + " - " + expectedCommonName; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ported from v1: https://github.com/aws/aws-sdk-java/blob/d866126817fcc10595a3e7cd4b40efe626f05a7c/aws-java-sdk-sns/src/main/java/com/amazonaws/services/sns/message/SnsMessageManager.java#L119