From 082a218fb7d6c8560bf6aab88fc622b23248a857 Mon Sep 17 00:00:00 2001 From: John Viegas Date: Sat, 25 Jan 2025 21:46:50 -0800 Subject: [PATCH 1/6] Override RegionSet in EnpointResolverInterceptor after fetching the Signing Properties from Endpoint rules --- .../scheme/AuthSchemeInterceptorSpec.java | 17 +- .../EndpointResolverInterceptorSpec.java | 73 ++++- ...-sigv4a-value-auth-scheme-interceptor.java | 12 +- .../endpoint-rule-set.json | 90 +++++++ .../endpoint-resolve-interceptor-preSra.java | 3 +- .../rules/endpoint-resolve-interceptor.java | 3 +- .../multiauth/endpoint-rule-set.json | 143 +++++----- .../multiauth/service-2.json | 63 ++++- .../auth/Sigv4aSigningRegionSetTest.java | 3 +- .../EndpointInterceptorTests.java | 19 +- .../MultiAuthSigningPropertiesTest.java | 253 ++++++++++++++++++ .../multiauth/Sigv4aMultiAuthTest.java | 143 ---------- 12 files changed, 569 insertions(+), 253 deletions(-) create mode 100644 codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/ops-with-auth-sigv4a-value/endpoint-rule-set.json create mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/MultiAuthSigningPropertiesTest.java delete mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aMultiAuthTest.java diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index c7fdbab7ffee..f87fafff4092 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -63,6 +63,7 @@ import software.amazon.awssdk.metrics.MetricCollector; import software.amazon.awssdk.metrics.SdkMetric; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.utils.CollectionUtils; import software.amazon.awssdk.utils.Logger; import software.amazon.awssdk.utils.Validate; @@ -452,19 +453,13 @@ private TypeName toTypeName(Object valueType) { private void generateSigv4aRegionSet(MethodSpec.Builder builder) { if (authSchemeSpecUtils.usesSigV4a()) { builder.addStatement( - "$T regionSet = executionAttributes.getOptionalAttribute($T.AWS_SIGV4A_SIGNING_REGION_SET)\n" + - " .filter(regions -> !regions.isEmpty())\n" + - " .map(regions -> $T.create(String.join(\", \", regions)))\n" + - " .orElseGet(() -> {\n" + - " $T fallbackRegion = executionAttributes.getAttribute($T.AWS_REGION);\n" + - " return fallbackRegion != null ? $T.create(fallbackRegion.toString()) : null;\n" + - " });", - RegionSet.class, AwsExecutionAttribute.class, - RegionSet.class, Region.class, AwsExecutionAttribute.class, + "executionAttributes.getOptionalAttribute($T.AWS_SIGV4A_SIGNING_REGION_SET)\n" + + " .filter(regionSet -> !$T.isNullOrEmpty(regionSet))\n" + + " .ifPresent(nonEmptyRegionSet -> builder.regionSet($T.create(nonEmptyRegionSet)))", + AwsExecutionAttribute.class, + CollectionUtils.class, RegionSet.class ); - - builder.addStatement("builder.regionSet(regionSet)"); } } } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java index 78ec4b6f9ce8..a0fe830293a9 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java @@ -99,6 +99,7 @@ public class EndpointResolverInterceptorSpec implements ClassSpec { private final JmesPathAcceptorGenerator jmesPathGenerator; private final boolean dependsOnHttpAuthAws; private final boolean useSraAuth; + private final boolean multiAuthSigv4a; public EndpointResolverInterceptorSpec(IntermediateModel model) { @@ -116,6 +117,7 @@ public EndpointResolverInterceptorSpec(IntermediateModel model) { supportedAuthSchemes.contains(AwsV4aAuthScheme.class); this.useSraAuth = new AuthSchemeSpecUtils(model).useSraAuth(); + this.multiAuthSigv4a = new AuthSchemeSpecUtils(model).usesSigV4a(); } @Override @@ -155,6 +157,10 @@ public TypeSpec poetSpec() { b.addMethod(signerProviderMethod()); } + if (multiAuthSigv4a) { + b.addMethod(createHasRegionSetMethod()); + b.addMethod(createUpdateAuthSchemeWithRegionSetMethod()); + } endpointParamsKnowledgeIndex.addAccountIdMethodsIfPresent(b); return b.build(); } @@ -192,7 +198,9 @@ private MethodSpec modifyRequestMethod(String endpointAuthSchemeStrategyFieldNam endpointRulesSpecUtils.providerInterfaceName(), providerVar, SdkInternalExecutionAttribute.class); b.beginControlFlow("try"); b.addStatement("long resolveEndpointStart = $T.nanoTime()", System.class); - b.addStatement("$T endpoint = $N.resolveEndpoint(ruleParams(result, executionAttributes)).join()", + b.addStatement("$T endpointParams = ruleParams(result, executionAttributes)", + endpointRulesSpecUtils.parametersClassName()); + b.addStatement("$T endpoint = $N.resolveEndpoint(endpointParams).join()", Endpoint.class, providerVar); b.addStatement("$1T resolveEndpointDuration = $1T.ofNanos($2T.nanoTime() - resolveEndpointStart)", Duration.class, System.class); @@ -219,7 +227,11 @@ private MethodSpec modifyRequestMethod(String endpointAuthSchemeStrategyFieldNam SelectedAuthScheme.class, SdkInternalExecutionAttribute.class); b.beginControlFlow("if (endpointAuthSchemes != null && selectedAuthScheme != null)"); b.addStatement("selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme)"); - + if (multiAuthSigv4a) { + b.beginControlFlow("if(!hasRegionSet(selectedAuthScheme))"); + b.addStatement("selectedAuthScheme = updateAuthSchemeWithRegionSet(selectedAuthScheme, endpointParams)"); + b.endControlFlow(); + } b.addStatement("executionAttributes.putAttribute($T.SELECTED_AUTH_SCHEME, selectedAuthScheme)", SdkInternalExecutionAttribute.class); b.endControlFlow(); @@ -774,7 +786,7 @@ private static CodeBlock copyV4EndpointSignerPropertiesToAuth() { return code.build(); } - private static CodeBlock copyV4aEndpointSignerPropertiesToAuth() { + private CodeBlock copyV4aEndpointSignerPropertiesToAuth() { CodeBlock.Builder code = CodeBlock.builder(); code.beginControlFlow("if (endpointAuthScheme instanceof $T)", SigV4aAuthScheme.class); @@ -784,10 +796,12 @@ private static CodeBlock copyV4aEndpointSignerPropertiesToAuth() { code.addStatement("option.putSignerProperty($T.DOUBLE_URL_ENCODE, !v4aAuthScheme.disableDoubleEncoding())", AwsV4aHttpSigner.class); code.endControlFlow(); - - code.beginControlFlow("if (v4aAuthScheme.signingRegionSet() != null)"); + if (multiAuthSigv4a) { + code.beginControlFlow("if (!hasRegionSet(selectedAuthScheme) && v4aAuthScheme.signingRegionSet() != null)"); + } else { + code.beginControlFlow("if (v4aAuthScheme.signingRegionSet() != null)"); + } code.addStatement("$1T regionSet = $1T.create(v4aAuthScheme.signingRegionSet())", RegionSet.class); - code.addStatement("option.putSignerProperty($T.REGION_SET, regionSet)", AwsV4aHttpSigner.class); code.endControlFlow(); @@ -882,4 +896,51 @@ private MethodSpec constructorMethodSpec(String endpointAuthSchemeFieldName) { return b.build(); } + private MethodSpec createHasRegionSetMethod() { + TypeVariableName tExtendsIdentity = TypeVariableName.get("T", Identity.class); + TypeName selectedAuthSchemeOfT = ParameterizedTypeName.get(ClassName.get(SelectedAuthScheme.class), + TypeVariableName.get("T")); + + return + MethodSpec.methodBuilder("hasRegionSet") + .addModifiers(Modifier.PRIVATE) + .addTypeVariable(tExtendsIdentity) + .returns(boolean.class) + .addParameter(selectedAuthSchemeOfT, "selectedAuthScheme") + .addCode( + CodeBlock.builder() + .addStatement("return selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID)" + + " && selectedAuthScheme.authSchemeOption().signerProperty($T.REGION_SET) != " + + "null", AwsV4aAuthScheme.class, AwsV4aHttpSigner.class) + .build()) + .build(); + } + + private MethodSpec createUpdateAuthSchemeWithRegionSetMethod() { + TypeVariableName tExtendsIdentity = TypeVariableName.get("T", Identity.class); + TypeName selectedAuthSchemeOfT = ParameterizedTypeName.get( + ClassName.get(SelectedAuthScheme.class), + TypeVariableName.get("T") + ); + + return MethodSpec.methodBuilder("updateAuthSchemeWithRegionSet") + .addModifiers(Modifier.PRIVATE) + .addTypeVariable(tExtendsIdentity) + .returns(selectedAuthSchemeOfT) + .addParameter(selectedAuthSchemeOfT, "selectedAuthScheme") + .addParameter(endpointRulesSpecUtils.parametersClassName(), "endpointParams") + .addCode(CodeBlock.builder() + .addStatement("$T optionBuilder = selectedAuthScheme.authSchemeOption().toBuilder()", + ClassName.get(AuthSchemeOption.Builder.class)) + .addStatement("$T regionSet = $T.create(endpointParams.region().id())", + RegionSet.class, RegionSet.class) + .addStatement("optionBuilder.putSignerProperty($T.REGION_SET, regionSet)", + AwsV4aHttpSigner.class) + .addStatement("return new $T<>(selectedAuthScheme.identity(), " + + "selectedAuthScheme.signer(), optionBuilder.build())", + SelectedAuthScheme.class) + .build()) + .build(); + } + } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java index b2117fd2af76..70b901338096 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java @@ -35,6 +35,7 @@ import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.database.auth.scheme.DatabaseAuthSchemeParams; import software.amazon.awssdk.services.database.auth.scheme.DatabaseAuthSchemeProvider; +import software.amazon.awssdk.utils.CollectionUtils; import software.amazon.awssdk.utils.Logger; import software.amazon.awssdk.utils.Validate; @@ -88,14 +89,9 @@ private DatabaseAuthSchemeParams authSchemeParams(SdkRequest request, ExecutionA DatabaseAuthSchemeParams.Builder builder = DatabaseAuthSchemeParams.builder().operation(operation); Region region = executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION); builder.region(region); - RegionSet regionSet = executionAttributes.getOptionalAttribute(AwsExecutionAttribute.AWS_SIGV4A_SIGNING_REGION_SET) - .filter(regions -> !regions.isEmpty()).map(regions -> RegionSet.create(String.join(", ", regions))) - .orElseGet(() -> { - Region fallbackRegion = executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION); - return fallbackRegion != null ? RegionSet.create(fallbackRegion.toString()) : null; - }); - ; - builder.regionSet(regionSet); + executionAttributes.getOptionalAttribute(AwsExecutionAttribute.AWS_SIGV4A_SIGNING_REGION_SET) + .filter(regionSet -> !CollectionUtils.isNullOrEmpty(regionSet)) + .ifPresent(nonEmptyRegionSet -> builder.regionSet(RegionSet.create(nonEmptyRegionSet))); return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/ops-with-auth-sigv4a-value/endpoint-rule-set.json b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/ops-with-auth-sigv4a-value/endpoint-rule-set.json new file mode 100644 index 000000000000..502f3ffeb19d --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/ops-with-auth-sigv4a-value/endpoint-rule-set.json @@ -0,0 +1,90 @@ +{ + "version": "1.2", + "serviceId": "Database Service", + "parameters": { + "region": { + "type": "string", + "builtIn": "AWS::Region", + "required": true, + "documentation": "The region to send requests to" + }, + "useDualStackEndpoint": { + "type": "boolean", + "builtIn": "AWS::UseDualStack" + }, + "useFIPSEndpoint": { + "type": "boolean", + "builtIn": "AWS::UseFIPS" + }, + "AccountId": { + "type": "String", + "builtIn": "AWS::Auth::AccountId" + }, + "operationContextParam": { + "type": "string" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "region" + } + ], + "assign": "partitionResult" + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "endpointId" + } + ] + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "useFIPSEndpoint" + } + ] + } + ], + "error": "FIPS endpoints not supported with multi-region endpoints", + "type": "error" + }, + { + "endpoint": { + "url": "https://{endpointId}.query.{partitionResult#dualStackDnsSuffix}", + "properties": { + "authSchemes": [ + { + "name": "sigv4a", + "signingName": "query", + "signingRegionSet": ["*"] + } + ] + } + }, + "type": "endpoint" + } + ], + "type": "tree" + } + ], + "type": "tree" + } + ] +} + diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java index c8c4028f9295..f9a6c52d8512 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java @@ -68,7 +68,8 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut .getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); try { long resolveEndpointStart = System.nanoTime(); - Endpoint endpoint = provider.resolveEndpoint(ruleParams(result, executionAttributes)).join(); + QueryEndpointParams endpointParams = ruleParams(result, executionAttributes); + Endpoint endpoint = provider.resolveEndpoint(endpointParams).join(); Duration resolveEndpointDuration = Duration.ofNanos(System.nanoTime() - resolveEndpointStart); Optional metricCollector = executionAttributes .getOptionalAttribute(SdkExecutionAttribute.API_CALL_METRIC_COLLECTOR); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java index 138b2cf5fe07..f8a4a1ebd78f 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java @@ -56,7 +56,8 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut .getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); try { long resolveEndpointStart = System.nanoTime(); - Endpoint endpoint = provider.resolveEndpoint(ruleParams(result, executionAttributes)).join(); + QueryEndpointParams endpointParams = ruleParams(result, executionAttributes); + Endpoint endpoint = provider.resolveEndpoint(endpointParams).join(); Duration resolveEndpointDuration = Duration.ofNanos(System.nanoTime() - resolveEndpointStart); Optional metricCollector = executionAttributes .getOptionalAttribute(SdkExecutionAttribute.API_CALL_METRIC_COLLECTOR); diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/endpoint-rule-set.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/endpoint-rule-set.json index cf58fb6fe996..80a2e9c7e0ba 100644 --- a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/endpoint-rule-set.json +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/endpoint-rule-set.json @@ -46,9 +46,70 @@ }, "StringClientContextParam": { "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" + }, + "RegionDefinedInRules" + ] + } + ], + "endpoint": { + "url": "https://only-region.{Region}.on.aws", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "greengrass", + "signingRegion": "us-gov-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + }, { "conditions": [ { @@ -66,81 +127,29 @@ { "conditions": [ { - "fn": "isSet", + "fn": "stringEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "ApiType" + }, + "OnlyRegion" ] - }, - { - "fn": "parseURL", - "argv": [ - { - "ref": "Endpoint" - } - ], - "assign": "url" } ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", - "type": "error" - }, - { - "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", - "type": "error" - }, + "endpoint": { + "url": "https://only-region.{Region}.on.aws", + "properties": { + "authSchemes": [ { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "{Region}", - "signingName": "restjson" - } - ] - }, - "headers": {} - }, - "type": "endpoint" + "name": "sigv4", + "signingName": "onlyRegion", + "signingRegion": "us-east-2" } ] - } - ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/service-2.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/service-2.json index e0be3bee5ca7..1e467bbc8c40 100644 --- a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/service-2.json +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/multiauth/service-2.json @@ -14,27 +14,68 @@ "uid":"restjson-2016-03-11" }, "operations":{ - "sigv4aOperation":{ - "name":"sigv4a", + "multiAuthWithOnlySigv4a":{ + "name":"multiAuthWithOnlySigv4a", "http":{ "method":"POST", - "requestUri":"/2016-03-11/sigv4aoperation" + "requestUri":"/2016-03-11/multiAuthWithOnlySigv4a" }, - "input":{"shape":"sigv4aShape"}, - "auth": ["aws.auth#sigv4a"] + "input":{"shape":"sampleRequest"}, + "auth": ["aws.auth#sigv4a"], + "staticContextParams":{ + "ApiType":{"value":"NoEndpointSigningProperties"} + } }, - "sigv4AndSigv4aOperation":{ - "name":"sigv4a", + "multiAuthWithOnlySigv4aAndSigv4":{ + "name":"multiAuthWithOnlySigv4aAndSigv4", "http":{ "method":"POST", - "requestUri":"/2016-03-11/sigv4andsigv4aoperation" + "requestUri":"/2016-03-11/multiAuthWithOnlySigv4aAndSigv4" + }, + "input":{"shape":"sampleRequest"}, + "auth": ["aws.auth#sigv4a", "aws.auth#sigv4"], + "staticContextParams":{ + "ApiType":{"value":"NoEndpointSigningProperties"} + } + }, + "legacySigv4WithEndpointsRules": { + "name": "legacySigv4WithEndpointsRules", + "http": { + "method": "GET", + "requestUri": "/legacySigv4WithEndpointsRules", + "responseCode": 200 + }, + "input": { + "shape": "sampleRequest" + }, + "documentation": "

Operation with EndpointParams w

", + "staticContextParams": { + "ApiType": { + "value": "RegionDefinedInRules" + } + } + }, + "multiAuthWithRegionSetInEndpointParams": { + "name": "multiAuthWithRegionSetInEndpointParams", + "http": { + "method": "GET", + "requestUri": "/multiAuthWithRegionSetInEndpointParams", + "responseCode": 200 }, - "input":{"shape":"sigv4aShape"}, - "auth": ["aws.auth#sigv4a", "aws.auth#sigv4"] + "input": { + "shape": "sampleRequest" + }, + "documentation": "

Operation with EndpointParams w

", + "auth": ["aws.auth#sigv4a", "aws.auth#sigv4"], + "staticContextParams": { + "ApiType": { + "value": "onlySigv4a" + } + } } }, "shapes": { - "sigv4aShape": { + "sampleRequest": { "type": "structure", "members": { "StringMember": { diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/auth/Sigv4aSigningRegionSetTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/auth/Sigv4aSigningRegionSetTest.java index 24c435b645bc..ba0c9144f9c2 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/auth/Sigv4aSigningRegionSetTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/auth/Sigv4aSigningRegionSetTest.java @@ -207,9 +207,8 @@ void resolvesSigv4aSigningRegionSet(TestCase testCase) { MultiauthClient client = builder.build(); try { - client.sigv4AndSigv4aOperation(b -> b.stringMember("test").build()); + client.multiAuthWithOnlySigv4aAndSigv4(b -> b.stringMember("test").build()); } catch (EndpointCapturingInterceptor.CaptureCompletedException e) { - // Expected } assertThat(interceptor.sigv4aSigningRegionSet()) .containsExactlyInAnyOrderElementsOf(testCase.expectedValues); diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java index 6ca37d6d3f73..d24456368fbd 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java @@ -15,20 +15,33 @@ package software.amazon.awssdk.services.endpointproviders; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; import org.junit.Test; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4aAuthScheme; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.endpoints.Endpoint; +import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; +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.scheme.AuthSchemeOption; 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; @@ -39,6 +52,7 @@ import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.regions.RegionScope; import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersAsyncClient; import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersAsyncClientBuilder; import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClient; @@ -187,8 +201,7 @@ public void async_endpointProviderReturnsHeaders_appendedToExistingRequest() { assertThat(interceptor.context.httpRequest().matchingHeaders("TestHeader")).containsExactly("TestValue", "TestValue0"); } - // TODO(sra-identity-auth): Enable for useSraAuth=true - /* + @Test public void sync_endpointProviderReturnsSignerProperties_overridesV4AuthSchemeResolverProperties() { RestJsonEndpointProvidersEndpointProvider defaultEndpointProvider = @@ -452,7 +465,7 @@ public void sync_endpointProviderReturnsV4aSignerProperties_executionAttributesF assertThat(interceptor.executionAttributes.getAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME)).isEqualTo("name-from-ep"); assertThat(interceptor.executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE)).isEqualTo(true); } - */ + private static AuthScheme authScheme(String schemeId, HttpSigner signer) { return new AuthScheme() { diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/MultiAuthSigningPropertiesTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/MultiAuthSigningPropertiesTest.java new file mode 100644 index 000000000000..d9ac20d3210e --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/MultiAuthSigningPropertiesTest.java @@ -0,0 +1,253 @@ +/* + * 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.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.StringJoiner; +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.AwsV4HttpSigner; +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.testutils.EnvironmentVariableHelper; +import software.amazon.awssdk.utils.CompletableFutureUtils; + +@DisplayName("Multi-Auth Tests") +class MultiAuthSigningPropertiesTest { + + 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; + + @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(); + } + + + @Nested + @DisplayName("Region Set Configuration Tests") + class RegionSetConfigurationTests { + + @Test + @DisplayName("Should use environment variable region set when provided") + void shouldUseEnvironmentVariableRegionSet() { + environmentVariableHelper.set(SdkSystemSetting.AWS_SIGV4A_SIGNING_REGION_SET, "us-west-2,us-west-1"); + CapturingSigner signer = new CapturingSigner(); + + MultiauthClient client = createMultiauthClient(signer); + + assertThatThrownBy(() -> client.multiAuthWithOnlySigv4a(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + assertThat(signer.request.property(AwsV4aHttpSigner.REGION_SET)) + .isEqualTo(RegionSet.create(Arrays.asList("us-west-2", "us-west-1"))); + } + + @Test + @DisplayName("Should fall back to client region when no environment variable is set") + void shouldFallBackToClientRegion() { + CapturingSigner signer = new CapturingSigner(); + MultiauthClient client = createMultiauthClient(signer); + + assertThatThrownBy(() -> client.multiAuthWithOnlySigv4a(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + assertThat(signer.request.property(AwsV4aHttpSigner.REGION_SET)) + .isEqualTo(RegionSet.create(Region.US_WEST_2.toString())); + } + + @Test + @DisplayName("Should use GLOBAL Regionset as defined in the endpoint rule set when no region set defined on client") + void endpointParamsDefinedAsGlobalUsedWhenNoRegionSetConfigured() { + CapturingSigner signer = new CapturingSigner(); + + MultiauthClient client = createMultiauthClient(signer); + + assertThatThrownBy(() -> client.multiAuthWithRegionSetInEndpointParams(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + assertThat(signer.request.property(AwsV4aHttpSigner.REGION_SET)) + .isEqualTo(RegionSet.GLOBAL); + } + + @Test + @DisplayName("Should use the Region set from Endpoint RuleSet when no RegionSet configured") + void clientApiConfiguredRegionSetTakePrecedenceOverEndpointRulesRegionSet() { + CapturingSigner signer = new CapturingSigner(); + MultiauthClient client = MultiauthClient.builder() + .httpClient(mockHttpClient) + .region(Region.US_WEST_2) + .putAuthScheme(authScheme("aws.auth#sigv4a", signer)) + .sigv4aRegionSet(RegionSet.create(new StringJoiner(",") + .add(Region.US_WEST_2.id()) + .add(Region.US_GOV_EAST_1.id()) + .toString())) + .build(); + + assertThatThrownBy(() -> client.multiAuthWithRegionSetInEndpointParams(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + assertThat(signer.request.property(AwsV4aHttpSigner.REGION_SET)) + .isEqualTo(RegionSet.create("us-west-2,us-gov-east-1")); + } + } + + @Nested + @DisplayName("Fallback Behavior Tests") + class FallbackBehaviorTests { + + @Test + @DisplayName("Should throw error when Sigv4a has no fallback to Sigv4") + void shouldThrowErrorWhenNoFallback() { + MultiauthClient client = MultiauthClient.builder() + .httpClient(mockHttpClient) + .region(Region.US_WEST_2) + .build(); + + assertThatThrownBy(() -> client.multiAuthWithOnlySigv4a(r -> r.stringMember(""))) + .hasMessageContaining(CRT_DEPENDENCY_ERROR_MESSAGE); + } + + @Test + @DisplayName("Should fall back to Sigv4 when Sigv4a is not available") + void shouldFallBackToSigv4() { + MultiauthClient client = createMultiauthClient(null); + + assertThatThrownBy(() -> client.multiAuthWithOnlySigv4aAndSigv4(r -> r.stringMember(""))) + .hasMessageContaining(EXPECTED_EXCEPTION_MESSAGE); + + verify(mockHttpClient).prepareRequest( + argThat(request -> request.httpRequest().firstMatchingHeader("Authorization").isPresent())); + } + } + + @Nested + @DisplayName("Region Configuration Tests") + class RegionConfigurationTests { + @Test + @DisplayName("Endpoint Rules Auth Scheme Region take highest precedence ") + void authSchemesParamsUpdatedWithStaticContextAndDefaultEndpointParams() { + environmentVariableHelper.set(SdkSystemSetting.AWS_SIGV4A_SIGNING_REGION_SET, "us-west-2,us-west-1"); + + CapturingSigner signer = new CapturingSigner(); + + MultiauthClient multiauthClient = MultiauthClient.builder() + .httpClient(mockHttpClient) + .putAuthScheme(authScheme("aws.auth#sigv4", signer)) + .region(Region.EU_CENTRAL_1) + .build(); + + Assertions.assertThatThrownBy(() -> multiauthClient.legacySigv4WithEndpointsRules(r -> r.stringMember(""))) + .hasMessageContaining("stop"); + + Assertions.assertThat(signer.request.property(AwsV4HttpSigner.REGION_NAME)).isEqualTo(Region.US_GOV_EAST_1.id()); + } + } + + + private MultiauthClient createMultiauthClient(CapturingSigner signer) { + MultiauthClientBuilder builder = MultiauthClient.builder() + .httpClient(mockHttpClient) + .region(Region.US_WEST_2); + + if (signer != null) { + builder.putAuthScheme(authScheme("aws.auth#sigv4a", signer)); + } + + return builder.build(); + } + + + + 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")); + } + } + + 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; + } + }; + } + +} \ No newline at end of file diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aMultiAuthTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aMultiAuthTest.java deleted file mode 100644 index 8aabc1dd39bf..000000000000 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/multiauth/Sigv4aMultiAuthTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -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.Arrays; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import software.amazon.awssdk.core.SdkSystemSetting; -import software.amazon.awssdk.http.HttpExecuteRequest; -import software.amazon.awssdk.http.SdkHttpClient; -import software.amazon.awssdk.http.SdkHttpRequest; -import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; -import software.amazon.awssdk.http.auth.aws.signer.RegionSet; -import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.multiauth.auth.scheme.MultiauthAuthSchemeParams; -import software.amazon.awssdk.services.multiauth.auth.scheme.MultiauthAuthSchemeProvider; -import software.amazon.awssdk.testutils.EnvironmentVariableHelper; - -/** - * Unit tests for the Sigv4a multi-auth functionality. - */ -class Sigv4aMultiAuthTest { - - private EnvironmentVariableHelper environmentVariableHelper; - private SdkHttpClient mockHttpClient; - private MultiauthAuthSchemeProvider multiauthAuthSchemeProvider; - - @BeforeEach - void setUp() { - environmentVariableHelper = new EnvironmentVariableHelper(); - multiauthAuthSchemeProvider = mock(MultiauthAuthSchemeProvider.class); - - mockHttpClient = mock(SdkHttpClient.class); - when(mockHttpClient.clientName()).thenReturn("MockHttpClient"); - when(mockHttpClient.prepareRequest(any())).thenThrow(new RuntimeException("expected exception")); - - List authSchemeOptions = Collections.singletonList( - AuthSchemeOption.builder().schemeId(AwsV4AuthScheme.SCHEME_ID).build() - ); - when(multiauthAuthSchemeProvider.resolveAuthScheme(any(MultiauthAuthSchemeParams.class))) - .thenReturn(authSchemeOptions); - } - - @AfterEach - void tearDown() { - environmentVariableHelper.reset(); - } - - @Test - void requestHasRegionSetParamsUpdatedToRegion() { - environmentVariableHelper.set(SdkSystemSetting.AWS_SIGV4A_SIGNING_REGION_SET, "us-west-2,us-west-1"); - - MultiauthClient multiauthClient = MultiauthClient.builder() - .httpClient(mockHttpClient) - .authSchemeProvider(multiauthAuthSchemeProvider) - .region(Region.US_WEST_2) - .build(); - - assertThatThrownBy(() -> multiauthClient.sigv4aOperation(r -> r.stringMember(""))) - .hasMessageContaining("expected exception"); - - ArgumentCaptor paramsCaptor = - ArgumentCaptor.forClass(MultiauthAuthSchemeParams.class); - verify(multiauthAuthSchemeProvider).resolveAuthScheme(paramsCaptor.capture()); - - MultiauthAuthSchemeParams resolvedAuthSchemeParams = paramsCaptor.getValue(); - assertThat(resolvedAuthSchemeParams.regionSet()) - .isEqualTo(RegionSet.create(Arrays.asList("us-west-2", "us-west-1"))); - } - - @Test - void requestHasRegionSetSdkSystemSettings() { - MultiauthClient multiauthClient = MultiauthClient.builder() - .httpClient(mockHttpClient) - .authSchemeProvider(multiauthAuthSchemeProvider) - .region(Region.US_WEST_2) - .build(); - - assertThatThrownBy(() -> multiauthClient.sigv4aOperation(r -> r.stringMember(""))) - .hasMessageContaining("expected exception"); - - ArgumentCaptor paramsCaptor = - ArgumentCaptor.forClass(MultiauthAuthSchemeParams.class); - verify(multiauthAuthSchemeProvider).resolveAuthScheme(paramsCaptor.capture()); - - MultiauthAuthSchemeParams resolvedAuthSchemeParams = paramsCaptor.getValue(); - assertThat(resolvedAuthSchemeParams.regionSet()) - .isEqualTo(RegionSet.create(Region.US_WEST_2.toString())); - } - - @Test - void errorWhenSigv4aDoesNotHasFallbackSigv4() { - MultiauthClient multiauthClient = MultiauthClient.builder() - .httpClient(mockHttpClient) - .region(Region.US_WEST_2) - .build(); - - assertThatThrownBy(() -> multiauthClient.sigv4aOperation(r -> r.stringMember(""))) - .hasMessageContaining("You must add a dependency on the 'software.amazon.awssdk:http-auth-aws-crt' " - + "module to enable the CRT-V4a signing feature"); - } - - @Test - void fallBackToSigv4WhenSigv4aIsNotAvailable() { - MultiauthClient multiauthClient = MultiauthClient.builder() - .httpClient(mockHttpClient) - .region(Region.US_WEST_2) - .build(); - - assertThatThrownBy(() -> multiauthClient.sigv4AndSigv4aOperation(r -> r.stringMember(""))) - .hasMessageContaining("expected exception"); - - ArgumentCaptor httpRequestCaptor = ArgumentCaptor.forClass(HttpExecuteRequest.class); - verify(mockHttpClient).prepareRequest(httpRequestCaptor.capture()); - SdkHttpRequest request = httpRequestCaptor.getAllValues().get(0).httpRequest(); - assertThat(request.firstMatchingHeader("Authorization")).isPresent(); - } -} From 81cca37f788d894cc792b23f5a4b61e4e27c33c4 Mon Sep 17 00:00:00 2001 From: John Viegas Date: Sat, 25 Jan 2025 21:57:17 -0800 Subject: [PATCH 2/6] Endpoint Resolver Spec test added --- .../EndpointResolverInterceptorSpecTest.java | 6 + ...olve-interceptor-with-multiauthsigv4a.java | 171 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpecTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpecTest.java index f3cf566962d1..cbc364afda36 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpecTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpecTest.java @@ -42,4 +42,10 @@ private static IntermediateModel getModel(boolean useSraAuth) { model.getCustomizationConfig().setUseSraAuth(useSraAuth); return model; } + + @Test + void endpointResolverInterceptorClassWithSigv4aMultiAuth() { + ClassSpec endpointProviderInterceptor = new EndpointResolverInterceptorSpec(ClientTestModels.opsWithSigv4a()); + assertThat(endpointProviderInterceptor, generatesTo("endpoint-resolve-interceptor-with-multiauthsigv4a.java")); + } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java new file mode 100644 index 000000000000..067fc5186653 --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java @@ -0,0 +1,171 @@ +package software.amazon.awssdk.services.database.endpoints.internal; + +import java.time.Duration; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletionException; +import software.amazon.awssdk.annotations.Generated; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; +import software.amazon.awssdk.awscore.endpoints.authscheme.EndpointAuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; +import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4aAuthScheme; +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.SelectedAuthScheme; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; +import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; +import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.endpoints.Endpoint; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; +import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; +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.AuthSchemeOption; +import software.amazon.awssdk.identity.spi.Identity; +import software.amazon.awssdk.metrics.MetricCollector; +import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointParams; +import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointProvider; + +@Generated("software.amazon.awssdk:codegen") +@SdkInternalApi +public final class DatabaseResolveEndpointInterceptor implements ExecutionInterceptor { + @Override + public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { + SdkRequest result = context.request(); + if (AwsEndpointProviderUtils.endpointIsDiscovered(executionAttributes)) { + return result; + } + DatabaseEndpointProvider provider = (DatabaseEndpointProvider) executionAttributes + .getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); + try { + long resolveEndpointStart = System.nanoTime(); + DatabaseEndpointParams endpointParams = ruleParams(result, executionAttributes); + Endpoint endpoint = provider.resolveEndpoint(endpointParams).join(); + Duration resolveEndpointDuration = Duration.ofNanos(System.nanoTime() - resolveEndpointStart); + Optional metricCollector = executionAttributes + .getOptionalAttribute(SdkExecutionAttribute.API_CALL_METRIC_COLLECTOR); + metricCollector.ifPresent(mc -> mc.reportMetric(CoreMetric.ENDPOINT_RESOLVE_DURATION, resolveEndpointDuration)); + if (!AwsEndpointProviderUtils.disableHostPrefixInjection(executionAttributes)) { + Optional hostPrefix = hostPrefix(executionAttributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME), + result); + if (hostPrefix.isPresent()) { + endpoint = AwsEndpointProviderUtils.addHostPrefix(endpoint, hostPrefix.get()); + } + } + List endpointAuthSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); + SelectedAuthScheme selectedAuthScheme = executionAttributes + .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); + if (endpointAuthSchemes != null && selectedAuthScheme != null) { + selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme); + if (!hasRegionSet(selectedAuthScheme)) { + selectedAuthScheme = updateAuthSchemeWithRegionSet(selectedAuthScheme, endpointParams); + } + executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme); + } + executionAttributes.putAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT, endpoint); + return result; + } catch (CompletionException e) { + Throwable cause = e.getCause(); + if (cause instanceof SdkClientException) { + throw (SdkClientException) cause; + } else { + throw SdkClientException.create("Endpoint resolution failed", cause); + } + } + } + + @Override + public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { + Endpoint resolvedEndpoint = executionAttributes.getAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT); + if (resolvedEndpoint.headers().isEmpty()) { + return context.httpRequest(); + } + SdkHttpRequest.Builder httpRequestBuilder = context.httpRequest().toBuilder(); + resolvedEndpoint.headers().forEach((name, values) -> { + values.forEach(v -> httpRequestBuilder.appendHeader(name, v)); + }); + return httpRequestBuilder.build(); + } + + public static DatabaseEndpointParams ruleParams(SdkRequest request, ExecutionAttributes executionAttributes) { + DatabaseEndpointParams.Builder builder = DatabaseEndpointParams.builder(); + builder.region(AwsEndpointProviderUtils.regionBuiltIn(executionAttributes)); + builder.endpoint(AwsEndpointProviderUtils.endpointBuiltIn(executionAttributes)); + setContextParams(builder, executionAttributes.getAttribute(AwsExecutionAttribute.OPERATION_NAME), request); + setStaticContextParams(builder, executionAttributes.getAttribute(AwsExecutionAttribute.OPERATION_NAME)); + setOperationContextParams(builder, executionAttributes.getAttribute(AwsExecutionAttribute.OPERATION_NAME), request); + return builder.build(); + } + + private static void setContextParams(DatabaseEndpointParams.Builder params, String operationName, SdkRequest request) { + } + + private static void setStaticContextParams(DatabaseEndpointParams.Builder params, String operationName) { + } + + private SelectedAuthScheme authSchemeWithEndpointSignerProperties( + List endpointAuthSchemes, SelectedAuthScheme selectedAuthScheme) { + for (EndpointAuthScheme endpointAuthScheme : endpointAuthSchemes) { + if (!endpointAuthScheme.schemeId().equals(selectedAuthScheme.authSchemeOption().schemeId())) { + continue; + } + AuthSchemeOption.Builder option = selectedAuthScheme.authSchemeOption().toBuilder(); + if (endpointAuthScheme instanceof SigV4AuthScheme) { + SigV4AuthScheme v4AuthScheme = (SigV4AuthScheme) endpointAuthScheme; + if (v4AuthScheme.isDisableDoubleEncodingSet()) { + option.putSignerProperty(AwsV4HttpSigner.DOUBLE_URL_ENCODE, !v4AuthScheme.disableDoubleEncoding()); + } + if (v4AuthScheme.signingRegion() != null) { + option.putSignerProperty(AwsV4HttpSigner.REGION_NAME, v4AuthScheme.signingRegion()); + } + if (v4AuthScheme.signingName() != null) { + option.putSignerProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, v4AuthScheme.signingName()); + } + return new SelectedAuthScheme<>(selectedAuthScheme.identity(), selectedAuthScheme.signer(), option.build()); + } + if (endpointAuthScheme instanceof SigV4aAuthScheme) { + SigV4aAuthScheme v4aAuthScheme = (SigV4aAuthScheme) endpointAuthScheme; + if (v4aAuthScheme.isDisableDoubleEncodingSet()) { + option.putSignerProperty(AwsV4aHttpSigner.DOUBLE_URL_ENCODE, !v4aAuthScheme.disableDoubleEncoding()); + } + if (!hasRegionSet(selectedAuthScheme) && v4aAuthScheme.signingRegionSet() != null) { + RegionSet regionSet = RegionSet.create(v4aAuthScheme.signingRegionSet()); + option.putSignerProperty(AwsV4aHttpSigner.REGION_SET, regionSet); + } + if (v4aAuthScheme.signingName() != null) { + option.putSignerProperty(AwsV4aHttpSigner.SERVICE_SIGNING_NAME, v4aAuthScheme.signingName()); + } + return new SelectedAuthScheme<>(selectedAuthScheme.identity(), selectedAuthScheme.signer(), option.build()); + } + throw new IllegalArgumentException("Endpoint auth scheme '" + endpointAuthScheme.name() + + "' cannot be mapped to the SDK auth scheme. Was it declared in the service's model?"); + } + return selectedAuthScheme; + } + + private static void setOperationContextParams(DatabaseEndpointParams.Builder params, String operationName, SdkRequest request) { + } + + private static Optional hostPrefix(String operationName, SdkRequest request) { + return Optional.empty(); + } + + private boolean hasRegionSet(SelectedAuthScheme selectedAuthScheme) { + return selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) + && selectedAuthScheme.authSchemeOption().signerProperty(AwsV4aHttpSigner.REGION_SET) != null; + } + + private SelectedAuthScheme updateAuthSchemeWithRegionSet(SelectedAuthScheme selectedAuthScheme, + DatabaseEndpointParams endpointParams) { + AuthSchemeOption.Builder optionBuilder = selectedAuthScheme.authSchemeOption().toBuilder(); + RegionSet regionSet = RegionSet.create(endpointParams.region().id()); + optionBuilder.putSignerProperty(AwsV4aHttpSigner.REGION_SET, regionSet); + return new SelectedAuthScheme<>(selectedAuthScheme.identity(), selectedAuthScheme.signer(), optionBuilder.build()); + } +} From 5953fdf4824ea4a55ba2933a6977d5568902a290 Mon Sep 17 00:00:00 2001 From: John Viegas Date: Sun, 26 Jan 2025 12:19:40 -0800 Subject: [PATCH 3/6] Add test case for service with MultiAuth only and not using Sigv4 --- .../amazon/awssdk/codegen/AddMetadata.java | 3 +- .../scheme/AuthSchemeInterceptorSpec.java | 1 - .../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 +++++ .../auth/Sigv4aSigningRegionSetTest.java | 13 +- .../multiauth/Sigv4aOnlyMultiAuthTest.java | 193 ++++++++++++++++++ 9 files changed, 337 insertions(+), 9 deletions(-) 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/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index f87fafff4092..3d3cf2c03674 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -197,7 +197,6 @@ private MethodSpec generateAuthSchemeParams() { builder.addStatement("(($T)builder).endpointProvider(($T)endpointProvider)", paramsBuilderClass, endpointProviderClass); builder.endControlFlow(); builder.endControlFlow(); - // TODO: Implement addRegionSet() for legacy services that resolve authentication from endpoints in one of next PRs. builder.addStatement("return builder.build()"); return builder.build(); } 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/auth/Sigv4aSigningRegionSetTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/auth/Sigv4aSigningRegionSetTest.java index ba0c9144f9c2..c689cc19c7ab 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/auth/Sigv4aSigningRegionSetTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/auth/Sigv4aSigningRegionSetTest.java @@ -16,6 +16,7 @@ package software.amazon.awssdk.services.auth; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.util.Arrays; import java.util.Collections; @@ -206,10 +207,9 @@ void resolvesSigv4aSigningRegionSet(TestCase testCase) { MultiauthClient client = builder.build(); - try { - client.multiAuthWithOnlySigv4aAndSigv4(b -> b.stringMember("test").build()); - } catch (EndpointCapturingInterceptor.CaptureCompletedException e) { - } + assertThatExceptionOfType(CaptureCompletedException.class) + .isThrownBy(() -> client.multiAuthWithOnlySigv4aAndSigv4(b -> b.stringMember("test").build())); + assertThat(interceptor.sigv4aSigningRegionSet()) .containsExactlyInAnyOrderElementsOf(testCase.expectedValues); @@ -267,8 +267,7 @@ public Set sigv4aSigningRegionSet() { public void reset() { sigv4aSigningRegionSet = Collections.emptySet(); } - - public static class CaptureCompletedException extends RuntimeException { - } + } + public static class CaptureCompletedException extends RuntimeException { } } 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())); + } + } + + +} From 94f395190a4bb0a6e6f7dd316d845bc44fedce11 Mon Sep 17 00:00:00 2001 From: John Viegas Date: Sun, 26 Jan 2025 12:31:21 -0800 Subject: [PATCH 4/6] checkstyle fixed --- .../main/java/software/amazon/awssdk/codegen/AddMetadata.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8e921fac0a2f..56ed58996ac8 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,7 @@ public static Metadata constructMetadata(ServiceModel serviceModel, .withJsonVersion(serviceMetadata.getJsonVersion()) .withEndpointPrefix(serviceMetadata.getEndpointPrefix()) .withSigningName(serviceMetadata.getSigningName()) - .withAuthType(serviceMetadata.getSignatureVersion() !=null ? + .withAuthType(serviceMetadata.getSignatureVersion() != null ? AuthType.fromValue(serviceMetadata.getSignatureVersion()) : null) .withUid(serviceMetadata.getUid()) .withServiceId(serviceMetadata.getServiceId()) From 9bd5a753a63229d1585f247181cf4d5c44864cea Mon Sep 17 00:00:00 2001 From: John Viegas Date: Thu, 30 Jan 2025 21:22:50 -0800 Subject: [PATCH 5/6] Handled comments --- .../EndpointResolverInterceptorSpec.java | 71 ++++--------------- .../endpoint-resolve-interceptor-preSra.java | 2 +- ...olve-interceptor-with-multiauthsigv4a.java | 27 +++---- .../rules/endpoint-resolve-interceptor.java | 2 +- 4 files changed, 28 insertions(+), 74 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java index a0fe830293a9..850854b02b37 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java @@ -157,10 +157,6 @@ public TypeSpec poetSpec() { b.addMethod(signerProviderMethod()); } - if (multiAuthSigv4a) { - b.addMethod(createHasRegionSetMethod()); - b.addMethod(createUpdateAuthSchemeWithRegionSetMethod()); - } endpointParamsKnowledgeIndex.addAccountIdMethodsIfPresent(b); return b.build(); } @@ -225,11 +221,19 @@ private MethodSpec modifyRequestMethod(String endpointAuthSchemeStrategyFieldNam List.class, EndpointAuthScheme.class, AwsEndpointAttribute.class); b.addStatement("$T selectedAuthScheme = executionAttributes.getAttribute($T.SELECTED_AUTH_SCHEME)", SelectedAuthScheme.class, SdkInternalExecutionAttribute.class); - b.beginControlFlow("if (endpointAuthSchemes != null && selectedAuthScheme != null)"); + b.beginControlFlow("if (endpointAuthSchemes != null)"); b.addStatement("selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme)"); if (multiAuthSigv4a) { - b.beginControlFlow("if(!hasRegionSet(selectedAuthScheme))"); - b.addStatement("selectedAuthScheme = updateAuthSchemeWithRegionSet(selectedAuthScheme, endpointParams)"); + b.beginControlFlow("if(selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID) " + + "&& selectedAuthScheme.authSchemeOption().signerProperty($T.REGION_SET) == null)", + AwsV4aAuthScheme.class, AwsV4aHttpSigner.class); + b.addStatement("$T optionBuilder = selectedAuthScheme.authSchemeOption().toBuilder()", + AuthSchemeOption.Builder.class); + b.addStatement("$T regionSet = $T.create(endpointParams.region().id())", + RegionSet.class, RegionSet.class); + b.addStatement("optionBuilder.putSignerProperty($T.REGION_SET, regionSet)", AwsV4aHttpSigner.class); + b.addStatement("selectedAuthScheme = new $T(selectedAuthScheme.identity(), selectedAuthScheme.signer(), " + + "optionBuilder.build())", SelectedAuthScheme.class); b.endControlFlow(); } b.addStatement("executionAttributes.putAttribute($T.SELECTED_AUTH_SCHEME, selectedAuthScheme)", @@ -797,7 +801,10 @@ private CodeBlock copyV4aEndpointSignerPropertiesToAuth() { AwsV4aHttpSigner.class); code.endControlFlow(); if (multiAuthSigv4a) { - code.beginControlFlow("if (!hasRegionSet(selectedAuthScheme) && v4aAuthScheme.signingRegionSet() != null)"); + code.beginControlFlow("if (!(selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID) " + + "&& selectedAuthScheme.authSchemeOption().signerProperty($T.REGION_SET) != null) " + + "&& v4aAuthScheme.signingRegionSet() != null)", + AwsV4aAuthScheme.class, AwsV4aHttpSigner.class); } else { code.beginControlFlow("if (v4aAuthScheme.signingRegionSet() != null)"); } @@ -895,52 +902,4 @@ private MethodSpec constructorMethodSpec(String endpointAuthSchemeFieldName) { b.addStatement("this.$N = $N.endpointAuthSchemeStrategy()", endpointAuthSchemeFieldName, factoryLocalVarName); return b.build(); } - - private MethodSpec createHasRegionSetMethod() { - TypeVariableName tExtendsIdentity = TypeVariableName.get("T", Identity.class); - TypeName selectedAuthSchemeOfT = ParameterizedTypeName.get(ClassName.get(SelectedAuthScheme.class), - TypeVariableName.get("T")); - - return - MethodSpec.methodBuilder("hasRegionSet") - .addModifiers(Modifier.PRIVATE) - .addTypeVariable(tExtendsIdentity) - .returns(boolean.class) - .addParameter(selectedAuthSchemeOfT, "selectedAuthScheme") - .addCode( - CodeBlock.builder() - .addStatement("return selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID)" - + " && selectedAuthScheme.authSchemeOption().signerProperty($T.REGION_SET) != " - + "null", AwsV4aAuthScheme.class, AwsV4aHttpSigner.class) - .build()) - .build(); - } - - private MethodSpec createUpdateAuthSchemeWithRegionSetMethod() { - TypeVariableName tExtendsIdentity = TypeVariableName.get("T", Identity.class); - TypeName selectedAuthSchemeOfT = ParameterizedTypeName.get( - ClassName.get(SelectedAuthScheme.class), - TypeVariableName.get("T") - ); - - return MethodSpec.methodBuilder("updateAuthSchemeWithRegionSet") - .addModifiers(Modifier.PRIVATE) - .addTypeVariable(tExtendsIdentity) - .returns(selectedAuthSchemeOfT) - .addParameter(selectedAuthSchemeOfT, "selectedAuthScheme") - .addParameter(endpointRulesSpecUtils.parametersClassName(), "endpointParams") - .addCode(CodeBlock.builder() - .addStatement("$T optionBuilder = selectedAuthScheme.authSchemeOption().toBuilder()", - ClassName.get(AuthSchemeOption.Builder.class)) - .addStatement("$T regionSet = $T.create(endpointParams.region().id())", - RegionSet.class, RegionSet.class) - .addStatement("optionBuilder.putSignerProperty($T.REGION_SET, regionSet)", - AwsV4aHttpSigner.class) - .addStatement("return new $T<>(selectedAuthScheme.identity(), " + - "selectedAuthScheme.signer(), optionBuilder.build())", - SelectedAuthScheme.class) - .build()) - .build(); - } - } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java index f9a6c52d8512..4611aaa4ad94 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java @@ -84,7 +84,7 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut List endpointAuthSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); SelectedAuthScheme selectedAuthScheme = executionAttributes .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (endpointAuthSchemes != null && selectedAuthScheme != null) { + if (endpointAuthSchemes != null) { selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme); executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java index 067fc5186653..1c7cecdb11dc 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java @@ -61,10 +61,15 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut List endpointAuthSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); SelectedAuthScheme selectedAuthScheme = executionAttributes .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (endpointAuthSchemes != null && selectedAuthScheme != null) { + if (endpointAuthSchemes != null) { selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme); - if (!hasRegionSet(selectedAuthScheme)) { - selectedAuthScheme = updateAuthSchemeWithRegionSet(selectedAuthScheme, endpointParams); + if (selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) + && selectedAuthScheme.authSchemeOption().signerProperty(AwsV4aHttpSigner.REGION_SET) == null) { + AuthSchemeOption.Builder optionBuilder = selectedAuthScheme.authSchemeOption().toBuilder(); + RegionSet regionSet = RegionSet.create(endpointParams.region().id()); + optionBuilder.putSignerProperty(AwsV4aHttpSigner.REGION_SET, regionSet); + selectedAuthScheme = new SelectedAuthScheme(selectedAuthScheme.identity(), selectedAuthScheme.signer(), + optionBuilder.build()); } executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme); } @@ -134,7 +139,9 @@ private SelectedAuthScheme authSchemeWithEndpointSignerP if (v4aAuthScheme.isDisableDoubleEncodingSet()) { option.putSignerProperty(AwsV4aHttpSigner.DOUBLE_URL_ENCODE, !v4aAuthScheme.disableDoubleEncoding()); } - if (!hasRegionSet(selectedAuthScheme) && v4aAuthScheme.signingRegionSet() != null) { + if (!(selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) + && selectedAuthScheme.authSchemeOption().signerProperty(AwsV4aHttpSigner.REGION_SET) != null) + && v4aAuthScheme.signingRegionSet() != null) { RegionSet regionSet = RegionSet.create(v4aAuthScheme.signingRegionSet()); option.putSignerProperty(AwsV4aHttpSigner.REGION_SET, regionSet); } @@ -156,16 +163,4 @@ private static Optional hostPrefix(String operationName, SdkRequest requ return Optional.empty(); } - private boolean hasRegionSet(SelectedAuthScheme selectedAuthScheme) { - return selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) - && selectedAuthScheme.authSchemeOption().signerProperty(AwsV4aHttpSigner.REGION_SET) != null; - } - - private SelectedAuthScheme updateAuthSchemeWithRegionSet(SelectedAuthScheme selectedAuthScheme, - DatabaseEndpointParams endpointParams) { - AuthSchemeOption.Builder optionBuilder = selectedAuthScheme.authSchemeOption().toBuilder(); - RegionSet regionSet = RegionSet.create(endpointParams.region().id()); - optionBuilder.putSignerProperty(AwsV4aHttpSigner.REGION_SET, regionSet); - return new SelectedAuthScheme<>(selectedAuthScheme.identity(), selectedAuthScheme.signer(), optionBuilder.build()); - } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java index f8a4a1ebd78f..1bc582b844ba 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java @@ -72,7 +72,7 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut List endpointAuthSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); SelectedAuthScheme selectedAuthScheme = executionAttributes .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (endpointAuthSchemes != null && selectedAuthScheme != null) { + if (endpointAuthSchemes != null) { selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme); executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme); } From e517fc441436ad2fa2a1afa0598418d709c49891 Mon Sep 17 00:00:00 2001 From: John Viegas Date: Fri, 31 Jan 2025 11:53:27 -0800 Subject: [PATCH 6/6] Updated after Integ test failure and some comments --- .../codegen/poet/rules/EndpointResolverInterceptorSpec.java | 3 ++- .../poet/rules/endpoint-resolve-interceptor-preSra.java | 2 +- .../endpoint-resolve-interceptor-with-multiauthsigv4a.java | 3 ++- .../codegen/poet/rules/endpoint-resolve-interceptor.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java index 850854b02b37..6013ec6b7d36 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointResolverInterceptorSpec.java @@ -221,9 +221,10 @@ private MethodSpec modifyRequestMethod(String endpointAuthSchemeStrategyFieldNam List.class, EndpointAuthScheme.class, AwsEndpointAttribute.class); b.addStatement("$T selectedAuthScheme = executionAttributes.getAttribute($T.SELECTED_AUTH_SCHEME)", SelectedAuthScheme.class, SdkInternalExecutionAttribute.class); - b.beginControlFlow("if (endpointAuthSchemes != null)"); + b.beginControlFlow("if (endpointAuthSchemes != null && selectedAuthScheme != null)"); b.addStatement("selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme)"); if (multiAuthSigv4a) { + b.addComment("Precedence of SigV4a RegionSet is set according to multi-auth SigV4a specifications"); b.beginControlFlow("if(selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID) " + "&& selectedAuthScheme.authSchemeOption().signerProperty($T.REGION_SET) == null)", AwsV4aAuthScheme.class, AwsV4aHttpSigner.class); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java index 4611aaa4ad94..f9a6c52d8512 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-preSra.java @@ -84,7 +84,7 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut List endpointAuthSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); SelectedAuthScheme selectedAuthScheme = executionAttributes .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (endpointAuthSchemes != null) { + if (endpointAuthSchemes != null && selectedAuthScheme != null) { selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme); executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java index 1c7cecdb11dc..e7c5c0cb3958 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor-with-multiauthsigv4a.java @@ -61,8 +61,9 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut List endpointAuthSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); SelectedAuthScheme selectedAuthScheme = executionAttributes .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (endpointAuthSchemes != null) { + if (endpointAuthSchemes != null && selectedAuthScheme != null) { selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme); + // Precedence of SigV4a RegionSet is set according to multi-auth SigV4a specifications if (selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) && selectedAuthScheme.authSchemeOption().signerProperty(AwsV4aHttpSigner.REGION_SET) == null) { AuthSchemeOption.Builder optionBuilder = selectedAuthScheme.authSchemeOption().toBuilder(); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java index 1bc582b844ba..f8a4a1ebd78f 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-resolve-interceptor.java @@ -72,7 +72,7 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut List endpointAuthSchemes = endpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES); SelectedAuthScheme selectedAuthScheme = executionAttributes .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (endpointAuthSchemes != null) { + if (endpointAuthSchemes != null && selectedAuthScheme != null) { selectedAuthScheme = authSchemeWithEndpointSignerProperties(endpointAuthSchemes, selectedAuthScheme); executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme); }