diff --git a/.changes/next-release/feature-AWSSDKforJavav2-7723452.json b/.changes/next-release/feature-AWSSDKforJavav2-7723452.json new file mode 100644 index 000000000000..f6163da47fa7 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-7723452.json @@ -0,0 +1,6 @@ +{ + "category": "AWS SDK for Java v2", + "contributor": "", + "type": "feature", + "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." +} diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java index fb00fdebe921..c6ee258c064e 100644 --- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java +++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java @@ -21,6 +21,7 @@ import software.amazon.awssdk.authcrt.signer.internal.DefaultAwsCrtS3V4aSigner; import software.amazon.awssdk.core.signer.Presigner; import software.amazon.awssdk.core.signer.Signer; +import software.amazon.awssdk.regions.RegionScope; /** * 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 { static AwsCrtS3V4aSigner create() { return DefaultAwsCrtS3V4aSigner.create(); } + + static Builder builder() { + return DefaultAwsCrtS3V4aSigner.builder(); + } + + interface Builder { + /** + * The region scope that this signer will default to if not provided explicitly when the signer is invoked. + * + * @param defaultRegionScope The default region scope. + * @return This builder for method chaining. + */ + Builder defaultRegionScope(RegionScope defaultRegionScope); + + AwsCrtS3V4aSigner build(); + } } diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java index a61b30a289e5..692ecec5047f 100644 --- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java +++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java @@ -21,6 +21,7 @@ import software.amazon.awssdk.authcrt.signer.internal.DefaultAwsCrtV4aSigner; import software.amazon.awssdk.core.signer.Presigner; import software.amazon.awssdk.core.signer.Signer; +import software.amazon.awssdk.regions.RegionScope; /** * 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 { static AwsCrtV4aSigner create() { return DefaultAwsCrtV4aSigner.create(); } + + static Builder builder() { + return DefaultAwsCrtV4aSigner.builder(); + } + + interface Builder { + /** + * The region scope that this signer will default to if not provided explicitly when the signer is invoked. + * + * @param defaultRegionScope The default region scope. + * @return This builder for method chaining. + */ + Builder defaultRegionScope(RegionScope defaultRegionScope); + + AwsCrtV4aSigner build(); + } } diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtS3V4aSigner.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtS3V4aSigner.java index f7bb8a92cb26..940f1a60cb24 100644 --- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtS3V4aSigner.java +++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtS3V4aSigner.java @@ -31,20 +31,36 @@ import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig; import software.amazon.awssdk.http.ContentStreamProvider; import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.regions.RegionScope; @SdkInternalApi public final class DefaultAwsCrtS3V4aSigner implements AwsCrtS3V4aSigner { private final AwsCrt4aSigningAdapter signerAdapter; private final SigningConfigProvider configProvider; + private final RegionScope defaultRegionScope; DefaultAwsCrtS3V4aSigner(AwsCrt4aSigningAdapter signerAdapter, SigningConfigProvider signingConfigProvider) { + this(signerAdapter, signingConfigProvider, null); + } + + DefaultAwsCrtS3V4aSigner(AwsCrt4aSigningAdapter signerAdapter, SigningConfigProvider signingConfigProvider, + RegionScope defaultRegionScope) { this.signerAdapter = signerAdapter; this.configProvider = signingConfigProvider; + this.defaultRegionScope = defaultRegionScope; + } + + private DefaultAwsCrtS3V4aSigner(BuilderImpl builder) { + this(new AwsCrt4aSigningAdapter(), new SigningConfigProvider(), builder.defaultRegionScope); } public static AwsCrtS3V4aSigner create() { - return new DefaultAwsCrtS3V4aSigner(new AwsCrt4aSigningAdapter(), new SigningConfigProvider()); + return builder().build(); + } + + public static Builder builder() { + return new BuilderImpl(); } @Override @@ -52,13 +68,14 @@ public SdkHttpFullRequest sign(SdkHttpFullRequest request, ExecutionAttributes e if (credentialsAreAnonymous(executionAttributes)) { return request; } - AwsSigningConfig requestSigningConfig = configProvider.createS3CrtSigningConfig(executionAttributes); - if (shouldSignPayload(request, executionAttributes)) { + ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes); + AwsSigningConfig requestSigningConfig = configProvider.createS3CrtSigningConfig(defaultsApplied); + if (shouldSignPayload(request, defaultsApplied)) { requestSigningConfig.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD); SdkHttpFullRequest.Builder mutableRequest = request.toBuilder(); setHeaderContentLength(mutableRequest); SdkSigningResult signingResult = signerAdapter.sign(mutableRequest.build(), requestSigningConfig); - AwsSigningConfig chunkConfig = configProvider.createChunkedSigningConfig(executionAttributes); + AwsSigningConfig chunkConfig = configProvider.createChunkedSigningConfig(defaultsApplied); return enablePayloadSigning(signingResult, chunkConfig); } else { requestSigningConfig.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.UNSIGNED_PAYLOAD); @@ -71,7 +88,8 @@ public SdkHttpFullRequest presign(SdkHttpFullRequest request, ExecutionAttribute if (credentialsAreAnonymous(executionAttributes)) { return request; } - return signerAdapter.signRequest(request, configProvider.createS3CrtPresigningConfig(executionAttributes)); + ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes); + return signerAdapter.signRequest(request, configProvider.createS3CrtPresigningConfig(defaultsApplied)); } private boolean credentialsAreAnonymous(ExecutionAttributes executionAttributes) { @@ -121,4 +139,38 @@ private boolean booleanValue(Boolean attribute) { return Boolean.TRUE.equals(attribute); } + /** + * Applies preconfigured defaults for values that are not present in {@code executionAttributes}. + */ + private ExecutionAttributes applyDefaults(ExecutionAttributes executionAttributes) { + return applyDefaultRegionScope(executionAttributes); + } + + private ExecutionAttributes applyDefaultRegionScope(ExecutionAttributes executionAttributes) { + if (executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE) != null) { + return executionAttributes; + } + + if (defaultRegionScope == null) { + return executionAttributes; + } + + return executionAttributes.copy() + .putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, defaultRegionScope); + } + + private static class BuilderImpl implements Builder { + private RegionScope defaultRegionScope; + + @Override + public Builder defaultRegionScope(RegionScope defaultRegionScope) { + this.defaultRegionScope = defaultRegionScope; + return this; + } + + @Override + public AwsCrtS3V4aSigner build() { + return new DefaultAwsCrtS3V4aSigner(this); + } + } } diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtV4aSigner.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtV4aSigner.java index a179b89b8977..aa56814fa703 100644 --- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtV4aSigner.java +++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/DefaultAwsCrtV4aSigner.java @@ -21,20 +21,32 @@ import software.amazon.awssdk.authcrt.signer.AwsCrtV4aSigner; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.regions.RegionScope; @SdkInternalApi public final class DefaultAwsCrtV4aSigner implements AwsCrtV4aSigner { private final AwsCrt4aSigningAdapter signer; private final SigningConfigProvider configProvider; + private final RegionScope defaultRegionScope; - private DefaultAwsCrtV4aSigner() { - signer = new AwsCrt4aSigningAdapter(); - configProvider = new SigningConfigProvider(); + private DefaultAwsCrtV4aSigner(BuilderImpl builder) { + this(new AwsCrt4aSigningAdapter(), new SigningConfigProvider(), builder.defaultRegionScope); + } + + DefaultAwsCrtV4aSigner(AwsCrt4aSigningAdapter signer, SigningConfigProvider configProvider, + RegionScope defaultRegionScope) { + this.signer = signer; + this.configProvider = configProvider; + this.defaultRegionScope = defaultRegionScope; } public static AwsCrtV4aSigner create() { - return new DefaultAwsCrtV4aSigner(); + return builder().build(); + } + + public static Builder builder() { + return new BuilderImpl(); } @Override @@ -42,11 +54,48 @@ public SdkHttpFullRequest sign(SdkHttpFullRequest request, ExecutionAttributes e if (CredentialUtils.isAnonymous(executionAttributes.getAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS))) { return request; } - return signer.signRequest(request, configProvider.createCrtSigningConfig(executionAttributes)); + ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes); + return signer.signRequest(request, configProvider.createCrtSigningConfig(defaultsApplied)); } @Override public SdkHttpFullRequest presign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) { - return signer.signRequest(request, configProvider.createCrtPresigningConfig(executionAttributes)); + ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes); + return signer.signRequest(request, configProvider.createCrtPresigningConfig(defaultsApplied)); + } + + /** + * Applies preconfigured defaults for values that are not present in {@code executionAttributes}. + */ + private ExecutionAttributes applyDefaults(ExecutionAttributes executionAttributes) { + return applyDefaultRegionScope(executionAttributes); + } + + private ExecutionAttributes applyDefaultRegionScope(ExecutionAttributes executionAttributes) { + if (executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE) != null) { + return executionAttributes; + } + + if (defaultRegionScope == null) { + return executionAttributes; + } + + return executionAttributes.copy() + .putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, defaultRegionScope); + } + + private static class BuilderImpl implements Builder { + private RegionScope defaultRegionScope; + + @Override + public Builder defaultRegionScope(RegionScope defaultRegionScope) { + this.defaultRegionScope = defaultRegionScope; + return this; + } + + @Override + public AwsCrtV4aSigner build() { + return new DefaultAwsCrtV4aSigner(this); + } } } diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningConfigProvider.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningConfigProvider.java index f055140701b2..9f18c030dc03 100644 --- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningConfigProvider.java +++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningConfigProvider.java @@ -26,7 +26,7 @@ import software.amazon.awssdk.auth.signer.internal.SignerConstant; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig; -import software.amazon.awssdk.regions.internal.util.RegionScope; +import software.amazon.awssdk.regions.RegionScope; @SdkInternalApi public class SigningConfigProvider { diff --git a/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/AwsCrtS3V4aSignerSigningScopeTest.java b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/AwsCrtS3V4aSignerSigningScopeTest.java new file mode 100644 index 000000000000..191c12bbcee4 --- /dev/null +++ b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/AwsCrtS3V4aSignerSigningScopeTest.java @@ -0,0 +1,70 @@ +/* + * 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.authcrt.signer.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; +import software.amazon.awssdk.authcrt.signer.AwsCrtS3V4aSigner; +import software.amazon.awssdk.authcrt.signer.SignerTestUtils; +import software.amazon.awssdk.authcrt.signer.SigningTestCase; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.regions.RegionScope; + +public class AwsCrtS3V4aSignerSigningScopeTest extends BaseSigningScopeTest { + @Test + public void signing_chunkedEncoding_regionalScope_present_overrides() { + SigningTestCase testCase = SignerTestUtils.createBasicChunkedSigningTestCase(); + SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, null, RegionScope.GLOBAL); + + String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); + assertThat(regionHeader).isEqualTo(RegionScope.GLOBAL.id()); + } + + @Test + public void testS3Presigning() { + SigningTestCase testCase = SignerTestUtils.createBasicQuerySigningTestCase(); + + SdkHttpFullRequest signedRequest = presignRequestWithScope(testCase, null, RegionScope.GLOBAL); + + List regionHeader = signedRequest.rawQueryParameters().get("X-Amz-Region-Set"); + assertThat(regionHeader.get(0)).isEqualTo(RegionScope.GLOBAL.id()); + } + + + protected SdkHttpFullRequest signRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope, + RegionScope regionScope) { + ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); + if (regionScope != null) { + executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope); + } + SdkHttpFullRequest request = testCase.requestBuilder.build(); + return AwsCrtS3V4aSigner.builder().defaultRegionScope(defaultRegionScope).build().sign(request, executionAttributes); + } + + protected SdkHttpFullRequest presignRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope, + RegionScope regionScope) { + ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); + if (regionScope != null) { + executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope); + } + SdkHttpFullRequest request = testCase.requestBuilder.build(); + return AwsCrtS3V4aSigner.builder().defaultRegionScope(defaultRegionScope).build().presign(request, executionAttributes); + } +} diff --git a/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/AwsCrtV4aSignerSigningScopeTest.java b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/AwsCrtV4aSignerSigningScopeTest.java new file mode 100644 index 000000000000..c6e02efe94a9 --- /dev/null +++ b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/AwsCrtV4aSignerSigningScopeTest.java @@ -0,0 +1,48 @@ +/* + * 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.authcrt.signer.internal; + +import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; +import software.amazon.awssdk.authcrt.signer.AwsCrtV4aSigner; +import software.amazon.awssdk.authcrt.signer.SignerTestUtils; +import software.amazon.awssdk.authcrt.signer.SigningTestCase; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.regions.RegionScope; + +public class AwsCrtV4aSignerSigningScopeTest extends BaseSigningScopeTest { + @Override + protected SdkHttpFullRequest signRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope, + RegionScope regionScope) { + ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); + if (regionScope != null) { + executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope); + } + SdkHttpFullRequest request = testCase.requestBuilder.build(); + return AwsCrtV4aSigner.builder().defaultRegionScope(defaultRegionScope).build().sign(request, executionAttributes); + } + + @Override + protected SdkHttpFullRequest presignRequestWithScope(SigningTestCase testCase, RegionScope defaultRegionScope, + RegionScope regionScope) { + ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); + if (regionScope != null) { + executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, regionScope); + } + SdkHttpFullRequest request = testCase.requestBuilder.build(); + return AwsCrtV4aSigner.builder().defaultRegionScope(defaultRegionScope).build().presign(request, executionAttributes); + } +} diff --git a/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/BaseSigningScopeTest.java b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/BaseSigningScopeTest.java new file mode 100644 index 000000000000..51ac8df6fb8e --- /dev/null +++ b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/BaseSigningScopeTest.java @@ -0,0 +1,113 @@ +/* + * 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.authcrt.signer.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.authcrt.signer.SignerTestUtils; +import software.amazon.awssdk.authcrt.signer.SigningTestCase; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.regions.RegionScope; + +/** + * Functional tests for signing scope handling. These tests call the CRT native signer code. + */ +public abstract class BaseSigningScopeTest { + @Test + public void signing_withDefaultRegionScopeOnly_usesDefaultRegionScope() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, RegionScope.GLOBAL, null); + + String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); + assertThat(regionHeader).isEqualTo(RegionScope.GLOBAL.id()); + } + + @Test + public void presigning_withDefaultRegionScopeOnly_usesDefaultRegionScope() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + SdkHttpFullRequest signedRequest = presignRequestWithScope(testCase, RegionScope.GLOBAL, null); + + assertThat(signedRequest.rawQueryParameters().get("X-Amz-Region-Set")).containsExactly(RegionScope.GLOBAL.id()); + } + + @Test + public void signing_withDefaultScopeAndExplicitScope_usesExplicitScope() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + + String expectdScope = "us-west-2"; + SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, RegionScope.GLOBAL, RegionScope.create(expectdScope)); + + String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); + assertThat(regionHeader).isEqualTo(expectdScope); + } + + @Test + public void presigning_withDefaultScopeAndExplicitScope_usesExplicitScope() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + + String expectdScope = "us-west-2"; + SdkHttpFullRequest signedRequest = presignRequestWithScope(testCase, RegionScope.GLOBAL, + RegionScope.create(expectdScope)); + + assertThat(signedRequest.rawQueryParameters().get("X-Amz-Region-Set")).containsExactly(expectdScope); + } + + @Test + public void signing_withSigningRegionAndRegionScope_usesRegionScope() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, null, RegionScope.GLOBAL); + + String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); + assertThat(regionHeader).isEqualTo(RegionScope.GLOBAL.id()); + } + + @Test + public void presigning_withSigningRegionAndRegionScope_usesRegionScope() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + SdkHttpFullRequest signedRequest = presignRequestWithScope(testCase, null, RegionScope.GLOBAL); + + assertThat(signedRequest.rawQueryParameters().get("X-Amz-Region-Set")).containsExactly(RegionScope.GLOBAL.id()); + } + + @Test + public void signing_withSigningRegionOnly_usesSigningRegion() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + + SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, null, null); + + String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); + assertThat(regionHeader).isEqualTo(Region.AWS_GLOBAL.id()); + } + + @Test + public void presigning_withSigningRegionOnly_usesSigningRegion() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + + SdkHttpFullRequest signedRequest = presignRequestWithScope(testCase, null, null); + + assertThat(signedRequest.rawQueryParameters().get("X-Amz-Region-Set")).containsExactly(Region.AWS_GLOBAL.id()); + } + + protected abstract SdkHttpFullRequest signRequestWithScope(SigningTestCase testCase, + RegionScope defaultRegionScope, + RegionScope regionScope); + + protected abstract SdkHttpFullRequest presignRequestWithScope(SigningTestCase testCase, + RegionScope defaultRegionScope, + RegionScope regionScope); +} diff --git a/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/SigningScopeTest.java b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/SigningScopeTest.java deleted file mode 100644 index 45a52a493240..000000000000 --- a/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/internal/SigningScopeTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.authcrt.signer.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertTrue; - -import java.util.List; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; -import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; -import software.amazon.awssdk.authcrt.signer.AwsCrtS3V4aSigner; -import software.amazon.awssdk.authcrt.signer.AwsCrtV4aSigner; -import software.amazon.awssdk.authcrt.signer.SignerTestUtils; -import software.amazon.awssdk.authcrt.signer.SigningTestCase; -import software.amazon.awssdk.core.interceptor.ExecutionAttributes; -import software.amazon.awssdk.http.SdkHttpFullRequest; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.regions.internal.util.RegionScope; - -/** - * Functional tests for the S3 specific Sigv4a signer. These tests call the CRT native signer code. - */ -@RunWith(MockitoJUnitRunner.class) -public class SigningScopeTest { - - @Test - public void signing_withSigningRegionAndRegionScope_usesRegionScope() { - SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); - SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, RegionScope.GLOBAL.id()); - - String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); - assertThat(regionHeader).isEqualTo(RegionScope.GLOBAL.id()); - } - - @Test - public void signing_withSigningRegionOnly_usesSigningRegion() { - SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); - ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); - SdkHttpFullRequest request = testCase.requestBuilder.build(); - SdkHttpFullRequest signedRequest = AwsCrtV4aSigner.create().sign(request, executionAttributes); - - String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); - assertThat(regionHeader).isEqualTo(Region.AWS_GLOBAL.id()); - } - - @Test - public void signing_chunkedEncoding_regionalScope_present_overrides() { - SigningTestCase testCase = SignerTestUtils.createBasicChunkedSigningTestCase(); - SdkHttpFullRequest signedRequest = signRequestWithScope(testCase, RegionScope.GLOBAL.id()); - - String regionHeader = signedRequest.firstMatchingHeader("X-Amz-Region-Set").get(); - assertThat(regionHeader).isEqualTo(RegionScope.GLOBAL.id()); - } - - @Test - public void testS3Presigning() { - SigningTestCase testCase = SignerTestUtils.createBasicQuerySigningTestCase(); - - SdkHttpFullRequest signedRequest = presignRequestWithScope(testCase, RegionScope.GLOBAL.id()); - - List regionHeader = signedRequest.rawQueryParameters().get("X-Amz-Region-Set"); - assertThat(regionHeader.get(0)).isEqualTo(RegionScope.GLOBAL.id()); - } - - private SdkHttpFullRequest signRequestWithScope(SigningTestCase testCase, String regionScope) { - ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); - executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, - RegionScope.builder().regionScope(regionScope).build()); - - SdkHttpFullRequest request = testCase.requestBuilder.build(); - return AwsCrtS3V4aSigner.create().sign(request, executionAttributes); - } - - private SdkHttpFullRequest presignRequestWithScope(SigningTestCase testCase, String regionScope) { - ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); - executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, - RegionScope.builder().regionScope(regionScope).build()); - SdkHttpFullRequest request = testCase.requestBuilder.build(); - return AwsCrtS3V4aSigner.create().presign(request, executionAttributes); - } - -} diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java index 5981d1bb56fa..ec4afb2552e4 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java @@ -23,7 +23,7 @@ import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.regions.internal.util.RegionScope; +import software.amazon.awssdk.regions.RegionScope; /** * AWS-specific signing attributes attached to the execution. This information is available to {@link ExecutionInterceptor}s and diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/RegionScope.java b/core/regions/src/main/java/software/amazon/awssdk/regions/RegionScope.java similarity index 74% rename from core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/RegionScope.java rename to core/regions/src/main/java/software/amazon/awssdk/regions/RegionScope.java index 0becf7eb0606..5f94b71d9a9a 100644 --- a/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/RegionScope.java +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/RegionScope.java @@ -13,16 +13,14 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.regions.internal.util; +package software.amazon.awssdk.regions; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.utils.Validate; -import software.amazon.awssdk.utils.builder.CopyableBuilder; -import software.amazon.awssdk.utils.builder.ToCopyableBuilder; /** * This class represents the concept of a regional scope, in form of a string with possible wildcards. @@ -45,8 +43,8 @@ *
  • 'eu-we*' - The wildcard must be its own segment
  • * */ -@SdkInternalApi -public final class RegionScope implements ToCopyableBuilder { +@SdkPublicApi +public final class RegionScope { public static final RegionScope GLOBAL; @@ -55,26 +53,16 @@ public final class RegionScope implements ToCopyableBuilder { - private String regionScope; - - private Builder() { - } - - public void setRegionScope(String regionScope) { - regionScope(regionScope); - } - - public Builder regionScope(String regionScope) { - this.regionScope = regionScope; - return this; - } - - @Override - public RegionScope build() { - return new RegionScope(this); - } - } } diff --git a/core/regions/src/test/java/software/amazon/awssdk/regions/internal/RegionScopeTest.java b/core/regions/src/test/java/software/amazon/awssdk/regions/internal/RegionScopeTest.java index 6ce2646b15d6..ca7ae2d8d635 100644 --- a/core/regions/src/test/java/software/amazon/awssdk/regions/internal/RegionScopeTest.java +++ b/core/regions/src/test/java/software/amazon/awssdk/regions/internal/RegionScopeTest.java @@ -22,7 +22,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import software.amazon.awssdk.regions.internal.util.RegionScope; +import software.amazon.awssdk.regions.RegionScope; @RunWith(Parameterized.class) public class RegionScopeTest { @@ -36,7 +36,7 @@ public RegionScopeTest(TestCase testCase) { @Test public void validateRegionScope() { try { - RegionScope regionScope = RegionScope.of(testCase.regionScope); + RegionScope regionScope = RegionScope.create(testCase.regionScope); assertThat(regionScope.id()).isEqualTo(testCase.regionScope); } catch (RuntimeException e) { if (testCase.expectedException == null) { diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java index 4433aaeda953..e67c67331ad6 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java @@ -25,7 +25,7 @@ import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.http.SdkHttpRequest; -import software.amazon.awssdk.regions.internal.util.RegionScope; +import software.amazon.awssdk.regions.RegionScope; import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.internal.ConfiguredS3SdkHttpRequest; import software.amazon.awssdk.services.s3.internal.endpoints.S3EndpointResolverContext;