Skip to content

Commit 11aa9e9

Browse files
RanVakninalextwoodsL-Applin
authored
Adding functionality to config preferred authschemeProvider (#6083)
* Adding functionality to config preferred authschemeProvider * adding test coverage * fix formatting checkstyle * Added changelog * Adding test coverage * Use SdkSystemSetting for both env and system * Add profiles to service pom * Fix test * Fix protocol test dependencies * Remove dependency on profiles (use option get in generated code instead) * Fix checkstyle * Move AuthSchemePreferenceProvider out of internal module * More checkstyle fixes * Fixing comments, adding fixture file, renaming provider to resolver, adding coverage * Remove unused import * Add codegen test for preferred auth scheme provider * Check for empty string * Add more documentation * Revert "Add codegen test for preferred auth scheme provider" This reverts commit 141b9d6. * Replace authprovider builder with overridden defaultProvider method * Update core/aws-core/src/main/java/software/amazon/awssdk/awscore/auth/AuthSchemePreferenceResolver.java Co-authored-by: Olivier L Applin <olapplin@amazon.com> * Update core/aws-core/src/main/java/software/amazon/awssdk/awscore/auth/AuthSchemePreferenceResolver.java Co-authored-by: Olivier L Applin <olapplin@amazon.com> * Fix import from suggested changes * move test to aws-core module --------- Co-authored-by: Ran Vaknin <RanVaknin@users.noreply.github.com> Co-authored-by: Alex Woods <alexwoo@amazon.com> Co-authored-by: Olivier L Applin <olapplin@amazon.com>
1 parent 6f60011 commit 11aa9e9

File tree

27 files changed

+1158
-247
lines changed

27 files changed

+1158
-247
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Added ability to configure preferred authentication schemes when multiple auth options are available."
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/AuthSchemeGeneratorTasks.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import software.amazon.awssdk.codegen.poet.auth.scheme.EndpointAwareAuthSchemeParamsSpec;
2929
import software.amazon.awssdk.codegen.poet.auth.scheme.EndpointBasedAuthSchemeProviderSpec;
3030
import software.amazon.awssdk.codegen.poet.auth.scheme.ModelBasedAuthSchemeProviderSpec;
31+
import software.amazon.awssdk.codegen.poet.auth.scheme.PreferredAuthSchemeProviderSpec;
3132

3233
public final class AuthSchemeGeneratorTasks extends BaseGeneratorTasks {
3334
private final GeneratorTaskParams generatorTaskParams;
@@ -45,6 +46,7 @@ protected List<GeneratorTask> createTasks() {
4546
tasks.add(generateProviderInterface());
4647
tasks.add(generateDefaultParamsImpl());
4748
tasks.add(generateModelBasedProvider());
49+
tasks.add(generatePreferenceProvider());
4850
tasks.add(generateAuthSchemeInterceptor());
4951
if (authSchemeSpecUtils.useEndpointBasedAuthProvider()) {
5052
tasks.add(generateEndpointBasedProvider());
@@ -69,6 +71,10 @@ private GeneratorTask generateModelBasedProvider() {
6971
return new PoetGeneratorTask(authSchemeInternalDir(), model.getFileHeader(), new ModelBasedAuthSchemeProviderSpec(model));
7072
}
7173

74+
private GeneratorTask generatePreferenceProvider() {
75+
return new PoetGeneratorTask(authSchemeInternalDir(), model.getFileHeader(), new PreferredAuthSchemeProviderSpec(model));
76+
}
77+
7278
private GeneratorTask generateEndpointBasedProvider() {
7379
return new PoetGeneratorTask(authSchemeInternalDir(), model.getFileHeader(),
7480
new EndpointBasedAuthSchemeProviderSpec(model));

codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeProviderSpec.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.squareup.javapoet.ParameterizedTypeName;
2222
import com.squareup.javapoet.TypeName;
2323
import com.squareup.javapoet.TypeSpec;
24+
import java.util.List;
2425
import java.util.function.Consumer;
2526
import javax.lang.model.element.Modifier;
2627
import software.amazon.awssdk.annotations.SdkPublicApi;
@@ -54,6 +55,7 @@ public TypeSpec poetSpec() {
5455
.addMethod(resolveAuthSchemeMethod())
5556
.addMethod(resolveAuthSchemeConsumerBuilderMethod())
5657
.addMethod(defaultProviderMethod())
58+
.addMethod(defaultPreferredProviderMethod())
5759
.build();
5860
}
5961

@@ -93,6 +95,17 @@ private MethodSpec defaultProviderMethod() {
9395
.build();
9496
}
9597

98+
private MethodSpec defaultPreferredProviderMethod() {
99+
return MethodSpec.methodBuilder("defaultProvider")
100+
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
101+
.addParameter(ParameterizedTypeName.get(List.class, String.class), "authSchemePreference")
102+
.returns(className())
103+
.addJavadoc("Get the default auth scheme provider the preferred auth schemes in order of preference.")
104+
.addStatement("return new $T(defaultProvider(), authSchemePreference)",
105+
authSchemeSpecUtils.preferredAuthSchemeProviderName())
106+
.build();
107+
}
108+
96109
private CodeBlock interfaceJavadoc() {
97110
CodeBlock.Builder b = CodeBlock.builder();
98111

@@ -105,3 +118,4 @@ private CodeBlock interfaceJavadoc() {
105118
return b.build();
106119
}
107120
}
121+

codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeSpecUtils.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ public ClassName modeledAuthSchemeProviderName() {
9797
return ClassName.get(internalPackage(), "Modeled" + providerInterfaceName().simpleName());
9898
}
9999

100+
public ClassName preferredAuthSchemeProviderName() {
101+
return ClassName.get(internalPackage(), "Preferred" + providerInterfaceName().simpleName());
102+
}
103+
104+
public ClassName authSchemeProviderBuilderName() {
105+
return ClassName.get(basePackage(),
106+
intermediateModel.getMetadata().getServiceName() + "AuthSchemeProviderBuilder");
107+
}
108+
100109
public ClassName authSchemeInterceptor() {
101110
return ClassName.get(internalPackage(), intermediateModel.getMetadata().getServiceName() + "AuthSchemeInterceptor");
102111
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.poet.auth.scheme;
17+
18+
import com.squareup.javapoet.ClassName;
19+
import com.squareup.javapoet.MethodSpec;
20+
import com.squareup.javapoet.ParameterizedTypeName;
21+
import com.squareup.javapoet.TypeSpec;
22+
import java.util.ArrayList;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import javax.lang.model.element.Modifier;
26+
import software.amazon.awssdk.annotations.SdkInternalApi;
27+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
28+
import software.amazon.awssdk.codegen.poet.ClassSpec;
29+
import software.amazon.awssdk.codegen.poet.PoetUtils;
30+
import software.amazon.awssdk.utils.CollectionUtils;
31+
32+
public class PreferredAuthSchemeProviderSpec implements ClassSpec {
33+
private final AuthSchemeSpecUtils authSchemeSpecUtils;
34+
35+
public PreferredAuthSchemeProviderSpec(IntermediateModel intermediateModel) {
36+
this.authSchemeSpecUtils = new AuthSchemeSpecUtils(intermediateModel);
37+
}
38+
39+
@Override
40+
public ClassName className() {
41+
return authSchemeSpecUtils.preferredAuthSchemeProviderName();
42+
}
43+
44+
@Override
45+
public TypeSpec poetSpec() {
46+
return PoetUtils.createClassBuilder(className())
47+
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
48+
.addAnnotation(SdkInternalApi.class)
49+
.addField(
50+
authSchemeSpecUtils.providerInterfaceName(), "delegate",
51+
Modifier.PRIVATE, Modifier.FINAL)
52+
.addField(
53+
ParameterizedTypeName.get(List.class, String.class), "authSchemePreference",
54+
Modifier.PRIVATE, Modifier.FINAL)
55+
.addSuperinterface(authSchemeSpecUtils.providerInterfaceName())
56+
.addMethod(constructor())
57+
.addMethod(resolveAuthSchemeMethod())
58+
.build();
59+
}
60+
61+
private MethodSpec constructor() {
62+
return MethodSpec
63+
.constructorBuilder()
64+
.addModifiers(Modifier.PUBLIC)
65+
.addParameter(authSchemeSpecUtils.providerInterfaceName(), "delegate")
66+
.addParameter(ParameterizedTypeName.get(List.class, String.class), "authSchemePreference")
67+
.addStatement("this.delegate = delegate")
68+
.addStatement("this.authSchemePreference = authSchemePreference != null ? authSchemePreference "
69+
+ ": $T.emptyList()",
70+
Collections.class)
71+
.build();
72+
}
73+
74+
private MethodSpec resolveAuthSchemeMethod() {
75+
MethodSpec.Builder b = MethodSpec.methodBuilder("resolveAuthScheme")
76+
.addModifiers(Modifier.PUBLIC)
77+
.addAnnotation(Override.class)
78+
.returns(authSchemeSpecUtils.resolverReturnType())
79+
.addParameter(authSchemeSpecUtils.parametersInterfaceName(), "params");
80+
b.addJavadoc("Resolve the auth schemes based on the given set of parameters.");
81+
b.addStatement("$T candidateAuthSchemes = delegate.resolveAuthScheme(params)",
82+
authSchemeSpecUtils.resolverReturnType());
83+
b.beginControlFlow("if ($T.isNullOrEmpty(authSchemePreference))", CollectionUtils.class)
84+
.addStatement("return candidateAuthSchemes")
85+
.endControlFlow();
86+
87+
b.addStatement("$T authSchemes = new $T<>()", authSchemeSpecUtils.resolverReturnType(), ArrayList.class);
88+
89+
b.beginControlFlow("authSchemePreference.forEach(preferredSchemeId -> ");
90+
91+
b.beginControlFlow("candidateAuthSchemes.stream().filter(candidate -> ");
92+
b.addStatement("String candidateSchemeName = candidate.schemeId().contains(\"#\") ? " +
93+
"candidate.schemeId().split(\"#\")[1] : candidate.schemeId()");
94+
b.addStatement("return candidateSchemeName.equals(preferredSchemeId)");
95+
b.endControlFlow(").findFirst().ifPresent(authSchemes::add)");
96+
b.endControlFlow(")");
97+
98+
b.beginControlFlow("candidateAuthSchemes.forEach(candidate -> ")
99+
.beginControlFlow("if (!authSchemes.contains(candidate))")
100+
.addStatement("authSchemes.add(candidate)")
101+
.endControlFlow()
102+
.endControlFlow(")");
103+
104+
b.addStatement("return authSchemes");
105+
return b.build();
106+
}
107+
108+
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import software.amazon.awssdk.auth.signer.Aws4Signer;
4444
import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider;
4545
import software.amazon.awssdk.auth.token.signer.aws.BearerTokenSigner;
46+
import software.amazon.awssdk.awscore.auth.AuthSchemePreferenceResolver;
4647
import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder;
4748
import software.amazon.awssdk.awscore.client.config.AwsClientOption;
4849
import software.amazon.awssdk.awscore.endpoint.AwsClientEndpointProvider;
@@ -277,7 +278,7 @@ private MethodSpec mergeServiceDefaultsMethod() {
277278
builder.addCode(".option($T.ENDPOINT_PROVIDER, defaultEndpointProvider())", SdkClientOption.class);
278279

279280
if (authSchemeSpecUtils.useSraAuth()) {
280-
builder.addCode(".option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider())", SdkClientOption.class);
281+
builder.addCode(".option($T.AUTH_SCHEME_PROVIDER, defaultAuthSchemeProvider(config))", SdkClientOption.class);
281282
builder.addCode(".option($T.AUTH_SCHEMES, authSchemes())", SdkClientOption.class);
282283
} else {
283284
if (defaultAwsAuthSignerMethod().isPresent()) {
@@ -442,7 +443,7 @@ private MethodSpec finalizeServiceConfigurationMethod() {
442443
// serviceConfigBuilder; the service configuration classes (e.g. S3Configuration) return primitive booleans that
443444
// have a default when not present.
444445
builder.addStatement("builder.option($T.DUALSTACK_ENDPOINT_ENABLED, serviceConfigBuilder.dualstackEnabled())",
445-
AwsClientOption.class);
446+
AwsClientOption.class);
446447
}
447448

448449
if (model.getCustomizationConfig().getServiceConfig().hasFipsProperty()) {
@@ -452,14 +453,14 @@ private MethodSpec finalizeServiceConfigurationMethod() {
452453

453454
if (model.getEndpointOperation().isPresent()) {
454455
builder.addStatement("builder.option($T.ENDPOINT_DISCOVERY_ENABLED, endpointDiscoveryEnabled)\n",
455-
SdkClientOption.class);
456+
SdkClientOption.class);
456457
}
457458

458459

459460
if (StringUtils.isNotBlank(model.getCustomizationConfig().getCustomRetryStrategy())) {
460461
builder.addStatement("builder.option($1T.RETRY_STRATEGY, $2T.resolveRetryStrategy(config))",
461-
SdkClientOption.class,
462-
PoetUtils.classNameFromFqcn(model.getCustomizationConfig().getCustomRetryStrategy()));
462+
SdkClientOption.class,
463+
PoetUtils.classNameFromFqcn(model.getCustomizationConfig().getCustomRetryStrategy()));
463464
}
464465

465466
if (StringUtils.isNotBlank(model.getCustomizationConfig().getCustomRetryPolicy())) {
@@ -485,7 +486,7 @@ private MethodSpec finalizeServiceConfigurationMethod() {
485486

486487
if (endpointParamsKnowledgeIndex.hasAccountIdEndpointModeBuiltIn()) {
487488
builder.addStatement("builder.option($T.$L, resolveAccountIdEndpointMode(config))",
488-
AwsClientOption.class, model.getNamingStrategy().getEnumValueName("accountIdEndpointMode"));
489+
AwsClientOption.class, model.getNamingStrategy().getEnumValueName("accountIdEndpointMode"));
489490
}
490491

491492
String serviceNameForEnvVar = model.getNamingStrategy().getServiceNameForEnvironmentVariables();
@@ -829,7 +830,19 @@ private MethodSpec sigv4aSigningRegionSetMethod() {
829830
private MethodSpec defaultAuthSchemeProviderMethod() {
830831
return MethodSpec.methodBuilder("defaultAuthSchemeProvider")
831832
.addModifiers(PRIVATE)
833+
.addParameter(SdkClientConfiguration.class, "config")
832834
.returns(authSchemeSpecUtils.providerInterfaceName())
835+
.addCode("$T authSchemePreferenceProvider = "
836+
+ "$T.builder()",
837+
AuthSchemePreferenceResolver.class, AuthSchemePreferenceResolver.class)
838+
.addCode(".profileFile(config.option($T.PROFILE_FILE_SUPPLIER))", SdkClientOption.class)
839+
.addCode(".profileName(config.option($T.PROFILE_NAME))", SdkClientOption.class)
840+
.addStatement(".build()")
841+
.addStatement("List<String> preferences = authSchemePreferenceProvider.resolveAuthSchemePreference()")
842+
.beginControlFlow("if(!preferences.isEmpty())")
843+
.addStatement("return $T.defaultProvider(preferences)",
844+
authSchemeSpecUtils.providerInterfaceName())
845+
.endControlFlow()
833846
.addStatement("return $T.defaultProvider()", authSchemeSpecUtils.providerInterfaceName())
834847
.build();
835848
}
@@ -965,10 +978,10 @@ private MethodSpec internalPluginsMethod() {
965978
List<String> internalPlugins = model.getCustomizationConfig().getInternalPlugins();
966979
if (internalPlugins.isEmpty()) {
967980
return builder.addStatement("return $T.emptyList()", Collections.class)
968-
.build();
981+
.build();
969982
}
970983

971-
builder.addStatement("$T internalPlugins = new $T<>()", parameterizedTypeName, ArrayList.class);
984+
builder.addStatement("$T internalPlugins = new $T<>()", parameterizedTypeName, ArrayList.class);
972985

973986
for (String internalPlugin : internalPlugins) {
974987
String arguments = internalPluginNewArguments(internalPlugin);

codegen/src/test/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeSpecTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ static List<TestCase> parameters() {
6666
.caseName("query")
6767
.outputFileSuffix("default-params")
6868
.build(),
69+
TestCase.builder()
70+
.modelProvider(ClientTestModels::queryServiceModels)
71+
.classSpecProvider(PreferredAuthSchemeProviderSpec::new)
72+
.caseName("query")
73+
.outputFileSuffix("preferred-provider")
74+
.build(),
6975
// query-endpoint-auth-params
7076
TestCase.builder()
7177
.modelProvider(ClientTestModels::queryServiceModelsEndpointAuthParamsWithAllowList)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package software.amazon.awssdk.services.query.auth.scheme.internal;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import software.amazon.awssdk.annotations.Generated;
7+
import software.amazon.awssdk.annotations.SdkInternalApi;
8+
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
9+
import software.amazon.awssdk.services.query.auth.scheme.QueryAuthSchemeParams;
10+
import software.amazon.awssdk.services.query.auth.scheme.QueryAuthSchemeProvider;
11+
import software.amazon.awssdk.utils.CollectionUtils;
12+
13+
@Generated("software.amazon.awssdk:codegen")
14+
@SdkInternalApi
15+
public final class PreferredQueryAuthSchemeProvider implements QueryAuthSchemeProvider {
16+
private final QueryAuthSchemeProvider delegate;
17+
18+
private final List<String> authSchemePreference;
19+
20+
public PreferredQueryAuthSchemeProvider(QueryAuthSchemeProvider delegate, List<String> authSchemePreference) {
21+
this.delegate = delegate;
22+
this.authSchemePreference = authSchemePreference != null ? authSchemePreference : Collections.emptyList();
23+
}
24+
25+
/**
26+
* Resolve the auth schemes based on the given set of parameters.
27+
*/
28+
@Override
29+
public List<AuthSchemeOption> resolveAuthScheme(QueryAuthSchemeParams params) {
30+
List<AuthSchemeOption> candidateAuthSchemes = delegate.resolveAuthScheme(params);
31+
if (CollectionUtils.isNullOrEmpty(authSchemePreference)) {
32+
return candidateAuthSchemes;
33+
}
34+
List<AuthSchemeOption> authSchemes = new ArrayList<>();
35+
authSchemePreference.forEach(preferredSchemeId -> {
36+
candidateAuthSchemes
37+
.stream()
38+
.filter(candidate -> {
39+
String candidateSchemeName = candidate.schemeId().contains("#") ? candidate.schemeId().split("#")[1]
40+
: candidate.schemeId();
41+
return candidateSchemeName.equals(preferredSchemeId);
42+
}).findFirst().ifPresent(authSchemes::add);
43+
});
44+
candidateAuthSchemes.forEach(candidate -> {
45+
if (!authSchemes.contains(candidate)) {
46+
authSchemes.add(candidate);
47+
}
48+
});
49+
return authSchemes;
50+
}
51+
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-auth-scheme-provider.java

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,3 @@
1-
/*
2-
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License").
5-
* You may not use this file except in compliance with the License.
6-
* A copy of the License is located at
7-
*
8-
* http://aws.amazon.com/apache2.0
9-
*
10-
* or in the "license" file accompanying this file. This file is distributed
11-
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12-
* express or implied. See the License for the specific language governing
13-
* permissions and limitations under the License.
14-
*/
15-
161
package software.amazon.awssdk.services.query.auth.scheme;
172

183
import java.util.List;
@@ -22,6 +7,7 @@
227
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
238
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeProvider;
249
import software.amazon.awssdk.services.query.auth.scheme.internal.DefaultQueryAuthSchemeProvider;
10+
import software.amazon.awssdk.services.query.auth.scheme.internal.PreferredQueryAuthSchemeProvider;
2511

2612
/**
2713
* An auth scheme provider for Query service. The auth scheme provider takes a set of parameters using
@@ -50,4 +36,11 @@ default List<AuthSchemeOption> resolveAuthScheme(Consumer<QueryAuthSchemeParams.
5036
static QueryAuthSchemeProvider defaultProvider() {
5137
return DefaultQueryAuthSchemeProvider.create();
5238
}
39+
40+
/**
41+
* Get the default auth scheme provider the preferred auth schemes in order of preference.
42+
*/
43+
static QueryAuthSchemeProvider defaultProvider(List<String> authSchemePreference) {
44+
return new PreferredQueryAuthSchemeProvider(defaultProvider(), authSchemePreference);
45+
}
5346
}

0 commit comments

Comments
 (0)