Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sigv4aRegionSet to Client Builder for Multi-Auth Services Supporting Sigv4a #5772

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
import software.amazon.awssdk.identity.spi.IdentityProvider;
import software.amazon.awssdk.identity.spi.IdentityProviders;
Expand Down Expand Up @@ -191,6 +192,10 @@ public TypeSpec poetSpec() {

builder.addMethod(validateClientOptionsMethod());

if (authSchemeSpecUtils.usesSigV4a()) {
builder.addMethod(sigv4aRegionSetMethod());
}

return builder.build();
}

Expand Down Expand Up @@ -721,6 +726,18 @@ private MethodSpec authSchemeProviderMethod() {
.build();
}

private MethodSpec sigv4aRegionSetMethod() {
return MethodSpec.methodBuilder("sigv4aRegionSet")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be regionSet? I guess we can finalize it in API surface area review

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure , I have this discussion during Surface API review

.addModifiers(Modifier.PUBLIC)
.returns(TypeVariableName.get("B"))
.addParameter(RegionSet.class, "sigv4aRegionSet")
.addStatement("clientConfiguration.option($T.AWS_SIGV4A_SIGNING_REGION_SET, sigv4aRegionSet == null ? "
+ "$T.emptySet() : sigv4aRegionSet.asSet())",
AwsClientOption.class, Collections.class)
.addStatement("return thisBuilder()")
.build();
}

private MethodSpec defaultAuthSchemeProviderMethod() {
return MethodSpec.methodBuilder("defaultAuthSchemeProvider")
.addModifiers(PRIVATE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
import software.amazon.awssdk.codegen.poet.rules.EndpointParamsKnowledgeIndex;
import software.amazon.awssdk.codegen.poet.rules.EndpointRulesSpecUtils;
import software.amazon.awssdk.codegen.utils.AuthUtils;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.identity.spi.IdentityProvider;
import software.amazon.awssdk.identity.spi.TokenIdentity;
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;
Expand Down Expand Up @@ -106,6 +108,10 @@ public TypeSpec poetSpec() {
builder.addMethod(tokenIdentityProviderMethod());
}

if (AuthUtils.usesSigv4aAuth(model)) {
builder.addMethod(sigv4aRegionSetMethod());
}

return builder.build();
}

Expand Down Expand Up @@ -255,4 +261,22 @@ private boolean hasSdkClientContextParams() {
&& model.getCustomizationConfig().getCustomClientContextParams() != null
&& !model.getCustomizationConfig().getCustomClientContextParams().isEmpty();
}

private MethodSpec sigv4aRegionSetMethod() {
return MethodSpec.methodBuilder("sigv4aRegionSet")
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addParameter(RegionSet.class, "sigv4aRegionSet")
.addJavadoc("Sets the {@link $T} to be used for operations using Sigv4a signing requests.\n" +
"This is optional; if not provided, the following precedence is used:\n" +
"<ol>\n" +
" <li>{@link $T#AWS_SIGV4A_SIGNING_REGION_SET}.</li>\n" +
" <li>as <code>sigv4a_signing_region_set</code> in the configuration file.</li>\n" +
" <li>The region configured for the client.</li>\n" +
"</ol>\n",
RegionSet.class,
SdkSystemSetting.class)
.returns(TypeVariableName.get("B"))
.addStatement("throw new $T()", UnsupportedOperationException.class)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static software.amazon.awssdk.codegen.poet.ClientTestModels.composedClientJsonServiceModels;
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;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.queryServiceModels;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.queryServiceModelsEndpointAuthParamsWithAllowList;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.restJsonServiceModels;
Expand Down Expand Up @@ -89,6 +90,11 @@ void baseClientBuilderClassWithNoAuthOperation_sra() {
validateBaseClientBuilderClassGeneration(operationWithNoAuth(), "test-no-auth-ops-client-builder-class.java", true);
}

@Test
void baseClientBuilderClassWithMultiAuthSigv4a() {
validateBaseClientBuilderClassGeneration(opsWithSigv4a(), "test-multi-auth-sigv4a-client-builder-class.java", true);
}

@Test
void baseClientBuilderClassWithNoAuthService_sra() {
validateBaseClientBuilderClassGeneration(serviceWithNoAuth(), "test-no-auth-service-client-builder-class.java", true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.opsWithSigv4a;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.queryServiceModels;
import static software.amazon.awssdk.codegen.poet.ClientTestModels.restJsonServiceModels;
import static software.amazon.awssdk.codegen.poet.builder.BuilderClassTestUtils.validateGeneration;
Expand Down Expand Up @@ -49,6 +50,11 @@ public void syncHasCrossRegionAccessEnabledPropertyBuilderClass() {
"test-customcontextparams-sync-client-builder-class.java");
}

@Test
void baseClientBuilderInterfaceWithMultiAuth() {
validateBaseClientBuilderInterfaceGeneration(opsWithSigv4a(), "test-multi-auth-sigv4a-client-builder-interface.java");
}

private void validateBaseClientBuilderInterfaceGeneration(IntermediateModel model, String expectedClassName) {
validateGeneration(BaseClientBuilderInterface::new, model, expectedClassName);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package software.amazon.awssdk.services.database;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import software.amazon.awssdk.annotations.Generated;
import software.amazon.awssdk.annotations.SdkInternalApi;
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.ExecutionInterceptor;
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme;
import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
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.regions.ServiceMetadataAdvancedOption;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.database.auth.scheme.DatabaseAuthSchemeProvider;
import software.amazon.awssdk.services.database.auth.scheme.internal.DatabaseAuthSchemeInterceptor;
import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointProvider;
import software.amazon.awssdk.services.database.endpoints.internal.DatabaseRequestSetEndpointInterceptor;
import software.amazon.awssdk.services.database.endpoints.internal.DatabaseResolveEndpointInterceptor;
import software.amazon.awssdk.services.database.internal.DatabaseServiceClientConfigurationBuilder;
import software.amazon.awssdk.utils.CollectionUtils;

/**
* Internal base class for {@link DefaultDatabaseClientBuilder} and {@link DefaultDatabaseAsyncClientBuilder}.
*/
@Generated("software.amazon.awssdk:codegen")
@SdkInternalApi
abstract class DefaultDatabaseBaseClientBuilder<B extends DatabaseBaseClientBuilder<B, C>, C> extends
AwsDefaultClientBuilder<B, C> {
private final Map<String, AuthScheme<?>> additionalAuthSchemes = new HashMap<>();

@Override
protected final String serviceEndpointPrefix() {
return "database-service-endpoint";
}

@Override
protected final String serviceName() {
return "Database";
}

@Override
protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) {
return config.merge(c -> c.option(SdkClientOption.ENDPOINT_PROVIDER, defaultEndpointProvider())
.option(SdkClientOption.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider())
.option(SdkClientOption.AUTH_SCHEMES, authSchemes())
.option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false));
}

@Override
protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) {
List<ExecutionInterceptor> endpointInterceptors = new ArrayList<>();
endpointInterceptors.add(new DatabaseAuthSchemeInterceptor());
endpointInterceptors.add(new DatabaseResolveEndpointInterceptor());
endpointInterceptors.add(new DatabaseRequestSetEndpointInterceptor());
ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory();
List<ExecutionInterceptor> interceptors = interceptorFactory
.getInterceptors("software/amazon/awssdk/services/database/execution.interceptors");
List<ExecutionInterceptor> 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<?> credentialsIdentityProvider = c.get(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER);
if (credentialsIdentityProvider != null) {
result.putIdentityProvider(credentialsIdentityProvider);
}
return result.build();
});
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());
return builder.build();
}

@Override
protected final String signingName() {
return "database-service";
}

private DatabaseEndpointProvider defaultEndpointProvider() {
return DatabaseEndpointProvider.defaultProvider();
}

public B authSchemeProvider(DatabaseAuthSchemeProvider authSchemeProvider) {
clientConfiguration.option(SdkClientOption.AUTH_SCHEME_PROVIDER, authSchemeProvider);
return thisBuilder();
}

private DatabaseAuthSchemeProvider defaultAuthSchemeProvider() {
return DatabaseAuthSchemeProvider.defaultProvider();
}

@Override
public B putAuthScheme(AuthScheme<?> authScheme) {
additionalAuthSchemes.put(authScheme.schemeId(), authScheme);
return thisBuilder();
}

private Map<String, AuthScheme<?>> authSchemes() {
Map<String, AuthScheme<?>> schemes = new HashMap<>(3 + this.additionalAuthSchemes.size());
AwsV4AuthScheme awsV4AuthScheme = AwsV4AuthScheme.create();
schemes.put(awsV4AuthScheme.schemeId(), awsV4AuthScheme);
AwsV4aAuthScheme awsV4aAuthScheme = AwsV4aAuthScheme.create();
schemes.put(awsV4aAuthScheme.schemeId(), awsV4aAuthScheme);
NoAuthAuthScheme noAuthAuthScheme = NoAuthAuthScheme.create();
schemes.put(noAuthAuthScheme.schemeId(), noAuthAuthScheme);
schemes.putAll(this.additionalAuthSchemes);
return schemes;
}

@Override
protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) {
List<SdkPlugin> internalPlugins = internalPlugins(config);
List<SdkPlugin> externalPlugins = plugins();
if (internalPlugins.isEmpty() && externalPlugins.isEmpty()) {
return config;
}
List<SdkPlugin> plugins = CollectionUtils.mergeLists(internalPlugins, externalPlugins);
SdkClientConfiguration.Builder configuration = config.toBuilder();
DatabaseServiceClientConfigurationBuilder serviceConfigBuilder = new DatabaseServiceClientConfigurationBuilder(
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<RetryStrategy.Builder<?, ?>> 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<SdkPlugin> internalPlugins(SdkClientConfiguration config) {
return Collections.emptyList();
}

protected static void validateClientOptions(SdkClientConfiguration c) {
}

public B sigv4aRegionSet(RegionSet sigv4aRegionSet) {
clientConfiguration.option(AwsClientOption.AWS_SIGV4A_SIGNING_REGION_SET,
sigv4aRegionSet == null ? Collections.emptySet() : sigv4aRegionSet.asSet());
return thisBuilder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package software.amazon.awssdk.services.database;

import software.amazon.awssdk.annotations.Generated;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.services.database.auth.scheme.DatabaseAuthSchemeProvider;
import software.amazon.awssdk.services.database.endpoints.DatabaseEndpointProvider;

/**
* This includes configuration specific to Database Service that is supported by both {@link DatabaseClientBuilder} and
* {@link DatabaseAsyncClientBuilder}.
*/
@Generated("software.amazon.awssdk:codegen")
@SdkPublicApi
public interface DatabaseBaseClientBuilder<B extends DatabaseBaseClientBuilder<B, C>, C> extends AwsClientBuilder<B, C> {
/**
* Set the {@link DatabaseEndpointProvider} implementation that will be used by the client to determine the endpoint
* for each request. This is optional; if none is provided a default implementation will be used the SDK.
*/
default B endpointProvider(DatabaseEndpointProvider endpointProvider) {
throw new UnsupportedOperationException();
}

/**
* Set the {@link DatabaseAuthSchemeProvider} implementation that will be used by the client to resolve the auth
* scheme for each request. This is optional; if none is provided a default implementation will be used the SDK.
*/
default B authSchemeProvider(DatabaseAuthSchemeProvider authSchemeProvider) {
throw new UnsupportedOperationException();
}

/**
* Sets the {@link RegionSet} to be used for operations using Sigv4a signing requests. This is optional; if not
* provided, the following precedence is used:
* <ol>
* <li>{@link software.amazon.awssdk.core.SdkSystemSetting#AWS_SIGV4A_SIGNING_REGION_SET}.</li>
* <li>as <code>sigv4a_signing_region_set</code> in the configuration file.</li>
* <li>The region configured for the client.</li>
* </ol>
*/
default B sigv4aRegionSet(RegionSet sigv4aRegionSet) {
throw new UnsupportedOperationException();
}
}
Loading
Loading