Skip to content

Commit e4c4612

Browse files
authored
Add default RegionScope for SigV4a signers (#3025)
This commit adds support for setting the default RegionScope used by the Sigv4a signer implementations. If the region scope to use is not supplied to the signing methods, the signers will default to this scope first and then the normal signing region.
1 parent aa31ccc commit e4c4612

File tree

14 files changed

+397
-161
lines changed

14 files changed

+397
-161
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "feature",
5+
"description": "Add support for setting the default `RegionScope` used by the Sigv4a signer implementations. If the region scope to use is not supplied to the signing methods, the signers will default to this scope first and then the normal signing region."
6+
}

core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java

+17
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import software.amazon.awssdk.authcrt.signer.internal.DefaultAwsCrtS3V4aSigner;
2222
import software.amazon.awssdk.core.signer.Presigner;
2323
import software.amazon.awssdk.core.signer.Signer;
24+
import software.amazon.awssdk.regions.RegionScope;
2425

2526
/**
2627
* Enables signing and presigning for S3 using Sigv4a (Asymmetric Sigv4) through an external API call to the AWS CRT
@@ -49,4 +50,20 @@ public interface AwsCrtS3V4aSigner extends Signer, Presigner {
4950
static AwsCrtS3V4aSigner create() {
5051
return DefaultAwsCrtS3V4aSigner.create();
5152
}
53+
54+
static Builder builder() {
55+
return DefaultAwsCrtS3V4aSigner.builder();
56+
}
57+
58+
interface Builder {
59+
/**
60+
* The region scope that this signer will default to if not provided explicitly when the signer is invoked.
61+
*
62+
* @param defaultRegionScope The default region scope.
63+
* @return This builder for method chaining.
64+
*/
65+
Builder defaultRegionScope(RegionScope defaultRegionScope);
66+
67+
AwsCrtS3V4aSigner build();
68+
}
5269
}

core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java

+17
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import software.amazon.awssdk.authcrt.signer.internal.DefaultAwsCrtV4aSigner;
2222
import software.amazon.awssdk.core.signer.Presigner;
2323
import software.amazon.awssdk.core.signer.Signer;
24+
import software.amazon.awssdk.regions.RegionScope;
2425

2526
/**
2627
* Enables signing and presigning using Sigv4a (Asymmetric Sigv4) through an external API call to the AWS CRT
@@ -39,4 +40,20 @@ public interface AwsCrtV4aSigner extends Signer, Presigner {
3940
static AwsCrtV4aSigner create() {
4041
return DefaultAwsCrtV4aSigner.create();
4142
}
43+
44+
static Builder builder() {
45+
return DefaultAwsCrtV4aSigner.builder();
46+
}
47+
48+
interface Builder {
49+
/**
50+
* The region scope that this signer will default to if not provided explicitly when the signer is invoked.
51+
*
52+
* @param defaultRegionScope The default region scope.
53+
* @return This builder for method chaining.
54+
*/
55+
Builder defaultRegionScope(RegionScope defaultRegionScope);
56+
57+
AwsCrtV4aSigner build();
58+
}
4259
}

core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtS3V4aSigner.java

+57-5
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,51 @@
3131
import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig;
3232
import software.amazon.awssdk.http.ContentStreamProvider;
3333
import software.amazon.awssdk.http.SdkHttpFullRequest;
34+
import software.amazon.awssdk.regions.RegionScope;
3435

3536
@SdkInternalApi
3637
public final class DefaultAwsCrtS3V4aSigner implements AwsCrtS3V4aSigner {
3738

3839
private final AwsCrt4aSigningAdapter signerAdapter;
3940
private final SigningConfigProvider configProvider;
41+
private final RegionScope defaultRegionScope;
4042

4143
DefaultAwsCrtS3V4aSigner(AwsCrt4aSigningAdapter signerAdapter, SigningConfigProvider signingConfigProvider) {
44+
this(signerAdapter, signingConfigProvider, null);
45+
}
46+
47+
DefaultAwsCrtS3V4aSigner(AwsCrt4aSigningAdapter signerAdapter, SigningConfigProvider signingConfigProvider,
48+
RegionScope defaultRegionScope) {
4249
this.signerAdapter = signerAdapter;
4350
this.configProvider = signingConfigProvider;
51+
this.defaultRegionScope = defaultRegionScope;
52+
}
53+
54+
private DefaultAwsCrtS3V4aSigner(BuilderImpl builder) {
55+
this(new AwsCrt4aSigningAdapter(), new SigningConfigProvider(), builder.defaultRegionScope);
4456
}
4557

4658
public static AwsCrtS3V4aSigner create() {
47-
return new DefaultAwsCrtS3V4aSigner(new AwsCrt4aSigningAdapter(), new SigningConfigProvider());
59+
return builder().build();
60+
}
61+
62+
public static Builder builder() {
63+
return new BuilderImpl();
4864
}
4965

5066
@Override
5167
public SdkHttpFullRequest sign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
5268
if (credentialsAreAnonymous(executionAttributes)) {
5369
return request;
5470
}
55-
AwsSigningConfig requestSigningConfig = configProvider.createS3CrtSigningConfig(executionAttributes);
56-
if (shouldSignPayload(request, executionAttributes)) {
71+
ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes);
72+
AwsSigningConfig requestSigningConfig = configProvider.createS3CrtSigningConfig(defaultsApplied);
73+
if (shouldSignPayload(request, defaultsApplied)) {
5774
requestSigningConfig.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD);
5875
SdkHttpFullRequest.Builder mutableRequest = request.toBuilder();
5976
setHeaderContentLength(mutableRequest);
6077
SdkSigningResult signingResult = signerAdapter.sign(mutableRequest.build(), requestSigningConfig);
61-
AwsSigningConfig chunkConfig = configProvider.createChunkedSigningConfig(executionAttributes);
78+
AwsSigningConfig chunkConfig = configProvider.createChunkedSigningConfig(defaultsApplied);
6279
return enablePayloadSigning(signingResult, chunkConfig);
6380
} else {
6481
requestSigningConfig.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.UNSIGNED_PAYLOAD);
@@ -71,7 +88,8 @@ public SdkHttpFullRequest presign(SdkHttpFullRequest request, ExecutionAttribute
7188
if (credentialsAreAnonymous(executionAttributes)) {
7289
return request;
7390
}
74-
return signerAdapter.signRequest(request, configProvider.createS3CrtPresigningConfig(executionAttributes));
91+
ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes);
92+
return signerAdapter.signRequest(request, configProvider.createS3CrtPresigningConfig(defaultsApplied));
7593
}
7694

7795
private boolean credentialsAreAnonymous(ExecutionAttributes executionAttributes) {
@@ -121,4 +139,38 @@ private boolean booleanValue(Boolean attribute) {
121139
return Boolean.TRUE.equals(attribute);
122140
}
123141

142+
/**
143+
* Applies preconfigured defaults for values that are not present in {@code executionAttributes}.
144+
*/
145+
private ExecutionAttributes applyDefaults(ExecutionAttributes executionAttributes) {
146+
return applyDefaultRegionScope(executionAttributes);
147+
}
148+
149+
private ExecutionAttributes applyDefaultRegionScope(ExecutionAttributes executionAttributes) {
150+
if (executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE) != null) {
151+
return executionAttributes;
152+
}
153+
154+
if (defaultRegionScope == null) {
155+
return executionAttributes;
156+
}
157+
158+
return executionAttributes.copy()
159+
.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, defaultRegionScope);
160+
}
161+
162+
private static class BuilderImpl implements Builder {
163+
private RegionScope defaultRegionScope;
164+
165+
@Override
166+
public Builder defaultRegionScope(RegionScope defaultRegionScope) {
167+
this.defaultRegionScope = defaultRegionScope;
168+
return this;
169+
}
170+
171+
@Override
172+
public AwsCrtS3V4aSigner build() {
173+
return new DefaultAwsCrtS3V4aSigner(this);
174+
}
175+
}
124176
}

core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtV4aSigner.java

+55-6
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,81 @@
2121
import software.amazon.awssdk.authcrt.signer.AwsCrtV4aSigner;
2222
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2323
import software.amazon.awssdk.http.SdkHttpFullRequest;
24+
import software.amazon.awssdk.regions.RegionScope;
2425

2526
@SdkInternalApi
2627
public final class DefaultAwsCrtV4aSigner implements AwsCrtV4aSigner {
2728

2829
private final AwsCrt4aSigningAdapter signer;
2930
private final SigningConfigProvider configProvider;
31+
private final RegionScope defaultRegionScope;
3032

31-
private DefaultAwsCrtV4aSigner() {
32-
signer = new AwsCrt4aSigningAdapter();
33-
configProvider = new SigningConfigProvider();
33+
private DefaultAwsCrtV4aSigner(BuilderImpl builder) {
34+
this(new AwsCrt4aSigningAdapter(), new SigningConfigProvider(), builder.defaultRegionScope);
35+
}
36+
37+
DefaultAwsCrtV4aSigner(AwsCrt4aSigningAdapter signer, SigningConfigProvider configProvider,
38+
RegionScope defaultRegionScope) {
39+
this.signer = signer;
40+
this.configProvider = configProvider;
41+
this.defaultRegionScope = defaultRegionScope;
3442
}
3543

3644
public static AwsCrtV4aSigner create() {
37-
return new DefaultAwsCrtV4aSigner();
45+
return builder().build();
46+
}
47+
48+
public static Builder builder() {
49+
return new BuilderImpl();
3850
}
3951

4052
@Override
4153
public SdkHttpFullRequest sign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
4254
if (CredentialUtils.isAnonymous(executionAttributes.getAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS))) {
4355
return request;
4456
}
45-
return signer.signRequest(request, configProvider.createCrtSigningConfig(executionAttributes));
57+
ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes);
58+
return signer.signRequest(request, configProvider.createCrtSigningConfig(defaultsApplied));
4659
}
4760

4861
@Override
4962
public SdkHttpFullRequest presign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
50-
return signer.signRequest(request, configProvider.createCrtPresigningConfig(executionAttributes));
63+
ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes);
64+
return signer.signRequest(request, configProvider.createCrtPresigningConfig(defaultsApplied));
65+
}
66+
67+
/**
68+
* Applies preconfigured defaults for values that are not present in {@code executionAttributes}.
69+
*/
70+
private ExecutionAttributes applyDefaults(ExecutionAttributes executionAttributes) {
71+
return applyDefaultRegionScope(executionAttributes);
72+
}
73+
74+
private ExecutionAttributes applyDefaultRegionScope(ExecutionAttributes executionAttributes) {
75+
if (executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE) != null) {
76+
return executionAttributes;
77+
}
78+
79+
if (defaultRegionScope == null) {
80+
return executionAttributes;
81+
}
82+
83+
return executionAttributes.copy()
84+
.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, defaultRegionScope);
85+
}
86+
87+
private static class BuilderImpl implements Builder {
88+
private RegionScope defaultRegionScope;
89+
90+
@Override
91+
public Builder defaultRegionScope(RegionScope defaultRegionScope) {
92+
this.defaultRegionScope = defaultRegionScope;
93+
return this;
94+
}
95+
96+
@Override
97+
public AwsCrtV4aSigner build() {
98+
return new DefaultAwsCrtV4aSigner(this);
99+
}
51100
}
52101
}

core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningConfigProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import software.amazon.awssdk.auth.signer.internal.SignerConstant;
2727
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2828
import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig;
29-
import software.amazon.awssdk.regions.internal.util.RegionScope;
29+
import software.amazon.awssdk.regions.RegionScope;
3030

3131
@SdkInternalApi
3232
public class SigningConfigProvider {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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.authcrt.signer.internal;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.util.List;
21+
import org.junit.jupiter.api.Test;
22+
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
23+
import software.amazon.awssdk.authcrt.signer.AwsCrtS3V4aSigner;
24+
import software.amazon.awssdk.authcrt.signer.SignerTestUtils;
25+
import software.amazon.awssdk.authcrt.signer.SigningTestCase;
26+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
27+
import software.amazon.awssdk.http.SdkHttpFullRequest;
28+
import software.amazon.awssdk.regions.RegionScope;
29+
30+
public class AwsCrtS3V4aSignerSigningScopeTest extends BaseSigningScopeTest {
31+
@Test
32+
public void signing_chunkedEncoding_regionalScope_present_overrides() {
33+
SigningTestCase testCase = SignerTestUtils.createBasicChunkedSigningTestCase();
34+
SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, null, RegionScope.GLOBAL);
35+
36+
String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get();
37+
assertThat(regionHeader).isEqualTo(RegionScope.GLOBAL.id());
38+
}
39+
40+
@Test
41+
public void testS3Presigning() {
42+
SigningTestCase testCase = SignerTestUtils.createBasicQuerySigningTestCase();
43+
44+
SdkHttpFullRequest signedRequest = presignRequestWithScope(testCase, null, RegionScope.GLOBAL);
45+
46+
List<String> regionHeader = signedRequest.rawQueryParameters().get("X-Amz-Region-Set");
47+
assertThat(regionHeader.get(0)).isEqualTo(RegionScope.GLOBAL.id());
48+
}
49+
50+
51+
protected SdkHttpFullRequest signRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope,
52+
RegionScope regionScope) {
53+
ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase);
54+
if (regionScope != null) {
55+
executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope);
56+
}
57+
SdkHttpFullRequest request = testCase.requestBuilder.build();
58+
return AwsCrtS3V4aSigner.builder().defaultRegionScope(defaultRegionScope).build().sign(request, executionAttributes);
59+
}
60+
61+
protected SdkHttpFullRequest presignRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope,
62+
RegionScope regionScope) {
63+
ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase);
64+
if (regionScope != null) {
65+
executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope);
66+
}
67+
SdkHttpFullRequest request = testCase.requestBuilder.build();
68+
return AwsCrtS3V4aSigner.builder().defaultRegionScope(defaultRegionScope).build().presign(request, executionAttributes);
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.authcrt.signer.internal;
17+
18+
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
19+
import software.amazon.awssdk.authcrt.signer.AwsCrtV4aSigner;
20+
import software.amazon.awssdk.authcrt.signer.SignerTestUtils;
21+
import software.amazon.awssdk.authcrt.signer.SigningTestCase;
22+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
23+
import software.amazon.awssdk.http.SdkHttpFullRequest;
24+
import software.amazon.awssdk.regions.RegionScope;
25+
26+
public class AwsCrtV4aSignerSigningScopeTest extends BaseSigningScopeTest {
27+
@Override
28+
protected SdkHttpFullRequest signRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope,
29+
RegionScope regionScope) {
30+
ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase);
31+
if (regionScope != null) {
32+
executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope);
33+
}
34+
SdkHttpFullRequest request = testCase.requestBuilder.build();
35+
return AwsCrtV4aSigner.builder().defaultRegionScope(defaultRegionScope).build().sign(request, executionAttributes);
36+
}
37+
38+
@Override
39+
protected SdkHttpFullRequest presignRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope,
40+
RegionScope regionScope) {
41+
ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase);
42+
if (regionScope != null) {
43+
executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope);
44+
}
45+
SdkHttpFullRequest request = testCase.requestBuilder.build();
46+
return AwsCrtV4aSigner.builder().defaultRegionScope(defaultRegionScope).build().presign(request, executionAttributes);
47+
}
48+
}

0 commit comments

Comments
 (0)