diff --git a/.changes/next-release/feature-AWSSDKforJavav2-1932d6a.json b/.changes/next-release/feature-AWSSDKforJavav2-1932d6a.json new file mode 100644 index 000000000000..4500cbe3abac --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-1932d6a.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Adds support for configuring bearer auth using a token sourced from the environment for services with the `enableEnvironmentBearerToken` customization flag." +} diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/CommonInternalGeneratorTasks.java b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/CommonInternalGeneratorTasks.java index 7d407f582f7d..1bafba5eed5d 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/CommonInternalGeneratorTasks.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/CommonInternalGeneratorTasks.java @@ -15,11 +15,12 @@ package software.amazon.awssdk.codegen.emitters.tasks; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import software.amazon.awssdk.codegen.emitters.GeneratorTask; import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams; import software.amazon.awssdk.codegen.emitters.PoetGeneratorTask; +import software.amazon.awssdk.codegen.poet.client.EnvironmentTokenSystemSettingsClass; import software.amazon.awssdk.codegen.poet.client.SdkClientOptions; import software.amazon.awssdk.codegen.poet.common.UserAgentUtilsSpec; @@ -33,7 +34,13 @@ public CommonInternalGeneratorTasks(GeneratorTaskParams params) { @Override protected List createTasks() throws Exception { - return Arrays.asList(createClientOptionTask(), createUserAgentTask()); + List tasks = new ArrayList<>(); + tasks.add(createClientOptionTask()); + tasks.add(createUserAgentTask()); + if (params.getModel().getCustomizationConfig().isEnableEnvironmentBearerToken()) { + tasks.add(createEnvironmentTokenSystemSettingTask()); + } + return tasks; } private PoetGeneratorTask createClientOptionTask() { @@ -46,6 +53,11 @@ private PoetGeneratorTask createUserAgentTask() { new UserAgentUtilsSpec(params.getModel())); } + private GeneratorTask createEnvironmentTokenSystemSettingTask() { + return new PoetGeneratorTask(clientOptionsDir(), params.getModel().getFileHeader(), + new EnvironmentTokenSystemSettingsClass(params.getModel())); + } + private String clientOptionsDir() { return params.getPathProvider().getClientInternalDirectory(); } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java index 32cecd79feb5..069c6ca6d5a2 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java @@ -350,6 +350,13 @@ public class CustomizationConfig { */ private boolean enableFastUnmarshaller; + /** + * A boolean flag to indicate if support for configuring a bearer token sourced from the environment should be added to the + * generated service. When enabled, the generated client will use bearer auth with the token sourced from the + * `AWS_BEARER_TOKEN_[SigningName]` environment variable. + */ + private boolean enableEnvironmentBearerToken = false; + private CustomizationConfig() { } @@ -924,4 +931,12 @@ public boolean getEnableFastUnmarshaller() { public void setEnableFastUnmarshaller(boolean enableFastUnmarshaller) { this.enableFastUnmarshaller = enableFastUnmarshaller; } + + public boolean isEnableEnvironmentBearerToken() { + return enableEnvironmentBearerToken; + } + + public void setEnableEnvironmentBearerToken(boolean enableEnvironmentBearerToken) { + this.enableEnvironmentBearerToken = enableEnvironmentBearerToken; + } } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java index e5a4904f295f..efb230313efb 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java @@ -432,6 +432,22 @@ private boolean isDisallowedNameForShape(String name, Shape parentShape) { } } + @Override + public String getSigningName() { + return Optional.ofNullable(serviceModel.getMetadata().getSigningName()) + .orElseGet(() -> serviceModel.getMetadata().getEndpointPrefix()); + } + + @Override + public String getSigningNameForEnvironmentVariables() { + return screamCase(getSigningName()); + } + + @Override + public String getSigningNameForSystemProperties() { + return pascalCase(getSigningName()); + } + @Override public void validateCustomerVisibleNaming(IntermediateModel trimmedModel) { Metadata metadata = trimmedModel.getMetadata(); diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java index 1fe32773d71f..637920be14de 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java @@ -200,6 +200,21 @@ public interface NamingStrategy { */ String getExistenceCheckMethodName(String memberName, Shape parentShape); + /** + * Retrieve the service's signing name that should be used based on the model. + */ + String getSigningName(); + + /** + * Retrieve the service's signing name that should be used for environment variables. + */ + String getSigningNameForEnvironmentVariables(); + + /** + * Retrieve the service's signing name that should be used for system properties. + */ + String getSigningNameForSystemProperties(); + /** * Verify the customer-visible naming in the provided intermediate model will compile and is idiomatic to Java. */ diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtension.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtension.java index b126fd2f201e..4112188c6451 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtension.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtension.java @@ -79,6 +79,10 @@ public ClassName getUserAgentClass() { return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "UserAgentUtils"); } + public ClassName getEnvironmentTokenSystemSettingsClass() { + return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "EnvironmentTokenSystemSettings"); + } + /** * @param operationName Name of the operation * @return A Poet {@link ClassName} for the response type of a paginated operation in the base service package. 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 7686ef132271..043bf74ba9d9 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 @@ -49,8 +49,10 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; +import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; 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.HttpSigner; @@ -70,8 +72,10 @@ public final class AuthSchemeInterceptorSpec implements ClassSpec { private final AuthSchemeSpecUtils authSchemeSpecUtils; private final EndpointRulesSpecUtils endpointRulesSpecUtils; + private final IntermediateModel intermediateModel; public AuthSchemeInterceptorSpec(IntermediateModel intermediateModel) { + this.intermediateModel = intermediateModel; this.authSchemeSpecUtils = new AuthSchemeSpecUtils(intermediateModel); this.endpointRulesSpecUtils = new EndpointRulesSpecUtils(intermediateModel); } @@ -99,9 +103,42 @@ public TypeSpec poetSpec() { .addMethod(generateTrySelectAuthScheme()) .addMethod(generateGetIdentityMetric()) .addMethod(putSelectedAuthSchemeMethodSpec()); + if (intermediateModel.getCustomizationConfig().isEnableEnvironmentBearerToken()) { + builder.addMethod(generateEnvironmentTokenMetric()); + } return builder.build(); } + private MethodSpec generateEnvironmentTokenMetric() { + return MethodSpec + .methodBuilder("recordEnvironmentTokenBusinessMetric") + .addModifiers(Modifier.PRIVATE) + .addTypeVariable(TypeVariableName.get("T", Identity.class)) + .addParameter(ParameterSpec.builder( + ParameterizedTypeName.get(ClassName.get(SelectedAuthScheme.class), + TypeVariableName.get("T")), + "selectedAuthScheme").build()) + .addParameter(ExecutionAttributes.class, "executionAttributes") + .addStatement("$T tokenFromEnv = executionAttributes.getAttribute($T.TOKEN_CONFIGURED_FROM_ENV)", + String.class, SdkInternalExecutionAttribute.class) + .beginControlFlow("if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals($T" + + ".SCHEME_ID) && selectedAuthScheme.identity().isDone())", BearerAuthScheme.class) + .beginControlFlow("if (selectedAuthScheme.identity().getNow(null) instanceof $T)", TokenIdentity.class) + + .addStatement("$T configuredToken = ($T) selectedAuthScheme.identity().getNow(null)", + TokenIdentity.class, TokenIdentity.class) + .beginControlFlow("if (configuredToken.token().equals(tokenFromEnv))") + .addStatement("executionAttributes.getAttribute($T.BUSINESS_METRICS)" + + ".addMetric($T.BEARER_SERVICE_ENV_VARS.value())", + SdkInternalExecutionAttribute.class, BusinessMetricFeatureId.class) + .endControlFlow() + .endControlFlow() + .endControlFlow() + .build(); + + + } + private MethodSpec generateBeforeExecution() { MethodSpec.Builder builder = MethodSpec.methodBuilder("beforeExecution") .addAnnotation(Override.class) @@ -116,6 +153,11 @@ private MethodSpec generateBeforeExecution() { .addStatement("$T selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes)", wildcardSelectedAuthScheme()) .addStatement("putSelectedAuthScheme(executionAttributes, selectedAuthScheme)"); + + if (intermediateModel.getCustomizationConfig().isEnableEnvironmentBearerToken()) { + builder.addStatement("recordEnvironmentTokenBusinessMetric(selectedAuthScheme, " + + "executionAttributes)"); + } return builder.build(); } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java index b7d20cf5603e..96d95f3650f8 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java @@ -41,6 +41,7 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.auth.credentials.TokenUtils; import software.amazon.awssdk.auth.signer.Aws4Signer; +import software.amazon.awssdk.auth.token.credentials.StaticTokenProvider; import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider; import software.amazon.awssdk.auth.token.signer.aws.BearerTokenSigner; import software.amazon.awssdk.awscore.auth.AuthSchemePreferenceResolver; @@ -53,6 +54,7 @@ import software.amazon.awssdk.codegen.model.service.AuthType; import software.amazon.awssdk.codegen.model.service.ClientContextParam; import software.amazon.awssdk.codegen.poet.ClassSpec; +import software.amazon.awssdk.codegen.poet.PoetExtension; import software.amazon.awssdk.codegen.poet.PoetUtils; import software.amazon.awssdk.codegen.poet.auth.scheme.AuthSchemeSpecUtils; import software.amazon.awssdk.codegen.poet.auth.scheme.ModelAuthSchemeClassesKnowledgeIndex; @@ -71,7 +73,9 @@ import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.endpointdiscovery.providers.DefaultEndpointDiscoveryProviderChain; import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory; +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.core.retry.RetryMode; import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.http.Protocol; @@ -102,6 +106,8 @@ public class BaseClientBuilderClass implements ClassSpec { private final AuthSchemeSpecUtils authSchemeSpecUtils; private final ServiceClientConfigurationUtils configurationUtils; private final EndpointParamsKnowledgeIndex endpointParamsKnowledgeIndex; + private final PoetExtension poetExtensions; + public BaseClientBuilderClass(IntermediateModel model) { this.model = model; @@ -112,6 +118,7 @@ public BaseClientBuilderClass(IntermediateModel model) { this.authSchemeSpecUtils = new AuthSchemeSpecUtils(model); this.configurationUtils = new ServiceClientConfigurationUtils(model); this.endpointParamsKnowledgeIndex = EndpointParamsKnowledgeIndex.of(model); + this.poetExtensions = new PoetExtension(model); } @Override @@ -266,24 +273,24 @@ private MethodSpec serviceNameMethod() { } private MethodSpec mergeServiceDefaultsMethod() { - boolean crc32FromCompressedDataEnabled = model.getCustomizationConfig().isCalculateCrc32FromCompressedData(); - MethodSpec.Builder builder = MethodSpec.methodBuilder("mergeServiceDefaults") .addAnnotation(Override.class) .addModifiers(PROTECTED, FINAL) .returns(SdkClientConfiguration.class) - .addParameter(SdkClientConfiguration.class, "config") - .addCode("return config.merge(c -> c"); + .addParameter(SdkClientConfiguration.class, "config"); - builder.addCode(".option($T.ENDPOINT_PROVIDER, defaultEndpointProvider())", SdkClientOption.class); + boolean crc32FromCompressedDataEnabled = model.getCustomizationConfig().isCalculateCrc32FromCompressedData(); + + builder.beginControlFlow("return config.merge(c -> "); + builder.addCode("c.option($T.ENDPOINT_PROVIDER, defaultEndpointProvider())", SdkClientOption.class); if (authSchemeSpecUtils.useSraAuth()) { - builder.addCode(".option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config))", SdkClientOption.class); - builder.addCode(".option($T.AUTH_SCHEMES, authSchemes())", SdkClientOption.class); - } else { - if (defaultAwsAuthSignerMethod().isPresent()) { - builder.addCode(".option($T.SIGNER, defaultSigner())\n", SdkAdvancedClientOption.class); + if (!model.getCustomizationConfig().isEnableEnvironmentBearerToken()) { + builder.addCode(".option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config))", SdkClientOption.class); } + builder.addCode(".option($T.AUTH_SCHEMES, authSchemes())", SdkClientOption.class); + } else if (defaultAwsAuthSignerMethod().isPresent()) { + builder.addCode(".option($T.SIGNER, defaultSigner())\n", SdkAdvancedClientOption.class); } builder.addCode(".option($T.CRC32_FROM_COMPRESSED_DATA_ENABLED, $L)\n", SdkClientOption.class, crc32FromCompressedDataEnabled); @@ -302,11 +309,47 @@ private MethodSpec mergeServiceDefaultsMethod() { builder.addCode(".option($T.TOKEN_SIGNER, defaultTokenSigner())", SdkAdvancedClientOption.class); } } + builder.addStatement(""); - builder.addCode(");"); + if (model.getCustomizationConfig().isEnableEnvironmentBearerToken()) { + configureEnvironmentBearerToken(builder); + } + builder.endControlFlow(")"); return builder.build(); } + private void configureEnvironmentBearerToken(MethodSpec.Builder builder) { + if (!authSchemeSpecUtils.useSraAuth()) { + throw new IllegalStateException("The enableEnvironmentBearerToken customization requires SRA Auth."); + } + if (!AuthUtils.usesBearerAuth(model)) { + throw new IllegalStateException("The enableEnvironmentBearerToken customization requires the service to model and " + + "support smithy.api#httpBearerAuth."); + } + + builder.addStatement("$T tokenFromEnv = new $T().getStringValue()", + ParameterizedTypeName.get(Optional.class, String.class), + poetExtensions.getEnvironmentTokenSystemSettingsClass()); + + builder + .beginControlFlow("if (tokenFromEnv.isPresent() && config.option($T.AUTH_SCHEME_PROVIDER) == null && config.option($T" + + ".TOKEN_IDENTITY_PROVIDER) == null)", + SdkClientOption.class, AwsClientOption.class) + .addStatement("c.option($T.AUTH_SCHEME_PROVIDER, $T.defaultProvider($T.singletonList($S)))", + SdkClientOption.class, authSchemeSpecUtils.providerInterfaceName(), Collections.class, + "httpBearerAuth") + .addStatement("c.option($T.TOKEN_IDENTITY_PROVIDER, $T.create(tokenFromEnv::get))", + AwsClientOption.class, StaticTokenProvider.class) + .addStatement("c.option($T.EXECUTION_ATTRIBUTES, " + + "$T.builder().put($T.TOKEN_CONFIGURED_FROM_ENV, tokenFromEnv.get()).build())", + SdkClientOption.class, ExecutionAttributes.class, SdkInternalExecutionAttribute.class) + .endControlFlow() + .beginControlFlow("else") + .addStatement("c.option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config))", SdkClientOption.class) + .endControlFlow(); + + } + private Optional mergeInternalDefaultsMethod() { String userAgent = model.getCustomizationConfig().getUserAgent(); RetryMode defaultRetryMode = model.getCustomizationConfig().getDefaultRetryMode(); diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/EnvironmentTokenSystemSettingsClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/EnvironmentTokenSystemSettingsClass.java new file mode 100644 index 000000000000..3ca3fb56ab41 --- /dev/null +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/EnvironmentTokenSystemSettingsClass.java @@ -0,0 +1,76 @@ +/* + * 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.codegen.poet.client; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; +import javax.lang.model.element.Modifier; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; +import software.amazon.awssdk.codegen.naming.NamingStrategy; +import software.amazon.awssdk.codegen.poet.ClassSpec; +import software.amazon.awssdk.codegen.poet.PoetExtension; +import software.amazon.awssdk.codegen.poet.PoetUtils; +import software.amazon.awssdk.utils.SystemSetting; + +public class EnvironmentTokenSystemSettingsClass implements ClassSpec { + protected final IntermediateModel model; + protected final PoetExtension poetExtensions; + + public EnvironmentTokenSystemSettingsClass(IntermediateModel model) { + this.model = model; + this.poetExtensions = new PoetExtension(model); + } + + @Override + public TypeSpec poetSpec() { + NamingStrategy namingStrategy = model.getNamingStrategy(); + + String systemPropertyName = "aws.bearerToken" + namingStrategy.getSigningNameForSystemProperties(); + String envName = "AWS_BEARER_TOKEN_" + namingStrategy.getSigningNameForEnvironmentVariables(); + + return TypeSpec.classBuilder(className()) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(PoetUtils.generatedAnnotation()) + .addAnnotation(SdkInternalApi.class) + .addSuperinterface(SystemSetting.class) + .addMethod(MethodSpec.methodBuilder("property") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(String.class) + .addStatement("return $S", systemPropertyName) + .build()) + .addMethod(MethodSpec.methodBuilder("environmentVariable") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(String.class) + .addStatement("return $S", envName) + .build()) + .addMethod(MethodSpec.methodBuilder("defaultValue") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(String.class) + .addStatement("return null") + .build()) + .build(); + } + + @Override + public ClassName className() { + return poetExtensions.getEnvironmentTokenSystemSettingsClass(); + } +} diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationUtils.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationUtils.java index 22f06d253b56..320506822765 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationUtils.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationUtils.java @@ -36,6 +36,7 @@ import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; import software.amazon.awssdk.codegen.poet.auth.scheme.AuthSchemeSpecUtils; import software.amazon.awssdk.codegen.poet.rules.EndpointRulesSpecUtils; +import software.amazon.awssdk.codegen.utils.AuthUtils; import software.amazon.awssdk.core.ClientEndpointProvider; import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; import software.amazon.awssdk.core.checksums.ResponseChecksumValidation; @@ -48,6 +49,7 @@ import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeProvider; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.identity.spi.IdentityProvider; +import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.Validate; @@ -105,10 +107,34 @@ private List fields(IntermediateModel model) { authSchemeProviderField() )); fields.addAll(addCustomClientParams(model)); + fields.addAll(addModeledIdentityProviders(model)); fields.addAll(addCustomClientConfigParams(model)); return fields; } + private List addModeledIdentityProviders(IntermediateModel model) { + List identityProviderFields = new ArrayList<>(); + if (AuthUtils.usesBearerAuth(model)) { + identityProviderFields.add(tokenIdentityProviderField()); + } + return identityProviderFields; + } + + private Field tokenIdentityProviderField() { + TypeName tokenIdentityProviderType = + ParameterizedTypeName.get(ClassName.get(IdentityProvider.class), + WildcardTypeName.subtypeOf(TokenIdentity.class)); + + return fieldBuilder("tokenProvider", tokenIdentityProviderType) + .doc("token provider") + .isInherited(false) + .localSetter(basicLocalSetterCode("tokenProvider")) + .localGetter(basicLocalGetterCode("tokenProvider")) + .configSetter(basicConfigSetterCode(AwsClientOption.TOKEN_IDENTITY_PROVIDER, "tokenProvider")) + .configGetter(basicConfigGetterCode(AwsClientOption.TOKEN_IDENTITY_PROVIDER)) + .build(); + } + private List addCustomClientParams(IntermediateModel model) { List customClientParamFields = new ArrayList<>(); 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 f870ceea284d..004d64fac245 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 @@ -68,7 +68,8 @@ public static boolean isOpBearerAuth(IntermediateModel model, OperationModel opM } private static boolean isServiceBearerAuth(IntermediateModel model) { - return model.getMetadata().getAuthType() == AuthType.BEARER; + return model.getMetadata().getAuthType() == AuthType.BEARER || + (model.getMetadata().getAuth() != null && model.getMetadata().getAuth().contains(AuthType.BEARER)); } private static boolean isServiceSigv4a(IntermediateModel model) { diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java index 6b8756474405..cec5a7fd4bb2 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java @@ -337,6 +337,39 @@ public void validateAllowsUnderscoresWithCustomization() { strategy.validateCustomerVisibleNaming(model); } + @Test + public void getSigningNameForEnvironmentVariables_convertsDashAndUppercases() { + when(serviceModel.getMetadata()).thenReturn(serviceMetadata); + when(serviceMetadata.getSigningName()).thenReturn("signing-name"); + + assertThat(strat.getSigningNameForEnvironmentVariables()).isEqualTo("SIGNING_NAME"); + } + + @Test + public void getSigningNameForSystemProperties_convertsDashAndUppercasesWords() { + when(serviceModel.getMetadata()).thenReturn(serviceMetadata); + when(serviceMetadata.getSigningName()).thenReturn("signing-name"); + + assertThat(strat.getSigningNameForSystemProperties()).isEqualTo("SigningName"); + } + + @Test + public void getSigningName_Uses_EndpointPrefix_whenSigningNameUnset() { + when(serviceModel.getMetadata()).thenReturn(serviceMetadata); + when(serviceMetadata.getSigningName()).thenReturn(null); + when(serviceMetadata.getEndpointPrefix()).thenReturn("EndpointPrefixFoo"); + + assertThat(strat.getSigningName()).isEqualTo("EndpointPrefixFoo"); + } + + @Test + public void getSigningName_Uses_SigningName() { + when(serviceModel.getMetadata()).thenReturn(serviceMetadata); + when(serviceMetadata.getSigningName()).thenReturn("Foo"); + + assertThat(strat.getSigningName()).isEqualTo("Foo"); + } + @Test public void validateServiceIdentifiersForEnvVarsAndProfileProperty() { when(serviceModel.getMetadata()).thenReturn(serviceMetadata); diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java index 308aa69ea487..978e837fc87f 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java @@ -86,6 +86,22 @@ public static IntermediateModel bearerAuthServiceModels() { return new IntermediateModelBuilder(models).build(); } + public static IntermediateModel envBearerTokenServiceModels() { + File serviceModel = new File(ClientTestModels.class.getResource( + "client/c2j/json-bearer-auth/service-2.json").getFile()); + File customizationModel = new File(ClientTestModels.class.getResource( + "client/c2j/json-bearer-auth/customization-env-bearer-token.config").getFile()); + File paginatorsModel = new File(ClientTestModels.class.getResource( + "client/c2j/json-bearer-auth/paginators.json").getFile()); + C2jModels models = C2jModels.builder() + .serviceModel(getServiceModel(serviceModel)) + .customizationConfig(getCustomizationConfig(customizationModel)) + .paginatorsModel(getPaginatorsModel(paginatorsModel)) + .build(); + + return new IntermediateModelBuilder(models).build(); + } + public static IntermediateModel restJsonServiceModels() { File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/rest-json/service-2.json").getFile()); File customizationModel = new File(ClientTestModels.class.getResource("client/c2j/rest-json/customization.config").getFile()); diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeSpecTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeSpecTest.java index 533dbe8a46ad..3e2807600928 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeSpecTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeSpecTest.java @@ -220,6 +220,13 @@ static List parameters() { .classSpecProvider(AuthSchemeInterceptorSpec::new) .caseName("ops-auth-sigv4a-value") .outputFileSuffix("interceptor") + .build(), + // service with environment bearer token enabled + TestCase.builder() + .modelProvider(ClientTestModels::envBearerTokenServiceModels) + .classSpecProvider(AuthSchemeInterceptorSpec::new) + .caseName("env-bearer-token") + .outputFileSuffix("interceptor") .build() ); } diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java index a09271f4001a..423ae5aba59a 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClassTest.java @@ -17,6 +17,7 @@ import static software.amazon.awssdk.codegen.poet.ClientTestModels.bearerAuthServiceModels; import static software.amazon.awssdk.codegen.poet.ClientTestModels.composedClientJsonServiceModels; +import static software.amazon.awssdk.codegen.poet.ClientTestModels.envBearerTokenServiceModels; import static software.amazon.awssdk.codegen.poet.ClientTestModels.internalConfigModels; import static software.amazon.awssdk.codegen.poet.ClientTestModels.operationWithNoAuth; import static software.amazon.awssdk.codegen.poet.ClientTestModels.opsWithSigv4a; @@ -87,6 +88,13 @@ void baseClientBuilderClassWithBearerAuth_sra() { validateBaseClientBuilderClassGeneration(bearerAuthServiceModels(), "test-bearer-auth-client-builder-class.java", true); } + @Test + void baseClientBuilderClassWithEnvBearerToken_sra() { + validateBaseClientBuilderClassGeneration(envBearerTokenServiceModels(), + "test-env-bearer-token-client-builder-class.java", + true); + } + @Test void baseClientBuilderClassWithNoAuthOperation_sra() { validateBaseClientBuilderClassGeneration(operationWithNoAuth(), "test-no-auth-ops-client-builder-class.java", true); diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/client/EnvironmentTokenSystemSettingsClassTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/client/EnvironmentTokenSystemSettingsClassTest.java new file mode 100644 index 000000000000..3946483248a6 --- /dev/null +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/client/EnvironmentTokenSystemSettingsClassTest.java @@ -0,0 +1,31 @@ +/* + * 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.codegen.poet.client; + +import static org.hamcrest.MatcherAssert.assertThat; +import static software.amazon.awssdk.codegen.poet.PoetMatchers.generatesTo; + +import org.junit.Test; +import software.amazon.awssdk.codegen.poet.ClassSpec; +import software.amazon.awssdk.codegen.poet.ClientTestModels; + +public class EnvironmentTokenSystemSettingsClassTest { + @Test + public void testEnvironmentTokenSystemSettingsClass() { + ClassSpec classSpec = new EnvironmentTokenSystemSettingsClass(ClientTestModels.restJsonServiceModels()); + assertThat(classSpec, generatesTo("test-environment-token-system-settings-class.java")); + } +} diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/utils/AuthUtilsTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/utils/AuthUtilsTest.java index 66e2311978ee..f93f0172fbc9 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/utils/AuthUtilsTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/utils/AuthUtilsTest.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -30,15 +31,17 @@ import software.amazon.awssdk.codegen.model.intermediate.Metadata; import software.amazon.awssdk.codegen.model.intermediate.OperationModel; import software.amazon.awssdk.codegen.model.service.AuthType; +import software.amazon.awssdk.utils.CollectionUtils; public class AuthUtilsTest { @ParameterizedTest @MethodSource("serviceValues") public void testIfServiceHasBearerAuth(AuthType serviceAuthType, + List serviceAuthTypes, List opAuthTypes, Boolean expectedResult) { - IntermediateModel model = modelWith(serviceAuthType); + IntermediateModel model = modelWith(serviceAuthType, serviceAuthTypes); model.setOperations(createOperations(opAuthTypes)); assertThat(AuthUtils.usesBearerAuth(model)).isEqualTo(expectedResult); } @@ -47,10 +50,11 @@ private static Stream serviceValues() { List oneBearerOp = Arrays.asList(AuthType.BEARER, AuthType.S3V4, AuthType.NONE); List noBearerOp = Arrays.asList(AuthType.S3V4, AuthType.S3V4, AuthType.NONE); - return Stream.of(Arguments.of(AuthType.BEARER, noBearerOp, true), - Arguments.of(AuthType.BEARER, oneBearerOp, true), - Arguments.of(AuthType.S3V4, noBearerOp, false), - Arguments.of(AuthType.S3V4, oneBearerOp, true)); + return Stream.of(Arguments.of(AuthType.BEARER, Collections.emptyList(), noBearerOp, true), + Arguments.of(AuthType.BEARER, Collections.emptyList(), oneBearerOp, true), + Arguments.of(AuthType.S3V4, Collections.emptyList(), noBearerOp, false), + Arguments.of(AuthType.S3V4, Collections.emptyList(), oneBearerOp, true), + Arguments.of(AuthType.S3V4, oneBearerOp, noBearerOp, true)); } @ParameterizedTest @@ -106,6 +110,12 @@ private static IntermediateModel modelWith(AuthType authType) { return model; } + private static IntermediateModel modelWith(AuthType authType, List authTypes) { + IntermediateModel model = modelWith(authType); + model.getMetadata().setAuth(authTypes); + return model; + } + private static Map createOperations(List opAuthTypes) { return IntStream.range(0, opAuthTypes.size()) .boxed() diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/env-bearer-token-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/env-bearer-token-auth-scheme-interceptor.java new file mode 100644 index 000000000000..a7f8a8e8d336 --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/env-bearer-token-auth-scheme-interceptor.java @@ -0,0 +1,164 @@ +package software.amazon.awssdk.services.json.auth.scheme.internal; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import software.amazon.awssdk.annotations.Generated; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.SelectedAuthScheme; +import software.amazon.awssdk.core.exception.SdkException; +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.internal.util.MetricUtils; +import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; +import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; +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.HttpSigner; +import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; +import software.amazon.awssdk.identity.spi.Identity; +import software.amazon.awssdk.identity.spi.IdentityProvider; +import software.amazon.awssdk.identity.spi.IdentityProviders; +import software.amazon.awssdk.identity.spi.ResolveIdentityRequest; +import software.amazon.awssdk.identity.spi.TokenIdentity; +import software.amazon.awssdk.metrics.MetricCollector; +import software.amazon.awssdk.metrics.SdkMetric; +import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeParams; +import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; +import software.amazon.awssdk.utils.Logger; +import software.amazon.awssdk.utils.Validate; + +@Generated("software.amazon.awssdk:codegen") +@SdkInternalApi +public final class JsonAuthSchemeInterceptor implements ExecutionInterceptor { + private static Logger LOG = Logger.loggerFor(JsonAuthSchemeInterceptor.class); + + @Override + public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { + List authOptions = resolveAuthOptions(context, executionAttributes); + SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); + putSelectedAuthScheme(executionAttributes, selectedAuthScheme); + recordEnvironmentTokenBusinessMetric(selectedAuthScheme, executionAttributes); + } + + private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { + JsonAuthSchemeProvider authSchemeProvider = Validate.isInstanceOf(JsonAuthSchemeProvider.class, + executionAttributes.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_RESOLVER), + "Expected an instance of JsonAuthSchemeProvider"); + JsonAuthSchemeParams params = authSchemeParams(context.request(), executionAttributes); + return authSchemeProvider.resolveAuthScheme(params); + } + + private SelectedAuthScheme selectAuthScheme(List authOptions, + ExecutionAttributes executionAttributes) { + MetricCollector metricCollector = executionAttributes.getAttribute(SdkExecutionAttribute.API_CALL_METRIC_COLLECTOR); + Map> authSchemes = executionAttributes.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEMES); + IdentityProviders identityProviders = executionAttributes.getAttribute(SdkInternalExecutionAttribute.IDENTITY_PROVIDERS); + List> discardedReasons = new ArrayList<>(); + for (AuthSchemeOption authOption : authOptions) { + AuthScheme authScheme = authSchemes.get(authOption.schemeId()); + SelectedAuthScheme selectedAuthScheme = trySelectAuthScheme(authOption, authScheme, + identityProviders, discardedReasons, metricCollector, executionAttributes); + if (selectedAuthScheme != null) { + if (!discardedReasons.isEmpty()) { + LOG.debug(() -> String.format("%s auth will be used, discarded: '%s'", authOption.schemeId(), + discardedReasons.stream().map(Supplier::get).collect(Collectors.joining(", ")))); + } + return selectedAuthScheme; + } + } + throw SdkException + .builder() + .message( + "Failed to determine how to authenticate the user: " + + discardedReasons.stream().map(Supplier::get).collect(Collectors.joining(", "))).build(); + } + + private JsonAuthSchemeParams authSchemeParams(SdkRequest request, ExecutionAttributes executionAttributes) { + String operation = executionAttributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME); + JsonAuthSchemeParams.Builder builder = JsonAuthSchemeParams.builder().operation(operation); + return builder.build(); + } + + private SelectedAuthScheme trySelectAuthScheme(AuthSchemeOption authOption, AuthScheme authScheme, + IdentityProviders identityProviders, List> discardedReasons, MetricCollector metricCollector, + ExecutionAttributes executionAttributes) { + if (authScheme == null) { + discardedReasons.add(() -> String.format("'%s' is not enabled for this request.", authOption.schemeId())); + return null; + } + IdentityProvider identityProvider = authScheme.identityProvider(identityProviders); + if (identityProvider == null) { + discardedReasons + .add(() -> String.format("'%s' does not have an identity provider configured.", authOption.schemeId())); + return null; + } + HttpSigner signer; + try { + signer = authScheme.signer(); + } catch (RuntimeException e) { + discardedReasons.add(() -> String.format("'%s' signer could not be retrieved: %s", authOption.schemeId(), + e.getMessage())); + return null; + } + ResolveIdentityRequest.Builder identityRequestBuilder = ResolveIdentityRequest.builder(); + authOption.forEachIdentityProperty(identityRequestBuilder::putProperty); + CompletableFuture identity; + SdkMetric metric = getIdentityMetric(identityProvider); + if (metric == null) { + identity = identityProvider.resolveIdentity(identityRequestBuilder.build()); + } else { + identity = MetricUtils.reportDuration(() -> identityProvider.resolveIdentity(identityRequestBuilder.build()), + metricCollector, metric); + } + return new SelectedAuthScheme<>(identity, signer, authOption); + } + + private SdkMetric getIdentityMetric(IdentityProvider identityProvider) { + Class identityType = identityProvider.identityType(); + if (identityType == AwsCredentialsIdentity.class) { + return CoreMetric.CREDENTIALS_FETCH_DURATION; + } + if (identityType == TokenIdentity.class) { + return CoreMetric.TOKEN_FETCH_DURATION; + } + return null; + } + + private void putSelectedAuthScheme(ExecutionAttributes attributes, + SelectedAuthScheme selectedAuthScheme) { + SelectedAuthScheme existingAuthScheme = attributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); + if (existingAuthScheme != null) { + AuthSchemeOption.Builder selectedOption = selectedAuthScheme.authSchemeOption().toBuilder(); + existingAuthScheme.authSchemeOption().forEachIdentityProperty(selectedOption::putIdentityPropertyIfAbsent); + existingAuthScheme.authSchemeOption().forEachSignerProperty(selectedOption::putSignerPropertyIfAbsent); + selectedAuthScheme = new SelectedAuthScheme<>(selectedAuthScheme.identity(), selectedAuthScheme.signer(), + selectedOption.build()); + } + attributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme); + } + + private void recordEnvironmentTokenBusinessMetric(SelectedAuthScheme selectedAuthScheme, + ExecutionAttributes executionAttributes) { + String tokenFromEnv = executionAttributes.getAttribute(SdkInternalExecutionAttribute.TOKEN_CONFIGURED_FROM_ENV); + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(BearerAuthScheme.SCHEME_ID) + && selectedAuthScheme.identity().isDone()) { + if (selectedAuthScheme.identity().getNow(null) instanceof TokenIdentity) { + TokenIdentity configuredToken = (TokenIdentity) selectedAuthScheme.identity().getNow(null); + if (configuredToken.token().equals(tokenFromEnv)) { + executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS).addMetric( + BusinessMetricFeatureId.BEARER_SERVICE_ENV_VARS.value()); + } + } + } + } +} diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-bearer-auth-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-bearer-auth-client-builder-class.java index d07aa8df2aa6..ee8f9a73d3e5 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-bearer-auth-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-bearer-auth-client-builder-class.java @@ -60,14 +60,15 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-class.java index 6fc7c8605c76..a0bdac67d04d 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-class.java @@ -71,15 +71,16 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-endpoints-auth-params.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-endpoints-auth-params.java index 9c5e9647fa06..360d3664eaad 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-endpoints-auth-params.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-endpoints-auth-params.java @@ -69,14 +69,15 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-internal-defaults-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-internal-defaults-class.java index 15fa5602d745..9b143b9ccd69 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-internal-defaults-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-client-builder-internal-defaults-class.java @@ -56,10 +56,12 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-composed-sync-default-client-builder.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-composed-sync-default-client-builder.java index a765c15c4c34..117e19038881 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-composed-sync-default-client-builder.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-composed-sync-default-client-builder.java @@ -66,15 +66,16 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-env-bearer-token-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-env-bearer-token-client-builder-class.java new file mode 100644 index 000000000000..48ecf08535fa --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-env-bearer-token-client-builder-class.java @@ -0,0 +1,227 @@ +package software.amazon.awssdk.services.json; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Generated; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.auth.credentials.TokenUtils; +import software.amazon.awssdk.auth.token.credentials.StaticTokenProvider; +import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider; +import software.amazon.awssdk.awscore.auth.AuthSchemePreferenceResolver; +import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; +import software.amazon.awssdk.awscore.client.config.AwsClientOption; +import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; +import software.amazon.awssdk.awscore.retry.AwsRetryStrategy; +import software.amazon.awssdk.core.SdkPlugin; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory; +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.core.retry.RetryMode; +import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; +import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; +import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; +import software.amazon.awssdk.identity.spi.IdentityProvider; +import software.amazon.awssdk.identity.spi.IdentityProviders; +import software.amazon.awssdk.identity.spi.TokenIdentity; +import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.retries.api.RetryStrategy; +import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; +import software.amazon.awssdk.services.json.auth.scheme.internal.JsonAuthSchemeInterceptor; +import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; +import software.amazon.awssdk.services.json.endpoints.internal.JsonRequestSetEndpointInterceptor; +import software.amazon.awssdk.services.json.endpoints.internal.JsonResolveEndpointInterceptor; +import software.amazon.awssdk.services.json.internal.EnvironmentTokenSystemSettings; +import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder; +import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.Validate; + +/** + * Internal base class for {@link DefaultJsonClientBuilder} and {@link DefaultJsonAsyncClientBuilder}. + */ +@Generated("software.amazon.awssdk:codegen") +@SdkInternalApi +abstract class DefaultJsonBaseClientBuilder, C> extends AwsDefaultClientBuilder { + private final Map> additionalAuthSchemes = new HashMap<>(); + + @Override + protected final String serviceEndpointPrefix() { + return "json-service-endpoint"; + } + + @Override + protected final String serviceName() { + return "Json"; + } + + @Override + protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + Optional tokenFromEnv = new EnvironmentTokenSystemSettings().getStringValue(); + if (tokenFromEnv.isPresent() && config.option(SdkClientOption.AUTH_SCHEME_PROVIDER) == null + && config.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER) == null) { + c.option(SdkClientOption.AUTH_SCHEME_PROVIDER, + JsonAuthSchemeProvider.defaultProvider(Collections.singletonList("httpBearerAuth"))); + c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, StaticTokenProvider.create(tokenFromEnv::get)); + c.option( + SdkClientOption.EXECUTION_ATTRIBUTES, + ExecutionAttributes.builder() + .put(SdkInternalExecutionAttribute.TOKEN_CONFIGURED_FROM_ENV, tokenFromEnv.get()).build()); + } else { + c.option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)); + } + }); + } + + @Override + protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) { + List endpointInterceptors = new ArrayList<>(); + endpointInterceptors.add(new JsonAuthSchemeInterceptor()); + endpointInterceptors.add(new JsonResolveEndpointInterceptor()); + endpointInterceptors.add(new JsonRequestSetEndpointInterceptor()); + ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); + List interceptors = interceptorFactory + .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); + List additionalInterceptors = new ArrayList<>(); + interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); + interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); + interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS)); + SdkClientConfiguration.Builder builder = config.toBuilder(); + builder.lazyOption(SdkClientOption.IDENTITY_PROVIDERS, c -> { + IdentityProviders.Builder result = IdentityProviders.builder(); + IdentityProvider tokenIdentityProvider = c.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER); + if (tokenIdentityProvider != null) { + result.putIdentityProvider(tokenIdentityProvider); + } + return result.build(); + }); + builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); + builder.lazyOptionIfAbsent( + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") + .serviceProfileProperty("json_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); + return builder.build(); + } + + @Override + protected final String signingName() { + return "json-service"; + } + + private JsonEndpointProvider defaultEndpointProvider() { + return JsonEndpointProvider.defaultProvider(); + } + + public B authSchemeProvider(JsonAuthSchemeProvider authSchemeProvider) { + clientConfiguration.option(SdkClientOption.AUTH_SCHEME_PROVIDER, authSchemeProvider); + return thisBuilder(); + } + + private JsonAuthSchemeProvider defaultAuthSchemeProvider(SdkClientConfiguration config) { + AuthSchemePreferenceResolver authSchemePreferenceProvider = AuthSchemePreferenceResolver.builder() + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)).build(); + List preferences = authSchemePreferenceProvider.resolveAuthSchemePreference(); + if (!preferences.isEmpty()) { + return JsonAuthSchemeProvider.defaultProvider(preferences); + } + return JsonAuthSchemeProvider.defaultProvider(); + } + + @Override + public B putAuthScheme(AuthScheme authScheme) { + additionalAuthSchemes.put(authScheme.schemeId(), authScheme); + return thisBuilder(); + } + + private Map> authSchemes() { + Map> schemes = new HashMap<>(2 + this.additionalAuthSchemes.size()); + BearerAuthScheme bearerAuthScheme = BearerAuthScheme.create(); + schemes.put(bearerAuthScheme.schemeId(), bearerAuthScheme); + NoAuthAuthScheme noAuthAuthScheme = NoAuthAuthScheme.create(); + schemes.put(noAuthAuthScheme.schemeId(), noAuthAuthScheme); + schemes.putAll(this.additionalAuthSchemes); + return schemes; + } + + private IdentityProvider defaultTokenProvider() { + return DefaultAwsTokenProvider.create(); + } + + @Override + protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) { + List internalPlugins = internalPlugins(config); + List externalPlugins = plugins(); + if (internalPlugins.isEmpty() && externalPlugins.isEmpty()) { + return config; + } + List plugins = CollectionUtils.mergeLists(internalPlugins, externalPlugins); + SdkClientConfiguration.Builder configuration = config.toBuilder(); + JsonServiceClientConfigurationBuilder serviceConfigBuilder = new JsonServiceClientConfigurationBuilder(configuration); + for (SdkPlugin plugin : plugins) { + plugin.configureClient(serviceConfigBuilder); + } + updateRetryStrategyClientConfiguration(configuration); + return configuration.build(); + } + + private void updateRetryStrategyClientConfiguration(SdkClientConfiguration.Builder configuration) { + ClientOverrideConfiguration.Builder builder = configuration.asOverrideConfigurationBuilder(); + RetryMode retryMode = builder.retryMode(); + if (retryMode != null) { + configuration.option(SdkClientOption.RETRY_STRATEGY, AwsRetryStrategy.forRetryMode(retryMode)); + } else { + Consumer> configurator = builder.retryStrategyConfigurator(); + if (configurator != null) { + RetryStrategy.Builder defaultBuilder = AwsRetryStrategy.defaultRetryStrategy().toBuilder(); + configurator.accept(defaultBuilder); + configuration.option(SdkClientOption.RETRY_STRATEGY, defaultBuilder.build()); + } else { + RetryStrategy retryStrategy = builder.retryStrategy(); + if (retryStrategy != null) { + configuration.option(SdkClientOption.RETRY_STRATEGY, retryStrategy); + } + } + } + configuration.option(SdkClientOption.CONFIGURED_RETRY_MODE, null); + configuration.option(SdkClientOption.CONFIGURED_RETRY_STRATEGY, null); + configuration.option(SdkClientOption.CONFIGURED_RETRY_CONFIGURATOR, null); + } + + private List internalPlugins(SdkClientConfiguration config) { + return Collections.emptyList(); + } + + protected static void validateClientOptions(SdkClientConfiguration c) { + Validate.notNull(c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER), + "The 'tokenProvider' must be configured in the client builder."); + } +} diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-multi-auth-sigv4a-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-multi-auth-sigv4a-client-builder-class.java index 38d843d80fb0..75faf2cad7a8 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-multi-auth-sigv4a-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-multi-auth-sigv4a-client-builder-class.java @@ -59,10 +59,12 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-ops-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-ops-client-builder-class.java index aab7db9ccff5..72d4f526bfb3 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-ops-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-ops-client-builder-class.java @@ -8,6 +8,8 @@ import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.auth.credentials.TokenUtils; +import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider; import software.amazon.awssdk.awscore.auth.AuthSchemePreferenceResolver; import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; @@ -26,6 +28,7 @@ import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; +import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.retries.api.RetryStrategy; @@ -36,6 +39,7 @@ import software.amazon.awssdk.services.database.endpoints.internal.DatabaseResolveEndpointInterceptor; import software.amazon.awssdk.services.database.internal.DatabaseServiceClientConfigurationBuilder; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.Validate; /** * Internal base class for {@link DefaultDatabaseClientBuilder} and {@link DefaultDatabaseAsyncClientBuilder}. @@ -58,10 +62,15 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + }); } @Override @@ -80,6 +89,10 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon SdkClientConfiguration.Builder builder = config.toBuilder(); builder.lazyOption(SdkClientOption.IDENTITY_PROVIDERS, c -> { IdentityProviders.Builder result = IdentityProviders.builder(); + IdentityProvider tokenIdentityProvider = c.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER); + if (tokenIdentityProvider != null) { + result.putIdentityProvider(tokenIdentityProvider); + } IdentityProvider credentialsIdentityProvider = c.get(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER); if (credentialsIdentityProvider != null) { result.putIdentityProvider(credentialsIdentityProvider); @@ -150,6 +163,10 @@ private Map> authSchemes() { return schemes; } + private IdentityProvider defaultTokenProvider() { + return DefaultAwsTokenProvider.create(); + } + @Override protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) { List internalPlugins = internalPlugins(config); @@ -196,5 +213,7 @@ private List internalPlugins(SdkClientConfiguration config) { } protected static void validateClientOptions(SdkClientConfiguration c) { + Validate.notNull(c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER), + "The 'tokenProvider' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-service-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-service-client-builder-class.java index 30958f0e2876..0be9c031d828 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-service-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-no-auth-service-client-builder-class.java @@ -55,10 +55,12 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-query-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-query-client-builder-class.java index 89a0c285bf60..19b8d5abbae1 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-query-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/sra/test-query-client-builder-class.java @@ -67,14 +67,15 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) - .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config)) + .option(SdkClientOption.AUTH_SCHEMES, authSchemes()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java index 9895ae765031..c7932ab9f7c6 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-bearer-auth-client-builder-class.java @@ -53,13 +53,14 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) - .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) + .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner()); + }); } @Override @@ -69,7 +70,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new JsonRequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); @@ -85,21 +86,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon }); builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") + .serviceProfileProperty("json_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } @@ -167,8 +168,8 @@ private List internalPlugins(SdkClientConfiguration config) { protected static void validateClientOptions(SdkClientConfiguration c) { Validate.notNull(c.option(SdkAdvancedClientOption.TOKEN_SIGNER), - "The 'overrideConfiguration.advancedOption[TOKEN_SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[TOKEN_SIGNER]' must be configured in the client builder."); Validate.notNull(c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER), - "The 'tokenProvider' must be configured in the client builder."); + "The 'tokenProvider' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java index 6013da8b2ea5..2eca7dfdcf2c 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java @@ -64,15 +64,16 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) - .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) + .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner()); + }); } @Override @@ -82,82 +83,82 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new JsonRequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS)); ServiceConfiguration.Builder serviceConfigBuilder = ((ServiceConfiguration) config - .option(SdkClientOption.SERVICE_CONFIGURATION)).toBuilder(); + .option(SdkClientOption.SERVICE_CONFIGURATION)).toBuilder(); serviceConfigBuilder.profileFile(serviceConfigBuilder.profileFileSupplier() != null ? serviceConfigBuilder - .profileFileSupplier() : config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)); + .profileFileSupplier() : config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)); serviceConfigBuilder.profileName(serviceConfigBuilder.profileName() != null ? serviceConfigBuilder.profileName() : config - .option(SdkClientOption.PROFILE_NAME)); + .option(SdkClientOption.PROFILE_NAME)); if (serviceConfigBuilder.dualstackEnabled() != null) { Validate.validState( - config.option(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED) == null, - "Dualstack has been configured on both ServiceConfiguration and the client/global level. Please limit dualstack configuration to one location."); + config.option(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED) == null, + "Dualstack has been configured on both ServiceConfiguration and the client/global level. Please limit dualstack configuration to one location."); } else { serviceConfigBuilder.dualstackEnabled(config.option(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)); } if (serviceConfigBuilder.fipsModeEnabled() != null) { Validate.validState( - config.option(AwsClientOption.FIPS_ENDPOINT_ENABLED) == null, - "Fips has been configured on both ServiceConfiguration and the client/global level. Please limit fips configuration to one location."); + config.option(AwsClientOption.FIPS_ENDPOINT_ENABLED) == null, + "Fips has been configured on both ServiceConfiguration and the client/global level. Please limit fips configuration to one location."); } else { serviceConfigBuilder.fipsModeEnabled(config.option(AwsClientOption.FIPS_ENDPOINT_ENABLED)); } if (serviceConfigBuilder.useArnRegionEnabled() != null) { Validate.validState( - clientContextParams.get(JsonClientContextParams.USE_ARN_REGION) == null, - "UseArnRegion has been configured on both ServiceConfiguration and the client/global level. Please limit UseArnRegion configuration to one location."); + clientContextParams.get(JsonClientContextParams.USE_ARN_REGION) == null, + "UseArnRegion has been configured on both ServiceConfiguration and the client/global level. Please limit UseArnRegion configuration to one location."); } else { serviceConfigBuilder.useArnRegionEnabled(clientContextParams.get(JsonClientContextParams.USE_ARN_REGION)); } if (serviceConfigBuilder.multiRegionEnabled() != null) { Validate.validState( - clientContextParams.get(JsonClientContextParams.DISABLE_MULTI_REGION_ACCESS_POINTS) == null, - "DisableMultiRegionAccessPoints has been configured on both ServiceConfiguration and the client/global level. Please limit DisableMultiRegionAccessPoints configuration to one location."); + clientContextParams.get(JsonClientContextParams.DISABLE_MULTI_REGION_ACCESS_POINTS) == null, + "DisableMultiRegionAccessPoints has been configured on both ServiceConfiguration and the client/global level. Please limit DisableMultiRegionAccessPoints configuration to one location."); } else if (clientContextParams.get(JsonClientContextParams.DISABLE_MULTI_REGION_ACCESS_POINTS) != null) { serviceConfigBuilder.multiRegionEnabled(!clientContextParams - .get(JsonClientContextParams.DISABLE_MULTI_REGION_ACCESS_POINTS)); + .get(JsonClientContextParams.DISABLE_MULTI_REGION_ACCESS_POINTS)); } if (serviceConfigBuilder.pathStyleAccessEnabled() != null) { Validate.validState( - clientContextParams.get(JsonClientContextParams.FORCE_PATH_STYLE) == null, - "ForcePathStyle has been configured on both ServiceConfiguration and the client/global level. Please limit ForcePathStyle configuration to one location."); + clientContextParams.get(JsonClientContextParams.FORCE_PATH_STYLE) == null, + "ForcePathStyle has been configured on both ServiceConfiguration and the client/global level. Please limit ForcePathStyle configuration to one location."); } else { serviceConfigBuilder.pathStyleAccessEnabled(clientContextParams.get(JsonClientContextParams.FORCE_PATH_STYLE)); } if (serviceConfigBuilder.accelerateModeEnabled() != null) { Validate.validState( - clientContextParams.get(JsonClientContextParams.ACCELERATE) == null, - "Accelerate has been configured on both ServiceConfiguration and the client/global level. Please limit Accelerate configuration to one location."); + clientContextParams.get(JsonClientContextParams.ACCELERATE) == null, + "Accelerate has been configured on both ServiceConfiguration and the client/global level. Please limit Accelerate configuration to one location."); } else { serviceConfigBuilder.accelerateModeEnabled(clientContextParams.get(JsonClientContextParams.ACCELERATE)); } Boolean checksumValidationEnabled = serviceConfigBuilder.checksumValidationEnabled(); if (checksumValidationEnabled != null) { Validate.validState( - config.option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION) == null, - "Checksum behavior has been configured on both ServiceConfiguration and the client/global level. Please limit checksum behavior configuration to one location."); + config.option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION) == null, + "Checksum behavior has been configured on both ServiceConfiguration and the client/global level. Please limit checksum behavior configuration to one location."); Validate.validState( - config.option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION) == null, - "Checksum behavior has been configured on both ServiceConfiguration and the client/global level. Please limit checksum behavior configuration to one location."); + config.option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION) == null, + "Checksum behavior has been configured on both ServiceConfiguration and the client/global level. Please limit checksum behavior configuration to one location."); if (checksumValidationEnabled) { config = config.toBuilder() - .option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation.WHEN_SUPPORTED) - .option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, ResponseChecksumValidation.WHEN_SUPPORTED).build(); + .option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation.WHEN_SUPPORTED) + .option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, ResponseChecksumValidation.WHEN_SUPPORTED).build(); } else { config = config.toBuilder() - .option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation.WHEN_REQUIRED) - .option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, ResponseChecksumValidation.WHEN_REQUIRED).build(); + .option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation.WHEN_REQUIRED) + .option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION, ResponseChecksumValidation.WHEN_REQUIRED).build(); } } ServiceConfiguration finalServiceConfig = serviceConfigBuilder.build(); clientContextParams.put(JsonClientContextParams.USE_ARN_REGION, finalServiceConfig.useArnRegionEnabled()); clientContextParams.put(JsonClientContextParams.DISABLE_MULTI_REGION_ACCESS_POINTS, - !finalServiceConfig.multiRegionEnabled()); + !finalServiceConfig.multiRegionEnabled()); clientContextParams.put(JsonClientContextParams.FORCE_PATH_STYLE, finalServiceConfig.pathStyleAccessEnabled()); clientContextParams.put(JsonClientContextParams.ACCELERATE, finalServiceConfig.accelerateModeEnabled()); SdkClientConfiguration.Builder builder = config.toBuilder(); @@ -182,21 +183,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon } builder.option(SdkClientOption.SERVICE_CONFIGURATION, finalServiceConfig); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") + .serviceProfileProperty("json_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); SdkClientConfiguration clientConfig = config; builder.lazyOption(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, c -> resolveRequestChecksumCalculation(clientConfig)); @@ -301,9 +302,9 @@ private RequestChecksumCalculation resolveRequestChecksumCalculation(SdkClientCo RequestChecksumCalculation configuredChecksumCalculation = config.option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION); if (configuredChecksumCalculation == null) { configuredChecksumCalculation = RequestChecksumCalculationResolver.create() - .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(config.option(SdkClientOption.PROFILE_NAME)) - .defaultChecksumCalculation(RequestChecksumCalculation.WHEN_SUPPORTED).resolve(); + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .defaultChecksumCalculation(RequestChecksumCalculation.WHEN_SUPPORTED).resolve(); } return configuredChecksumCalculation; } @@ -312,19 +313,19 @@ private ResponseChecksumValidation resolveResponseChecksumValidation(SdkClientCo ResponseChecksumValidation configuredChecksumValidation = config.option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION); if (configuredChecksumValidation == null) { configuredChecksumValidation = ResponseChecksumValidationResolver.create() - .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(config.option(SdkClientOption.PROFILE_NAME)) - .defaultChecksumValidation(ResponseChecksumValidation.WHEN_SUPPORTED).resolve(); + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .defaultChecksumValidation(ResponseChecksumValidation.WHEN_SUPPORTED).resolve(); } return configuredChecksumValidation; } protected static void validateClientOptions(SdkClientConfiguration c) { Validate.notNull(c.option(SdkAdvancedClientOption.SIGNER), - "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); Validate.notNull(c.option(SdkAdvancedClientOption.TOKEN_SIGNER), - "The 'overrideConfiguration.advancedOption[TOKEN_SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[TOKEN_SIGNER]' must be configured in the client builder."); Validate.notNull(c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER), - "The 'tokenProvider' must be configured in the client builder."); + "The 'tokenProvider' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java index 52c27dfcc8ac..4a8a346f1c76 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-endpoints-auth-params.java @@ -61,14 +61,15 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) - .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) + .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner()); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java index 12dec5b9986d..4f1e5bbd8e0d 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java @@ -50,9 +50,11 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false); + }); } @Override @@ -70,7 +72,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new JsonRequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); @@ -86,21 +88,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon }); builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") + .serviceProfileProperty("json_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } @@ -164,6 +166,6 @@ private List internalPlugins(SdkClientConfiguration config) { protected static void validateClientOptions(SdkClientConfiguration c) { Validate.notNull(c.option(SdkAdvancedClientOption.SIGNER), - "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java index 52f9d10e821f..778b676c4975 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-composed-sync-default-client-builder.java @@ -59,15 +59,16 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) - .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .option(SdkClientOption.SERVICE_CONFIGURATION, ServiceConfiguration.builder().build()) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) + .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner()); + }); } @Override @@ -77,17 +78,17 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new JsonRequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/json/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS)); ServiceConfiguration.Builder serviceConfigBuilder = ((ServiceConfiguration) config - .option(SdkClientOption.SERVICE_CONFIGURATION)).toBuilder(); + .option(SdkClientOption.SERVICE_CONFIGURATION)).toBuilder(); serviceConfigBuilder.profileFile(serviceConfigBuilder.profileFileSupplier() != null ? serviceConfigBuilder - .profileFileSupplier() : config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)); + .profileFileSupplier() : config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)); serviceConfigBuilder.profileName(serviceConfigBuilder.profileName() != null ? serviceConfigBuilder.profileName() : config - .option(SdkClientOption.PROFILE_NAME)); + .option(SdkClientOption.PROFILE_NAME)); ServiceConfiguration finalServiceConfig = serviceConfigBuilder.build(); SdkClientConfiguration.Builder builder = config.toBuilder(); builder.lazyOption(SdkClientOption.IDENTITY_PROVIDERS, c -> { @@ -105,21 +106,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.option(SdkClientOption.SERVICE_CONFIGURATION, finalServiceConfig); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") - .serviceProfileProperty("json_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_JSON_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlJson") + .serviceProfileProperty("json_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); SdkClientConfiguration clientConfig = config; builder.lazyOption(SdkClientOption.REQUEST_CHECKSUM_CALCULATION, c -> resolveRequestChecksumCalculation(clientConfig)); @@ -220,9 +221,9 @@ private RequestChecksumCalculation resolveRequestChecksumCalculation(SdkClientCo RequestChecksumCalculation configuredChecksumCalculation = config.option(SdkClientOption.REQUEST_CHECKSUM_CALCULATION); if (configuredChecksumCalculation == null) { configuredChecksumCalculation = RequestChecksumCalculationResolver.create() - .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(config.option(SdkClientOption.PROFILE_NAME)) - .defaultChecksumCalculation(RequestChecksumCalculation.WHEN_SUPPORTED).resolve(); + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .defaultChecksumCalculation(RequestChecksumCalculation.WHEN_SUPPORTED).resolve(); } return configuredChecksumCalculation; } @@ -231,19 +232,19 @@ private ResponseChecksumValidation resolveResponseChecksumValidation(SdkClientCo ResponseChecksumValidation configuredChecksumValidation = config.option(SdkClientOption.RESPONSE_CHECKSUM_VALIDATION); if (configuredChecksumValidation == null) { configuredChecksumValidation = ResponseChecksumValidationResolver.create() - .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(config.option(SdkClientOption.PROFILE_NAME)) - .defaultChecksumValidation(ResponseChecksumValidation.WHEN_SUPPORTED).resolve(); + .profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .defaultChecksumValidation(ResponseChecksumValidation.WHEN_SUPPORTED).resolve(); } return configuredChecksumValidation; } protected static void validateClientOptions(SdkClientConfiguration c) { Validate.notNull(c.option(SdkAdvancedClientOption.SIGNER), - "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); Validate.notNull(c.option(SdkAdvancedClientOption.TOKEN_SIGNER), - "The 'overrideConfiguration.advancedOption[TOKEN_SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[TOKEN_SIGNER]' must be configured in the client builder."); Validate.notNull(c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER), - "The 'tokenProvider' must be configured in the client builder."); + "The 'tokenProvider' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java index ddd2b3427e71..eb724ba82245 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-service-client-builder-class.java @@ -54,9 +54,11 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false); + }); } @Override @@ -66,7 +68,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new H2RequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/h2/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/h2/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); @@ -82,21 +84,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon }); builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2") - .serviceProfileProperty("h2_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2") + .serviceProfileProperty("h2_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } @@ -118,7 +120,7 @@ private H2EndpointProvider defaultEndpointProvider() { protected final AttributeMap serviceHttpConfig() { AttributeMap result = AttributeMap.empty(); return result.merge(AttributeMap.builder().put(SdkHttpConfigurationOption.PROTOCOL, Protocol.HTTP2) - .put(SdkHttpConfigurationOption.PROTOCOL_NEGOTIATION, ProtocolNegotiation.ALPN).build()); + .put(SdkHttpConfigurationOption.PROTOCOL_NEGOTIATION, ProtocolNegotiation.ALPN).build()); } @Override @@ -167,6 +169,6 @@ private List internalPlugins(SdkClientConfiguration config) { protected static void validateClientOptions(SdkClientConfiguration c) { Validate.notNull(c.option(SdkAdvancedClientOption.SIGNER), - "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java index a296652d9b41..a9dcec96847e 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-h2-usePriorKnowledgeForH2-service-client-builder-class.java @@ -53,9 +53,11 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false); + }); } @Override @@ -65,7 +67,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new H2RequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/h2/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/h2/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); @@ -81,21 +83,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon }); builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2") - .serviceProfileProperty("h2_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_H2_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlH2") + .serviceProfileProperty("h2_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } @@ -165,6 +167,6 @@ private List internalPlugins(SdkClientConfiguration config) { protected static void validateClientOptions(SdkClientConfiguration c) { Validate.notNull(c.option(SdkAdvancedClientOption.SIGNER), - "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java index 4c7699ac4c6d..5ec8c0facf9a 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-ops-client-builder-class.java @@ -6,7 +6,10 @@ import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.auth.credentials.TokenUtils; import software.amazon.awssdk.auth.signer.Aws4Signer; +import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider; +import software.amazon.awssdk.auth.token.signer.aws.BearerTokenSigner; import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider; @@ -22,6 +25,7 @@ import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; +import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.protocols.json.internal.unmarshall.SdkClientJsonProtocolAdvancedOption; import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; import software.amazon.awssdk.retries.api.RetryStrategy; @@ -38,7 +42,7 @@ @Generated("software.amazon.awssdk:codegen") @SdkInternalApi abstract class DefaultDatabaseBaseClientBuilder, C> extends - AwsDefaultClientBuilder { + AwsDefaultClientBuilder { @Override protected final String serviceEndpointPrefix() { return "database-service-endpoint"; @@ -51,9 +55,15 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) + .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner()); + }); } @Override @@ -63,7 +73,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new DatabaseRequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/database/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/database/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); @@ -71,6 +81,10 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon SdkClientConfiguration.Builder builder = config.toBuilder(); builder.lazyOption(SdkClientOption.IDENTITY_PROVIDERS, c -> { IdentityProviders.Builder result = IdentityProviders.builder(); + IdentityProvider tokenIdentityProvider = c.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER); + if (tokenIdentityProvider != null) { + result.putIdentityProvider(tokenIdentityProvider); + } IdentityProvider credentialsIdentityProvider = c.get(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER); if (credentialsIdentityProvider != null) { result.putIdentityProvider(credentialsIdentityProvider); @@ -79,21 +93,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon }); builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") - .serviceProfileProperty("database_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") + .serviceProfileProperty("database_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } @@ -111,6 +125,14 @@ private DatabaseEndpointProvider defaultEndpointProvider() { return DatabaseEndpointProvider.defaultProvider(); } + private IdentityProvider defaultTokenProvider() { + return DefaultAwsTokenProvider.create(); + } + + private Signer defaultTokenSigner() { + return BearerTokenSigner.create(); + } + @Override protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) { List internalPlugins = internalPlugins(config); @@ -121,7 +143,7 @@ protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) { List plugins = CollectionUtils.mergeLists(internalPlugins, externalPlugins); SdkClientConfiguration.Builder configuration = config.toBuilder(); DatabaseServiceClientConfigurationBuilder serviceConfigBuilder = new DatabaseServiceClientConfigurationBuilder( - configuration); + configuration); for (SdkPlugin plugin : plugins) { plugin.configureClient(serviceConfigBuilder); } @@ -158,6 +180,10 @@ private List internalPlugins(SdkClientConfiguration config) { protected static void validateClientOptions(SdkClientConfiguration c) { Validate.notNull(c.option(SdkAdvancedClientOption.SIGNER), - "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); + "The 'overrideConfiguration.advancedOption[SIGNER]' must be configured in the client builder."); + Validate.notNull(c.option(SdkAdvancedClientOption.TOKEN_SIGNER), + "The 'overrideConfiguration.advancedOption[TOKEN_SIGNER]' must be configured in the client builder."); + Validate.notNull(c.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER), + "The 'tokenProvider' must be configured in the client builder."); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java index a5aedee94c63..6a8e2290d918 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-no-auth-service-client-builder-class.java @@ -33,7 +33,7 @@ @Generated("software.amazon.awssdk:codegen") @SdkInternalApi abstract class DefaultDatabaseBaseClientBuilder, C> extends - AwsDefaultClientBuilder { + AwsDefaultClientBuilder { @Override protected final String serviceEndpointPrefix() { return "database-service-endpoint"; @@ -46,8 +46,10 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()).option( - SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()).option( + SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false); + }); } @Override @@ -57,7 +59,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon endpointInterceptors.add(new DatabaseRequestSetEndpointInterceptor()); ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory(); List interceptors = interceptorFactory - .getInterceptors("software/amazon/awssdk/services/database/execution.interceptors"); + .getInterceptors("software/amazon/awssdk/services/database/execution.interceptors"); List additionalInterceptors = new ArrayList<>(); interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors); interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors); @@ -69,21 +71,21 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon }); builder.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors); builder.lazyOptionIfAbsent( - SdkClientOption.CLIENT_ENDPOINT_PROVIDER, - c -> AwsClientEndpointProvider - .builder() - .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") - .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") - .serviceProfileProperty("database_service") - .serviceEndpointPrefix(serviceEndpointPrefix()) - .defaultProtocol("https") - .region(c.get(AwsClientOption.AWS_REGION)) - .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) - .profileName(c.get(SdkClientOption.PROFILE_NAME)) - .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, - c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) - .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) - .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); + SdkClientOption.CLIENT_ENDPOINT_PROVIDER, + c -> AwsClientEndpointProvider + .builder() + .serviceEndpointOverrideEnvironmentVariable("AWS_ENDPOINT_URL_DATABASE_SERVICE") + .serviceEndpointOverrideSystemProperty("aws.endpointUrlDatabase") + .serviceProfileProperty("database_service") + .serviceEndpointPrefix(serviceEndpointPrefix()) + .defaultProtocol("https") + .region(c.get(AwsClientOption.AWS_REGION)) + .profileFile(c.get(SdkClientOption.PROFILE_FILE_SUPPLIER)) + .profileName(c.get(SdkClientOption.PROFILE_NAME)) + .putAdvancedOption(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, + c.get(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT)) + .dualstackEnabled(c.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED)) + .fipsEnabled(c.get(AwsClientOption.FIPS_ENDPOINT_ENABLED)).build()); builder.option(SdkClientJsonProtocolAdvancedOption.ENABLE_FAST_UNMARSHALLER, true); return builder.build(); } @@ -107,7 +109,7 @@ protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) { List plugins = CollectionUtils.mergeLists(internalPlugins, externalPlugins); SdkClientConfiguration.Builder configuration = config.toBuilder(); DatabaseServiceClientConfigurationBuilder serviceConfigBuilder = new DatabaseServiceClientConfigurationBuilder( - configuration); + configuration); for (SdkPlugin plugin : plugins) { plugin.configureClient(serviceConfigBuilder); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java index df04abcb8bef..2ef919b3671f 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java @@ -60,14 +60,15 @@ protected final String serviceName() { @Override protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) { - return config.merge(c -> c - .option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) - .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) - .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) - .lazyOption(AwsClientOption.TOKEN_PROVIDER, - p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) - .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) - .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner())); + return config.merge(c -> { + c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider()) + .option(SdkAdvancedClientOption.SIGNER, defaultSigner()) + .option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false) + .lazyOption(AwsClientOption.TOKEN_PROVIDER, + p -> TokenUtils.toSdkTokenProvider(p.get(AwsClientOption.TOKEN_IDENTITY_PROVIDER))) + .option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, defaultTokenProvider()) + .option(SdkAdvancedClientOption.TOKEN_SIGNER, defaultTokenSigner()); + }); } @Override diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json-bearer-auth/customization-env-bearer-token.config b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json-bearer-auth/customization-env-bearer-token.config new file mode 100644 index 000000000000..2edb12c857bc --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json-bearer-auth/customization-env-bearer-token.config @@ -0,0 +1,3 @@ +{ + "enableEnvironmentBearerToken": true +} diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-environment-token-system-settings-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-environment-token-system-settings-class.java new file mode 100644 index 000000000000..bb604b75807b --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-environment-token-system-settings-class.java @@ -0,0 +1,24 @@ +package software.amazon.awssdk.services.json.internal; + +import software.amazon.awssdk.annotations.Generated; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.SystemSetting; + +@Generated("software.amazon.awssdk:codegen") +@SdkInternalApi +public class EnvironmentTokenSystemSettings implements SystemSetting { + @Override + public String property() { + return "aws.bearerTokenJsonService"; + } + + @Override + public String environmentVariable() { + return "AWS_BEARER_TOKEN_JSON_SERVICE"; + } + + @Override + public String defaultValue() { + return null; + } +} diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration-withchecksum-builder.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration-withchecksum-builder.java index 3552d6abd9c3..f535c3d73c74 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration-withchecksum-builder.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration-withchecksum-builder.java @@ -18,6 +18,7 @@ import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeProvider; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.identity.spi.IdentityProvider; +import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.json.JsonServiceClientConfiguration; import software.amazon.awssdk.services.json.auth.scheme.JsonAuthSchemeProvider; @@ -167,6 +168,23 @@ public JsonAuthSchemeProvider authSchemeProvider() { + JsonAuthSchemeProvider.class.getSimpleName()); } + /** + * Sets the value for token provider + */ + @Override + public JsonServiceClientConfiguration.Builder tokenProvider(IdentityProvider tokenProvider) { + config.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER, tokenProvider); + return this; + } + + /** + * Gets the value for token provider + */ + @Override + public IdentityProvider tokenProvider() { + return config.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER); + } + /** * Sets the value for client behavior for response checksum validation */ diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java index cde970af6402..a73691ab1df1 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java @@ -23,6 +23,7 @@ import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.identity.spi.IdentityProvider; +import software.amazon.awssdk.identity.spi.TokenIdentity; import software.amazon.awssdk.utils.builder.SdkBuilder; /** @@ -31,10 +32,12 @@ @SdkPublicApi public final class AwsRequestOverrideConfiguration extends RequestOverrideConfiguration { private final IdentityProvider credentialsProvider; + private final IdentityProvider tokenIdentityProvider; private AwsRequestOverrideConfiguration(BuilderImpl builder) { super(builder); this.credentialsProvider = builder.awsCredentialsProvider; + this.tokenIdentityProvider = builder.tokenIdentityProvider; } /** @@ -75,6 +78,16 @@ public Optional> credentialsI return Optional.ofNullable(credentialsProvider); } + /** + * The optional {@link IdentityProvider} that will provide a token identity to be used to + * authenticate this request. + * + * @return The optional {@link IdentityProvider}. + */ + public Optional> tokenIdentityProvider() { + return Optional.ofNullable(tokenIdentityProvider); + } + @Override public Builder toBuilder() { return new BuilderImpl(this); @@ -97,7 +110,8 @@ public boolean equals(Object o) { return false; } AwsRequestOverrideConfiguration that = (AwsRequestOverrideConfiguration) o; - return Objects.equals(credentialsProvider, that.credentialsProvider); + return Objects.equals(credentialsProvider, that.credentialsProvider) && + Objects.equals(tokenIdentityProvider, that.tokenIdentityProvider); } @Override @@ -105,6 +119,7 @@ public int hashCode() { int hashCode = 1; hashCode = 31 * hashCode + super.hashCode(); hashCode = 31 * hashCode + Objects.hashCode(credentialsProvider); + hashCode = 31 * hashCode + Objects.hashCode(tokenIdentityProvider); return hashCode; } @@ -139,6 +154,17 @@ default Builder credentialsProvider(IdentityProvider} that will provide a token identity to be used + * to authenticate this request. + * + * @param tokenIdentityProvider The {@link IdentityProvider}. + * @return This object for chaining. + */ + default Builder tokenIdentityProvider(IdentityProvider tokenIdentityProvider) { + throw new UnsupportedOperationException(); + } + @Override AwsRequestOverrideConfiguration build(); } @@ -146,6 +172,7 @@ default Builder credentialsProvider(IdentityProvider implements Builder { private IdentityProvider awsCredentialsProvider; + private IdentityProvider tokenIdentityProvider; private BuilderImpl() { } @@ -157,6 +184,7 @@ private BuilderImpl(RequestOverrideConfiguration requestOverrideConfiguration) { private BuilderImpl(AwsRequestOverrideConfiguration awsRequestOverrideConfig) { super(awsRequestOverrideConfig); this.awsCredentialsProvider = awsRequestOverrideConfig.credentialsProvider; + this.tokenIdentityProvider = awsRequestOverrideConfig.tokenIdentityProvider; } @Override @@ -170,6 +198,12 @@ public AwsCredentialsProvider credentialsProvider() { return CredentialUtils.toCredentialsProvider(awsCredentialsProvider); } + @Override + public Builder tokenIdentityProvider(IdentityProvider tokenIdentityProvider) { + this.tokenIdentityProvider = tokenIdentityProvider; + return this; + } + @Override public AwsRequestOverrideConfiguration build() { return new AwsRequestOverrideConfiguration(this); diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java index 63bbcaa6cdb0..b4a2b17bc048 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java @@ -69,7 +69,7 @@ private AwsExecutionContextBuilder() { */ public static ExecutionContext invokeInterceptorsAndCreateExecutionContext(ClientExecutionParams executionParams, - SdkClientConfiguration clientConfig) { + SdkClientConfiguration clientConfig) { // Note: This is currently copied to DefaultS3Presigner and other presigners. // Don't edit this without considering those @@ -134,13 +134,13 @@ private AwsExecutionContextBuilder() { putAuthSchemeResolutionAttributes(executionAttributes, clientConfig, originalRequest); ExecutionInterceptorChain executionInterceptorChain = - new ExecutionInterceptorChain(clientConfig.option(SdkClientOption.EXECUTION_INTERCEPTORS)); + new ExecutionInterceptorChain(clientConfig.option(SdkClientOption.EXECUTION_INTERCEPTORS)); InterceptorContext interceptorContext = InterceptorContext.builder() - .request(originalRequest) - .asyncRequestBody(executionParams.getAsyncRequestBody()) - .requestBody(executionParams.getRequestBody()) - .build(); + .request(originalRequest) + .asyncRequestBody(executionParams.getAsyncRequestBody()) + .requestBody(executionParams.getRequestBody()) + .build(); interceptorContext = runInitialInterceptors(interceptorContext, executionAttributes, executionInterceptorChain); SdkRequest modifiedRequests = interceptorContext.request(); @@ -217,9 +217,6 @@ private static void putAuthSchemeResolutionAttributes(ExecutionAttributes execut .putAttribute(SdkInternalExecutionAttribute.IDENTITY_PROVIDERS, identityProviders); } - // TODO(sra-identity-and-auth): This is hard coding the logic for the credentialsIdentityProvider from - // AwsRequestOverrideConfiguration. Currently, AwsRequestOverrideConfiguration does not support overriding the - // tokenIdentityProvider. When adding that support this method will need to be updated. private static IdentityProviders resolveIdentityProviders(SdkRequest originalRequest, SdkClientConfiguration clientConfig) { IdentityProviders identityProviders = @@ -232,13 +229,17 @@ private static IdentityProviders resolveIdentityProviders(SdkRequest originalReq return null; } - return originalRequest.overrideConfiguration() - .filter(c -> c instanceof AwsRequestOverrideConfiguration) - .map(c -> (AwsRequestOverrideConfiguration) c) - .flatMap(AwsRequestOverrideConfiguration::credentialsIdentityProvider) - .map(identityProvider -> - identityProviders.copy(b -> b.putIdentityProvider(identityProvider))) - .orElse(identityProviders); + return originalRequest + .overrideConfiguration() + .filter(c -> c instanceof AwsRequestOverrideConfiguration) + .map(c -> (AwsRequestOverrideConfiguration) c) + .map(c -> { + return identityProviders.copy(b -> { + c.credentialsIdentityProvider().ifPresent(b::putIdentityProvider); + c.tokenIdentityProvider().ifPresent(b::putIdentityProvider); + }); + }) + .orElse(identityProviders); } /** @@ -277,12 +278,13 @@ private static MetricCollector resolveMetricCollector(ClientExecutionParams tokenIdentityProvider = StaticTokenProvider.create(() -> "test-token"); + + AwsRequestOverrideConfiguration configuration1 = AwsRequestOverrideConfiguration + .builder().tokenIdentityProvider(tokenIdentityProvider).build(); + + assertThat(configuration1.tokenIdentityProvider().get().resolveIdentity().join().token()) + .isEqualTo(tokenIdentityProvider.resolveIdentity().join().token()); + } + private void assertCredentialsEqual(AwsCredentialsProvider credentialsProvider, IdentityProvider identityProvider) { AwsCredentials creds1 = credentialsProvider.resolveCredentials(); diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java index 4f2bea548c95..795e81500a13 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java @@ -36,6 +36,7 @@ import org.mockito.junit.MockitoJUnitRunner; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.auth.token.credentials.StaticTokenProvider; import software.amazon.awssdk.awscore.AwsRequest; import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; import software.amazon.awssdk.awscore.client.config.AwsClientOption; @@ -67,6 +68,7 @@ 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.identity.spi.TokenIdentity; import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.RegionScope; @@ -396,16 +398,24 @@ public void invokeInterceptorsAndCreateExecutionContext_withoutIdentityProviders public void invokeInterceptorsAndCreateExecutionContext_requestOverrideForIdentityProvider_updatesIdentityProviders() { IdentityProvider clientCredentialsProvider = StaticCredentialsProvider.create(AwsBasicCredentials.create("foo", "bar")); + IdentityProvider clientTokenProvider = StaticTokenProvider.create(() -> "client-token"); IdentityProviders identityProviders = - IdentityProviders.builder().putIdentityProvider(clientCredentialsProvider).build(); + IdentityProviders.builder() + .putIdentityProvider(clientCredentialsProvider) + .putIdentityProvider(clientTokenProvider) + .build(); SdkClientConfiguration clientConfig = testClientConfiguration() .option(SdkClientOption.IDENTITY_PROVIDERS, identityProviders) .build(); IdentityProvider requestCredentialsProvider = StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid")); + IdentityProvider requestTokenProvider = StaticTokenProvider.create(() -> "request-token"); Optional overrideConfiguration = - Optional.of(AwsRequestOverrideConfiguration.builder().credentialsProvider(requestCredentialsProvider).build()); + Optional.of(AwsRequestOverrideConfiguration.builder() + .credentialsProvider(requestCredentialsProvider) + .tokenIdentityProvider(requestTokenProvider) + .build()); when(sdkRequest.overrideConfiguration()).thenReturn(overrideConfiguration); ClientExecutionParams executionParams = clientExecutionParams(); @@ -420,6 +430,11 @@ public void invokeInterceptorsAndCreateExecutionContext_requestOverrideForIdenti actualIdentityProviders.identityProvider(AwsCredentialsIdentity.class); assertThat(actualIdentityProvider).isSameAs(requestCredentialsProvider); + + IdentityProvider actualTokenProvider = + actualIdentityProviders.identityProvider(TokenIdentity.class); + + assertThat(actualTokenProvider).isSameAs(requestTokenProvider); } private ClientExecutionParams clientExecutionParams() { diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkInternalExecutionAttribute.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkInternalExecutionAttribute.java index 08f890bd8333..f875836a1422 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkInternalExecutionAttribute.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkInternalExecutionAttribute.java @@ -189,6 +189,13 @@ public final class SdkInternalExecutionAttribute extends SdkExecutionAttribute { public static final ExecutionAttribute RESPONSE_CHECKSUM_VALIDATION = new ExecutionAttribute<>( "ResponseChecksumValidation"); + /** + * The token configured from the environment or system properties, used to determine if the BEARER_SERVICE_ENV_VARS + * business metric should be set. + */ + public static final ExecutionAttribute TOKEN_CONFIGURED_FROM_ENV = new ExecutionAttribute<>( + "TokenConfiguredFromEnv"); + /** * The backing attribute for RESOLVED_CHECKSUM_SPECS. * This holds the real ChecksumSpecs value, and is used to map to the ChecksumAlgorithm signer property diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java index 3779726894da..7f1483d56895 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java @@ -41,6 +41,7 @@ public enum BusinessMetricFeatureId { ACCOUNT_ID_MODE_REQUIRED("R"), RESOLVED_ACCOUNT_ID("T"), DDB_MAPPER("d"), + BEARER_SERVICE_ENV_VARS("3"), UNKNOWN("Unknown"); private static final Map VALUE_MAP = diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/customization.config b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/customization.config new file mode 100644 index 000000000000..86839537eeab --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/customization.config @@ -0,0 +1,4 @@ +{ + "skipEndpointTestGeneration": true, + "enableEnvironmentBearerToken": true +} diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/endpoint-rule-set.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/endpoint-rule-set.json new file mode 100644 index 000000000000..cc38f1ffb165 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/endpoint-rule-set.json @@ -0,0 +1,355 @@ +{ + "version": "1.3", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": true, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + }, + { + "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" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "{Region}", + "signingName": "environment-token" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://environment-token-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "{Region}", + "signingName": "environment-token" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://environment-token-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "{Region}", + "signingName": "environment-token" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://environment-token.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "{Region}", + "signingName": "environment-token" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "endpoint": { + "url": "https://environment-token.{Region}.{PartitionResult#dnsSuffix}", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingRegion": "{Region}", + "signingName": "environment-token" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/endpoint-tests.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/endpoint-tests.json new file mode 100644 index 000000000000..f94902ff9d99 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/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/environmenttokenprovider/service-2.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/service-2.json new file mode 100644 index 000000000000..c70811a87f80 --- /dev/null +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/environmenttokenprovider/service-2.json @@ -0,0 +1,38 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2016-03-11", + "endpointPrefix":"environment-token", + "auth":["aws.auth#sigv4", "smithy.api#httpBearerAuth"], + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"EnvironmentTokenProviderService", + "serviceFullName":"Environment Token Provider Service", + "serviceId":"EnvironmentTokenProviderService", + "signatureVersion":"v4", + "targetPrefix":"EnvironmentTokenProviderService", + "timestampFormat":"unixTimestamp", + "uid":"restjson-2016-03-11" + }, + "operations":{ + "OneOperation":{ + "name":"OneOperation", + "http":{ + "method":"POST", + "requestUri":"/2016-03-11/oneoperation" + }, + "input":{"shape":"OneShape"} + } + }, + "shapes": { + "OneShape": { + "type": "structure", + "members": { + "StringMember": { + "shape": "String" + } + } + }, + "String":{"type":"string"} + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/bearerauth/ClientBuilderTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/bearerauth/ClientBuilderTest.java index e9ff4da41fa1..371d089dea2b 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/bearerauth/ClientBuilderTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/bearerauth/ClientBuilderTest.java @@ -57,6 +57,9 @@ public void syncClient_customTokenIdentityProviderSet_presentInFinalConfig() { assertThat(config.option(AwsClientOption.TOKEN_IDENTITY_PROVIDER)) .isSameAs(mockProvider); + + assertThat(builder.buildClient().serviceClientConfiguration().tokenProvider()) + .isSameAs(mockProvider); } @Test diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/environmenttokenprovider/EnvironmentTokenProviderTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/environmenttokenprovider/EnvironmentTokenProviderTest.java new file mode 100644 index 000000000000..c16069a30dd5 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/environmenttokenprovider/EnvironmentTokenProviderTest.java @@ -0,0 +1,304 @@ +/* + * 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.environmenttokenprovider; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.auth.token.credentials.StaticTokenProvider; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; +import software.amazon.awssdk.http.HttpExecuteResponse; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.services.environmenttokenprovider.auth.scheme.EnvironmentTokenProviderAuthSchemeProvider; +import software.amazon.awssdk.services.environmenttokenprovider.model.OneOperationRequest; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; +import software.amazon.awssdk.testutils.service.http.MockAsyncHttpClient; +import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; + +public class EnvironmentTokenProviderTest { + private static final String ENV_NAME = "AWS_BEARER_TOKEN_ENVIRONMENT_TOKEN"; + private static final String SYSTEM_PROPERTY_NAME = "aws.bearerTokenEnvironmentToken"; + public static final String ENV_TOKEN = "env-test-token"; + public static final String SYSTEM_TEST_TOKEN = "system-test-token"; + + private MockSyncHttpClient mockHttpClient; + private MockAsyncHttpClient mockAsyncHttpClient; + private String systemPropertyBeforeTest; + + private final EnvironmentVariableHelper environmentVariableHelper = new EnvironmentVariableHelper(); + + @BeforeEach + void setUp() { + mockHttpClient = new MockSyncHttpClient(); + mockAsyncHttpClient = new MockAsyncHttpClient(); + systemPropertyBeforeTest = System.getProperty(SYSTEM_PROPERTY_NAME); + } + + @AfterEach + void tearDown() { + mockHttpClient.reset(); + mockAsyncHttpClient.reset(); + environmentVariableHelper.reset(); + if (systemPropertyBeforeTest != null) { + System.setProperty(SYSTEM_PROPERTY_NAME, systemPropertyBeforeTest); + } else { + System.clearProperty(SYSTEM_PROPERTY_NAME); + } + } + + @ParameterizedTest + @MethodSource("testCases") + void testAsyncClient(TestCase testCase) { + setupSystemAndEnv(testCase); + + mockAsyncHttpClient.stubNextResponse(mockResponse()); + + EnvironmentTokenProviderAsyncClientBuilder clientBuilder = EnvironmentTokenProviderAsyncClient + .builder() + .httpClient(mockAsyncHttpClient); + + if (testCase.authSchemeProvider != null) { + clientBuilder.authSchemeProvider(testCase.authSchemeProvider); + } + + EnvironmentTokenProviderAsyncClient client = clientBuilder.build(); + + if (testCase.operationToken == null) { + client.oneOperation(b -> {} ).join(); + } else { + client.oneOperation(requestWithOperationToken(testCase)).join(); + } + + SdkHttpFullRequest loggedRequest = (SdkHttpFullRequest) mockAsyncHttpClient.getLastRequest(); + + verifyRequest(testCase, loggedRequest); + } + + @ParameterizedTest + @MethodSource("testCases") + void testSyncClient(TestCase testCase) { + setupSystemAndEnv(testCase); + + mockHttpClient.stubNextResponse(mockResponse()); + + EnvironmentTokenProviderClientBuilder clientBuilder = EnvironmentTokenProviderClient + .builder() + .httpClient(mockHttpClient); + + if (testCase.authSchemeProvider != null) { + clientBuilder.authSchemeProvider(testCase.authSchemeProvider); + } + + EnvironmentTokenProviderClient client = clientBuilder.build(); + + if (testCase.operationToken == null) { + client.oneOperation(b -> {} ); + } else { + client.oneOperation(requestWithOperationToken(testCase)); + } + + + SdkHttpFullRequest loggedRequest = (SdkHttpFullRequest) mockHttpClient.getLastRequest(); + + verifyRequest(testCase, loggedRequest); + } + + private static void verifyRequest(TestCase testCase, SdkHttpFullRequest loggedRequest) { + if (testCase.expectBearerAuth) { + assertThat(loggedRequest.firstMatchingHeader("Authorization").get()) + .startsWith("Bearer"); + } else { + assertThat(loggedRequest.firstMatchingHeader("Authorization") + .get()).startsWith("AWS4-HMAC-SHA256"); + } + + if (testCase.expectBusinessMetricSet) { + assertThat(loggedRequest.firstMatchingHeader("User-Agent").get()) + .matches(".*m\\/[A-Za-z0-9,]+" + BusinessMetricFeatureId.BEARER_SERVICE_ENV_VARS); + } else { + assertThat(loggedRequest.firstMatchingHeader("User-Agent").get()) + .doesNotMatch(".*m\\/[A-Za-z0-9,]+" + BusinessMetricFeatureId.BEARER_SERVICE_ENV_VARS); + } + } + + static Stream testCases() { + return Stream.of( + TestCase.builder() + .description("Does not use bearer auth when ENV token is unset") + .expectBearerAuth(false) + .build(), + + TestCase.builder() + .description("Uses bearer auth when ENV token is set") + .envVar(ENV_NAME, ENV_TOKEN) + .expectBearerAuth(true) + .expectedBearerToken(ENV_TOKEN) + .expectBusinessMetricSet(true) + .build(), + + TestCase.builder() + .description("Uses bearer auth when system property token is set") + .envVar(ENV_NAME, "some-other-token") + .systemProperty(SYSTEM_TEST_TOKEN) + .expectBearerAuth(true) + .expectedBearerToken(SYSTEM_TEST_TOKEN) + .expectBusinessMetricSet(true) + .build(), + + TestCase.builder() + .description("Uses bearer auth from environment over auth scheme preference") + .envVar(ENV_NAME, ENV_TOKEN) + .envVar( + SdkSystemSetting.AWS_AUTH_SCHEME_PREFERENCE.environmentVariable(), + "sigv4") + .expectBearerAuth(true) + .expectedBearerToken(ENV_TOKEN) + .expectBusinessMetricSet(true) + .build(), + + TestCase.builder() + .description("Doesn't use bearer when AuthSchemeProvider is manually configured on the client") + .envVar(ENV_NAME, ENV_TOKEN) + .authSchemeProvider(EnvironmentTokenProviderAuthSchemeProvider.defaultProvider()) + .expectBearerAuth(false) + .expectBusinessMetricSet(false) + .build(), + + TestCase.builder() + .description("Business metric is not set when the token is overridden on the operation") + .envVar(ENV_NAME, ENV_TOKEN) + .operationToken("operation-token") + .expectBearerAuth(true) + .expectedBearerToken("operation-token") + .expectBusinessMetricSet(false) + .build() + ); + } + + private static OneOperationRequest requestWithOperationToken(TestCase testCase) { + return OneOperationRequest.builder() + .overrideConfiguration(c -> c.tokenIdentityProvider( + StaticTokenProvider.create(() -> testCase.operationToken))) + .build(); + } + + private void setupSystemAndEnv(TestCase testCase) { + testCase.envVars.forEach(environmentVariableHelper::set); + if (testCase.systemProperty != null) { + System.setProperty(SYSTEM_PROPERTY_NAME, testCase.systemProperty); + } + } + + private HttpExecuteResponse mockResponse() { + return HttpExecuteResponse.builder() + .response(SdkHttpResponse.builder().statusCode(200).build()) + .build(); + } + + static final class TestCase { + final String description; + final Map envVars; + final String systemProperty; + final EnvironmentTokenProviderAuthSchemeProvider authSchemeProvider; + final String operationToken; + final boolean expectBearerAuth; + final String expectedBearerToken; + final boolean expectBusinessMetricSet; + + private TestCase(Builder builder) { + this.description = builder.description; + this.envVars = builder.envVars; + this.systemProperty = builder.systemProperty; + this.authSchemeProvider = builder.authSchemeProvider; + this.operationToken = builder.operationToken; + this.expectBearerAuth = builder.expectBearerAuth; + this.expectedBearerToken = builder.expectedBearerToken; + this.expectBusinessMetricSet = builder.expectBusinessMetricSet; + } + + @Override + public String toString() { + return description; + } + + static Builder builder() { + return new Builder(); + } + + static class Builder { + private String description; + private Map envVars = new HashMap<>(); + private String systemProperty; + private EnvironmentTokenProviderAuthSchemeProvider authSchemeProvider; + private String operationToken; + private boolean expectBearerAuth; + private String expectedBearerToken; + private boolean expectBusinessMetricSet; + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder envVar(String key, String value) { + this.envVars.put(key, value); + return this; + } + + public Builder systemProperty(String systemProperty) { + this.systemProperty = systemProperty; + return this; + } + + public Builder authSchemeProvider(EnvironmentTokenProviderAuthSchemeProvider authSchemeProvider) { + this.authSchemeProvider = authSchemeProvider; + return this; + } + + public Builder operationToken(String operationToken) { + this.operationToken = operationToken; + return this; + } + + public Builder expectBearerAuth(boolean expectBearerAuth) { + this.expectBearerAuth = expectBearerAuth; + return this; + } + + public Builder expectedBearerToken(String expectedBearerToken) { + this.expectedBearerToken = expectedBearerToken; + return this; + } + + public Builder expectBusinessMetricSet(boolean expectBusinessMetricSet) { + this.expectBusinessMetricSet = expectBusinessMetricSet; + return this; + } + + public TestCase build() { + return new TestCase(this); + } + } + } +}