Skip to content

Commit d5f5f30

Browse files
authored
Implement authSchemes support for endpoints (#3455)
This change introduces a new ExecutionInterceptor that takes the resolved endpoints, and sets the headers and uses the auth scheme properties to modify the signing configuration for the request.
1 parent f7909e8 commit d5f5f30

File tree

20 files changed

+811
-75
lines changed

20 files changed

+811
-75
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
2727
import software.amazon.awssdk.codegen.model.service.ClientContextParam;
2828
import software.amazon.awssdk.codegen.poet.rules.ClientContextParamsClassSpec;
29+
import software.amazon.awssdk.codegen.poet.rules.EndpointAuthSchemeInterceptorClassSpec;
2930
import software.amazon.awssdk.codegen.poet.rules.EndpointParametersClassSpec;
3031
import software.amazon.awssdk.codegen.poet.rules.EndpointProviderInterfaceSpec;
3132
import software.amazon.awssdk.codegen.poet.rules.EndpointProviderSpec;
@@ -75,7 +76,9 @@ private GeneratorTask generateDefaultProvider() {
7576
private Collection<GeneratorTask> generateInterceptors() {
7677
return Arrays.asList(
7778
new PoetGeneratorTask(endpointRulesInternalDir(), model.getFileHeader(), new EndpointResolverInterceptorSpec(model)),
78-
new PoetGeneratorTask(endpointRulesInternalDir(), model.getFileHeader(), new RequestEndpointInterceptorSpec(model)));
79+
new PoetGeneratorTask(endpointRulesInternalDir(), model.getFileHeader(), new RequestEndpointInterceptorSpec(model)),
80+
new PoetGeneratorTask(endpointRulesInternalDir(), model.getFileHeader(),
81+
new EndpointAuthSchemeInterceptorClassSpec(model)));
7982
}
8083

8184
private GeneratorTask generateClientTests() {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ private MethodSpec finalizeServiceConfigurationMethod() {
240240
ArrayList.class);
241241

242242
builder.addStatement("additionalInterceptors.add(new $T())", endpointRulesSpecUtils.resolverInterceptorName());
243+
builder.addStatement("additionalInterceptors.add(new $T())", endpointRulesSpecUtils.authSchemesInterceptorName());
243244
builder.addStatement("additionalInterceptors.add(new $T())", endpointRulesSpecUtils.requestModifierInterceptorName());
244245

245246
if (model.getMetadata().isQueryProtocol()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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.rules;
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.List;
23+
import java.util.function.Supplier;
24+
import javax.lang.model.element.Modifier;
25+
import software.amazon.awssdk.annotations.SdkInternalApi;
26+
import software.amazon.awssdk.auth.signer.Aws4Signer;
27+
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
28+
import software.amazon.awssdk.auth.signer.SignerLoader;
29+
import software.amazon.awssdk.awscore.AwsRequest;
30+
import software.amazon.awssdk.awscore.rules.AwsEndpointAttribute;
31+
import software.amazon.awssdk.awscore.rules.AwsEndpointProviderUtils;
32+
import software.amazon.awssdk.awscore.rules.authscheme.AuthSchemeUtils;
33+
import software.amazon.awssdk.awscore.rules.authscheme.EndpointAuthScheme;
34+
import software.amazon.awssdk.awscore.util.SignerOverrideUtils;
35+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
36+
import software.amazon.awssdk.codegen.poet.ClassSpec;
37+
import software.amazon.awssdk.codegen.poet.PoetUtils;
38+
import software.amazon.awssdk.core.SdkRequest;
39+
import software.amazon.awssdk.core.exception.SdkClientException;
40+
import software.amazon.awssdk.core.interceptor.Context;
41+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
42+
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
43+
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
44+
import software.amazon.awssdk.core.rules.model.Endpoint;
45+
import software.amazon.awssdk.core.signer.Signer;
46+
47+
/**
48+
* Generates the Endpoint Interceptor responsible for applying the {@link AwsEndpointAttribute#AUTH_SCHEMES} property on the
49+
* endpoint if they exist. Auth schemes describe auth related requirements for the endpoint, such as signing name, signing
50+
* region, and the name of the auth scheme to use, such as SigV4.
51+
*/
52+
public class EndpointAuthSchemeInterceptorClassSpec implements ClassSpec {
53+
private static final String SIGV4_NAME = "sigv4";
54+
private static final String SIGV4A_NAME = "sigv4a";
55+
56+
private final IntermediateModel intermediateModel;
57+
private final EndpointRulesSpecUtils endpointRulesSpecUtils;
58+
59+
public EndpointAuthSchemeInterceptorClassSpec(IntermediateModel intermediateModel) {
60+
this.intermediateModel = intermediateModel;
61+
this.endpointRulesSpecUtils = new EndpointRulesSpecUtils(intermediateModel);
62+
}
63+
64+
@Override
65+
public TypeSpec poetSpec() {
66+
TypeSpec.Builder b = PoetUtils.createClassBuilder(className())
67+
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
68+
.addAnnotation(SdkInternalApi.class)
69+
.addSuperinterface(ExecutionInterceptor.class);
70+
71+
b.addMethod(modifyRequestMethod());
72+
b.addMethod(signerProviderMethod());
73+
74+
return b.build();
75+
}
76+
77+
@Override
78+
public ClassName className() {
79+
return endpointRulesSpecUtils.authSchemesInterceptorName();
80+
}
81+
82+
private MethodSpec modifyRequestMethod() {
83+
MethodSpec.Builder builder = MethodSpec.methodBuilder("modifyRequest")
84+
.addModifiers(Modifier.PUBLIC)
85+
.addAnnotation(Override.class)
86+
.addParameter(ClassName.get(Context.ModifyRequest.class), "context")
87+
.addParameter(ExecutionAttributes.class, "executionAttributes")
88+
.returns(SdkRequest.class);
89+
90+
builder.addStatement("$T resolvedEndpoint = executionAttributes.getAttribute($T.RESOLVED_ENDPOINT)", Endpoint.class,
91+
SdkInternalExecutionAttribute.class);
92+
93+
builder.addStatement("$1T request = ($1T) context.request()", AwsRequest.class);
94+
95+
builder.beginControlFlow("if (resolvedEndpoint.headers() != null)")
96+
.addStatement("request = $T.addHeaders(request, resolvedEndpoint.headers())", AwsEndpointProviderUtils.class);
97+
builder.endControlFlow();
98+
99+
builder.addStatement("$T authSchemes = resolvedEndpoint.attribute($T.AUTH_SCHEMES)",
100+
ParameterizedTypeName.get(List.class, EndpointAuthScheme.class), AwsEndpointAttribute.class);
101+
102+
builder.beginControlFlow("if (authSchemes == null)")
103+
.addStatement("return request")
104+
.endControlFlow();
105+
106+
// find the scheme to use
107+
builder.addStatement("$T chosenAuthScheme = $T.chooseAuthScheme(authSchemes)", EndpointAuthScheme.class,
108+
AuthSchemeUtils.class);
109+
110+
// Create a signer provider
111+
builder.addStatement("$T signerProvider = signerProvider(chosenAuthScheme)", ParameterizedTypeName.get(Supplier.class,
112+
Signer.class));
113+
114+
// Set signing attributes
115+
builder.addStatement("$T.setSigningParams(executionAttributes, chosenAuthScheme)", AuthSchemeUtils.class);
116+
117+
// Override signer
118+
builder.addStatement("return $T.overrideSignerIfNotOverridden(request, executionAttributes, signerProvider)",
119+
SignerOverrideUtils.class);
120+
121+
122+
return builder.build();
123+
}
124+
125+
private MethodSpec signerProviderMethod() {
126+
MethodSpec.Builder builder = MethodSpec.methodBuilder("signerProvider")
127+
.addModifiers(Modifier.PRIVATE)
128+
.addParameter(EndpointAuthScheme.class, "authScheme")
129+
.returns(ParameterizedTypeName.get(Supplier.class, Signer.class));
130+
131+
builder.beginControlFlow("switch (authScheme.name())");
132+
builder.addCode("case $S:", SIGV4_NAME);
133+
if (isS3()) {
134+
builder.addStatement("return $T::create", AwsS3V4Signer.class);
135+
} else {
136+
builder.addStatement("return $T::create", Aws4Signer.class);
137+
}
138+
139+
builder.addCode("case $S:", SIGV4A_NAME);
140+
if (isS3()) {
141+
builder.addStatement("return $T::getS3SigV4aSigner", SignerLoader.class);
142+
} else {
143+
builder.addStatement("return $T::getSigV4aSigner", SignerLoader.class);
144+
}
145+
146+
builder.addCode("default:");
147+
builder.addStatement("break");
148+
builder.endControlFlow();
149+
150+
builder.addStatement("throw $T.create($S + authScheme.name())",
151+
SdkClientException.class,
152+
"Don't know how to create signer for auth scheme: ");
153+
154+
return builder.build();
155+
}
156+
157+
private boolean isS3() {
158+
return "S3".equals(intermediateModel.getMetadata().getServiceName());
159+
}
160+
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesSpecUtils.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ public ClassName resolverInterceptorName() {
6262
md.getServiceName() + "ResolveEndpointInterceptor");
6363
}
6464

65+
public ClassName authSchemesInterceptorName() {
66+
Metadata md = intermediateModel.getMetadata();
67+
return ClassName.get(md.getFullInternalEndpointRulesPackageName(),
68+
md.getServiceName() + "EndpointAuthSchemeInterceptor");
69+
}
70+
6571
public ClassName requestModifierInterceptorName() {
6672
Metadata md = intermediateModel.getMetadata();
6773
return ClassName.get(md.getFullInternalEndpointRulesPackageName(),

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/TestGeneratorUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import java.util.List;
2828
import java.util.Map;
2929
import software.amazon.awssdk.awscore.rules.AwsEndpointAttribute;
30-
import software.amazon.awssdk.awscore.rules.SigV4AuthScheme;
31-
import software.amazon.awssdk.awscore.rules.SigV4aAuthScheme;
30+
import software.amazon.awssdk.awscore.rules.authscheme.SigV4AuthScheme;
31+
import software.amazon.awssdk.awscore.rules.authscheme.SigV4aAuthScheme;
3232
import software.amazon.awssdk.codegen.model.rules.endpoints.ExpectModel;
3333
import software.amazon.awssdk.core.rules.model.Endpoint;
3434
import software.amazon.awssdk.core.rules.testing.model.Expect;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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.rules;
17+
18+
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static software.amazon.awssdk.codegen.poet.PoetMatchers.generatesTo;
20+
21+
import org.junit.jupiter.api.Test;
22+
import software.amazon.awssdk.codegen.poet.ClassSpec;
23+
import software.amazon.awssdk.codegen.poet.ClientTestModels;
24+
25+
public class EndpointAuthSchemeInterceptorClassSpecTest {
26+
27+
@Test
28+
public void endpointAuthSchemeInterceptor() {
29+
ClassSpec endpointAuthSchemeInterceptor = new EndpointAuthSchemeInterceptorClassSpec(ClientTestModels.queryServiceModels());
30+
assertThat(endpointAuthSchemeInterceptor, generatesTo("endpoint-auth-scheme-interceptor.java"));
31+
}
32+
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
1717
import software.amazon.awssdk.core.signer.Signer;
1818
import software.amazon.awssdk.services.json.rules.JsonEndpointProvider;
19+
import software.amazon.awssdk.services.json.rules.internal.JsonEndpointAuthSchemeInterceptor;
1920
import software.amazon.awssdk.services.json.rules.internal.JsonRequestSetEndpointInterceptor;
2021
import software.amazon.awssdk.services.json.rules.internal.JsonResolveEndpointInterceptor;
2122
import software.amazon.awssdk.utils.AttributeMap;
@@ -53,6 +54,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon
5354
.getInterceptors("software/amazon/awssdk/services/json/execution.interceptors");
5455
List<ExecutionInterceptor> additionalInterceptors = new ArrayList<>();
5556
additionalInterceptors.add(new JsonResolveEndpointInterceptor());
57+
additionalInterceptors.add(new JsonEndpointAuthSchemeInterceptor());
5658
additionalInterceptors.add(new JsonRequestSetEndpointInterceptor());
5759
interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors);
5860
interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS));

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-internal-defaults-class.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import software.amazon.awssdk.core.retry.RetryMode;
1515
import software.amazon.awssdk.core.signer.Signer;
1616
import software.amazon.awssdk.services.json.rules.JsonEndpointProvider;
17+
import software.amazon.awssdk.services.json.rules.internal.JsonEndpointAuthSchemeInterceptor;
1718
import software.amazon.awssdk.services.json.rules.internal.JsonRequestSetEndpointInterceptor;
1819
import software.amazon.awssdk.services.json.rules.internal.JsonResolveEndpointInterceptor;
1920
import software.amazon.awssdk.utils.CollectionUtils;
@@ -56,6 +57,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon
5657
.getInterceptors("software/amazon/awssdk/services/json/execution.interceptors");
5758
List<ExecutionInterceptor> additionalInterceptors = new ArrayList<>();
5859
additionalInterceptors.add(new JsonResolveEndpointInterceptor());
60+
additionalInterceptors.add(new JsonEndpointAuthSchemeInterceptor());
5961
additionalInterceptors.add(new JsonRequestSetEndpointInterceptor());
6062
interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors);
6163
interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS));

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
1616
import software.amazon.awssdk.services.query.rules.QueryClientContextParams;
1717
import software.amazon.awssdk.services.query.rules.QueryEndpointProvider;
18+
import software.amazon.awssdk.services.query.rules.internal.QueryEndpointAuthSchemeInterceptor;
1819
import software.amazon.awssdk.services.query.rules.internal.QueryRequestSetEndpointInterceptor;
1920
import software.amazon.awssdk.services.query.rules.internal.QueryResolveEndpointInterceptor;
2021
import software.amazon.awssdk.utils.CollectionUtils;
@@ -49,6 +50,7 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon
4950
.getInterceptors("software/amazon/awssdk/services/query/execution.interceptors");
5051
List<ExecutionInterceptor> additionalInterceptors = new ArrayList<>();
5152
additionalInterceptors.add(new QueryResolveEndpointInterceptor());
53+
additionalInterceptors.add(new QueryEndpointAuthSchemeInterceptor());
5254
additionalInterceptors.add(new QueryRequestSetEndpointInterceptor());
5355
additionalInterceptors.add(new QueryParametersToBodyInterceptor());
5456
interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package software.amazon.awssdk.services.query.rules.internal;
2+
3+
import java.util.List;
4+
import java.util.function.Supplier;
5+
import software.amazon.awssdk.annotations.Generated;
6+
import software.amazon.awssdk.annotations.SdkInternalApi;
7+
import software.amazon.awssdk.auth.signer.Aws4Signer;
8+
import software.amazon.awssdk.auth.signer.SignerLoader;
9+
import software.amazon.awssdk.awscore.AwsRequest;
10+
import software.amazon.awssdk.awscore.rules.AwsEndpointAttribute;
11+
import software.amazon.awssdk.awscore.rules.AwsEndpointProviderUtils;
12+
import software.amazon.awssdk.awscore.rules.authscheme.AuthSchemeUtils;
13+
import software.amazon.awssdk.awscore.rules.authscheme.EndpointAuthScheme;
14+
import software.amazon.awssdk.awscore.util.SignerOverrideUtils;
15+
import software.amazon.awssdk.core.SdkRequest;
16+
import software.amazon.awssdk.core.exception.SdkClientException;
17+
import software.amazon.awssdk.core.interceptor.Context;
18+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
19+
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
20+
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
21+
import software.amazon.awssdk.core.rules.model.Endpoint;
22+
import software.amazon.awssdk.core.signer.Signer;
23+
24+
@Generated("software.amazon.awssdk:codegen")
25+
@SdkInternalApi
26+
public final class QueryEndpointAuthSchemeInterceptor implements ExecutionInterceptor {
27+
@Override
28+
public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) {
29+
Endpoint resolvedEndpoint = executionAttributes.getAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT);
30+
AwsRequest request = (AwsRequest) context.request();
31+
if (resolvedEndpoint.headers() != null) {
32+
request = AwsEndpointProviderUtils.addHeaders(request, resolvedEndpoint.headers());
33+
}
34+
List<EndpointAuthScheme> authSchemes = resolvedEndpoint.attribute(AwsEndpointAttribute.AUTH_SCHEMES);
35+
if (authSchemes == null) {
36+
return request;
37+
}
38+
EndpointAuthScheme chosenAuthScheme = AuthSchemeUtils.chooseAuthScheme(authSchemes);
39+
Supplier<Signer> signerProvider = signerProvider(chosenAuthScheme);
40+
AuthSchemeUtils.setSigningParams(executionAttributes, chosenAuthScheme);
41+
return SignerOverrideUtils.overrideSignerIfNotOverridden(request, executionAttributes, signerProvider);
42+
}
43+
44+
private Supplier<Signer> signerProvider(EndpointAuthScheme authScheme) {
45+
switch (authScheme.name()) {
46+
case "sigv4":
47+
return Aws4Signer::create;
48+
case "sigv4a":
49+
return SignerLoader::getSigV4aSigner;
50+
default:
51+
break;
52+
}
53+
throw SdkClientException.create("Don't know how to create signer for auth scheme: " + authScheme.name());
54+
}
55+
}

core/aws-core/src/main/java/software/amazon/awssdk/awscore/rules/AwsEndpointAttribute.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Collections;
1919
import java.util.List;
2020
import software.amazon.awssdk.annotations.SdkProtectedApi;
21+
import software.amazon.awssdk.awscore.rules.authscheme.EndpointAuthScheme;
2122
import software.amazon.awssdk.core.rules.model.EndpointAttributeKey;
2223

2324
/**

0 commit comments

Comments
 (0)