From e5fb5b723c68e692edabfd9802b9546d1690d394 Mon Sep 17 00:00:00 2001 From: John Viegas Date: Sun, 26 Jan 2025 12:19:40 -0800 Subject: [PATCH] Add test case for service with MultiAuth only and not using Sigv4 --- .../amazon/awssdk/codegen/AddMetadata.java | 3 +- .../awssdk/codegen/utils/AuthUtils.java | 8 + .../sigv4aonly/customization.config | 3 + .../sigv4aonly/endpoint-rule-set.json | 69 +++++++ .../sigv4aonly/endpoint-tests.json | 5 + .../sigv4aonly/service-2.json | 51 +++++ .../multiauth/Sigv4aOnlyMultiAuthTest.java | 193 ++++++++++++++++++ 7 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/customization.config create mode 100644 test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-rule-set.json create mode 100644 test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-tests.json create mode 100644 test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json create mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aOnlyMultiAuthTest.java diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/AddMetadata.java b/codegen/src/main/java/software/amazon/awssdk/codegen/AddMetadata.java index 86a6a6053ae5..8e921fac0a2f 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/AddMetadata.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/AddMetadata.java @@ -74,7 +74,8 @@ public static Metadata constructMetadata(ServiceModel serviceModel, .withJsonVersion(serviceMetadata.getJsonVersion()) .withEndpointPrefix(serviceMetadata.getEndpointPrefix()) .withSigningName(serviceMetadata.getSigningName()) - .withAuthType(AuthType.fromValue(serviceMetadata.getSignatureVersion())) + .withAuthType(serviceMetadata.getSignatureVersion() !=null ? + AuthType.fromValue(serviceMetadata.getSignatureVersion()) : null) .withUid(serviceMetadata.getUid()) .withServiceId(serviceMetadata.getServiceId()) .withSupportsH2(supportsH2(serviceMetadata)) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/utils/AuthUtils.java b/codegen/src/main/java/software/amazon/awssdk/codegen/utils/AuthUtils.java index d139e44459ce..f870ceea284d 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/utils/AuthUtils.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/utils/AuthUtils.java @@ -18,6 +18,7 @@ import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; import software.amazon.awssdk.codegen.model.intermediate.OperationModel; import software.amazon.awssdk.codegen.model.service.AuthType; +import software.amazon.awssdk.utils.CollectionUtils; public final class AuthUtils { private AuthUtils() { @@ -76,6 +77,12 @@ private static boolean isServiceSigv4a(IntermediateModel model) { private static boolean isServiceAwsAuthType(IntermediateModel model) { AuthType authType = model.getMetadata().getAuthType(); + if (authType == null && !CollectionUtils.isNullOrEmpty(model.getMetadata().getAuth())) { + return model.getMetadata().getAuth().stream() + .map(AuthType::value) + .map(AuthType::fromValue) + .anyMatch(AuthUtils::isAuthTypeAws); + } return isAuthTypeAws(authType); } @@ -85,6 +92,7 @@ private static boolean isAuthTypeAws(AuthType authType) { } switch (authType) { + case V4A: case V4: case S3: case S3V4: diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/customization.config b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/customization.config new file mode 100644 index 000000000000..b5c73436bb3f --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/customization.config @@ -0,0 +1,3 @@ +{ + "skipEndpointTestGeneration": true +} \ No newline at end of file diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-rule-set.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-rule-set.json new file mode 100644 index 000000000000..d308f4496a61 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-rule-set.json @@ -0,0 +1,69 @@ +{ + "version": "1.3", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": true, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "ApiType": { + "required": true, + "documentation": "Parameter to determine whether current API is a control plane or dataplane API", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "ApiType" + }, + "onlySigv4a" + ] + } + ], + "endpoint": { + "url": "https://only-region.{Region}.on.aws", + "properties": { + "authSchemes": [ + { + "name": "sigv4a", + "signingName": "from-endpoint-params", + "signingRegionSet": [ + "*" + ] + } + ] + }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "ApiType" + }, + "NoEndpointSigningProperties" + ] + } + ], + "endpoint": { + "url": "https://only-region.{Region}.on.aws", + "properties": { + "authSchemes": [ + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] +} \ No newline at end of file diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-tests.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-tests.json new file mode 100644 index 000000000000..f94902ff9d99 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/endpoint-tests.json @@ -0,0 +1,5 @@ +{ + "testCases": [ + ], + "version": "1.0" +} \ No newline at end of file diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json new file mode 100644 index 000000000000..937541733c07 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json @@ -0,0 +1,51 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2016-03-11", + "endpointPrefix":"internalconfig", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"AwsSigv4aMultiAuthService", + "serviceFullName":"AWS Multi Auth Service", + "serviceId":"Sigv4aauth", + "targetPrefix":"Sigv4aauth", + "auth":["aws.auth#sigv4a"], + "timestampFormat":"unixTimestamp", + "uid":"restjson-2016-03-11" + }, + "operations":{ + "simpleOperationWithNoEndpointParams":{ + "name":"simpleOperationWithNoEndpointParams", + "http":{ + "method":"POST", + "requestUri":"/2016-03-11/simpleOperationWithNoEndpointParams" + }, + "input":{"shape":"sampleRequest"}, + "staticContextParams":{ + "ApiType":{"value":"NoEndpointSigningProperties"} + } + }, + "simpleOperationWithEndpointParams":{ + "name":"simpleOperationWithEndpointParams", + "http":{ + "method":"POST", + "requestUri":"/2016-03-11/multiAuthWithOnlySigv4aAndSigv4" + }, + "input":{"shape":"sampleRequest"}, + "staticContextParams":{ + "ApiType":{"value":"onlySigv4a"} + } + } + }, + "shapes": { + "sampleRequest": { + "type": "structure", + "members": { + "StringMember": { + "shape": "String" + } + } + }, + "String":{"type":"string"} + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aOnlyMultiAuthTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aOnlyMultiAuthTest.java new file mode 100644 index 000000000000..736d909edad2 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aOnlyMultiAuthTest.java @@ -0,0 +1,193 @@ +/* + * 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.services.multiauth; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.util.concurrent.CompletableFuture; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner; +import software.amazon.awssdk.http.auth.aws.signer.RegionSet; +import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; +import software.amazon.awssdk.http.auth.spi.signer.AsyncSignRequest; +import software.amazon.awssdk.http.auth.spi.signer.AsyncSignedRequest; +import software.amazon.awssdk.http.auth.spi.signer.BaseSignRequest; +import software.amazon.awssdk.http.auth.spi.signer.HttpSigner; +import software.amazon.awssdk.http.auth.spi.signer.SignRequest; +import software.amazon.awssdk.http.auth.spi.signer.SignedRequest; +import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; +import software.amazon.awssdk.identity.spi.IdentityProvider; +import software.amazon.awssdk.identity.spi.IdentityProviders; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthClient; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; +import software.amazon.awssdk.utils.CompletableFutureUtils; + +public class Sigv4aOnlyMultiAuthTest { + + + private static final String MOCK_HTTP_CLIENT_NAME = "MockHttpClient"; + private static final String EXPECTED_EXCEPTION_MESSAGE = "expected exception"; + private static final String CRT_DEPENDENCY_ERROR_MESSAGE = + "You must add a dependency on the 'software.amazon.awssdk:http-auth-aws-crt' module to enable the CRT-V4a signing " + + "feature"; + + private final EnvironmentVariableHelper environmentVariableHelper = new EnvironmentVariableHelper(); + + @Mock + private SdkHttpClient mockHttpClient; + + private static AuthScheme authScheme(String schemeId, HttpSigner signer) { + return new AuthScheme() { + @Override + public String schemeId() { + return schemeId; + } + + @Override + public IdentityProvider identityProvider(IdentityProviders providers) { + return providers.identityProvider(AwsCredentialsIdentity.class); + } + + @Override + public HttpSigner signer() { + return signer; + } + }; + } + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + when(mockHttpClient.clientName()).thenReturn(MOCK_HTTP_CLIENT_NAME); + when(mockHttpClient.prepareRequest(any())).thenThrow(new RuntimeException(EXPECTED_EXCEPTION_MESSAGE)); + } + + @AfterEach + void tearDown() { + environmentVariableHelper.reset(); + } + + public static class CapturingSigner implements HttpSigner { + private BaseSignRequest request; + + @Override + public SignedRequest sign(SignRequest request) { + this.request = request; + throw new RuntimeException("stop"); + } + + @Override + public CompletableFuture signAsync( + AsyncSignRequest request) { + this.request = request; + return CompletableFutureUtils.failedFuture(new RuntimeException("stop")); + } + } + + @Nested + @DisplayName("Fall Back behaviour with No CRT library") + class FallbackBehaviorTests { + + @Test + @DisplayName("Should throw error when Sigv4a is ") + void shouldThrowErrorWhenNoFallback() { + Sigv4AauthClient client = Sigv4AauthClient.builder() + .httpClient(mockHttpClient) + .region(Region.US_WEST_2) + .build(); + + assertThatThrownBy(() -> client.simpleOperationWithNoEndpointParams(r -> r.stringMember(""))) + .hasMessageContaining(CRT_DEPENDENCY_ERROR_MESSAGE); + } + + } + + @Nested + @DisplayName("Region Configuration Tests") + class RegionConfigurationTests { + @Test + @DisplayName("Client Configured Auth Scheme RegionSet take highest precedence ") + void clientConfiguredRegionSetTakesPrecedenceOverEndpointRuleSet() { + environmentVariableHelper.set(SdkSystemSetting.AWS_SIGV4A_SIGNING_REGION_SET, "us-west-2,us-west-1"); + + CapturingSigner signer = new CapturingSigner(); + + Sigv4AauthClient sigv4AauthClient = Sigv4AauthClient.builder() + .httpClient(mockHttpClient) + .putAuthScheme(authScheme("aws.auth#sigv4a", signer)) + .region(Region.EU_CENTRAL_1) + .build(); + + Assertions.assertThatThrownBy(() -> sigv4AauthClient.simpleOperationWithEndpointParams(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + Assertions.assertThat(signer.request.property(AwsV4aHttpSigner.REGION_SET)).isEqualTo(RegionSet.create("us-west-2," + + "us-west-1" + )); + } + + @Test + @DisplayName("Endpoint Rules Auth Scheme RegionSet takes precedence over Endpoint region ") + void endpointRuleSetRegionSetTakesPrecedenceOverEndpointRegion() { + + CapturingSigner signer = new CapturingSigner(); + + Sigv4AauthClient sigv4AauthClient = Sigv4AauthClient.builder() + .httpClient(mockHttpClient) + .putAuthScheme(authScheme("aws.auth#sigv4a", signer)) + .region(Region.EU_CENTRAL_1) + .build(); + + Assertions.assertThatThrownBy(() -> sigv4AauthClient.simpleOperationWithEndpointParams(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + Assertions.assertThat(signer.request.property(AwsV4aHttpSigner.REGION_SET)).isEqualTo(RegionSet.GLOBAL); + } + + @Test + @DisplayName("Endpoint region takes precedence when No RegionSet defined ") + void endpointRegionTakesPrecedenceWhenNoRegionSetFound() { + + CapturingSigner signer = new CapturingSigner(); + + Sigv4AauthClient sigv4AauthClient = Sigv4AauthClient.builder() + .httpClient(mockHttpClient) + .putAuthScheme(authScheme("aws.auth#sigv4a", signer)) + .region(Region.EU_CENTRAL_1) + .build(); + + Assertions.assertThatThrownBy(() -> sigv4AauthClient.simpleOperationWithNoEndpointParams(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + Assertions.assertThat(signer.request.property(AwsV4aHttpSigner.REGION_SET)) + .isEqualTo(RegionSet.create(Region.EU_CENTRAL_1.id())); + } + } + + +}