diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java index 8e056a7dabd5..1d2c33b98e64 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.core.ClientType; import software.amazon.awssdk.core.ServiceConfiguration; @@ -129,6 +130,12 @@ public final class SdkClientOption extends ClientOption { */ public static final SdkClientOption PROFILE_FILE = new SdkClientOption<>(ProfileFile.class); + /** + * The profile file supplier to use for this client. + */ + public static final SdkClientOption> PROFILE_FILE_SUPPLIER = + new SdkClientOption<>(new UnsafeValueType(Supplier.class)); + /** * The profile name to use for this client. */ diff --git a/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java b/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java index 1dd6218801b1..d939bdbc389d 100644 --- a/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java +++ b/services/polly/src/main/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresigner.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Stream; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.auth.credentials.AwsCredentials; @@ -68,7 +69,7 @@ public final class DefaultPollyPresigner implements PollyPresigner { private static final String SERVICE_NAME = "polly"; private static final Aws4Signer DEFAULT_SIGNER = Aws4Signer.create(); - private final ProfileFile profileFile; + private final Supplier profileFile; private final String profileName; private final Region region; private final AwsCredentialsProvider credentialsProvider; @@ -77,11 +78,11 @@ public final class DefaultPollyPresigner implements PollyPresigner { private final Boolean fipsEnabled; private DefaultPollyPresigner(BuilderImpl builder) { - this.profileFile = ProfileFile.defaultProfileFile(); + this.profileFile = ProfileFile::defaultProfileFile; this.profileName = ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow(); this.region = builder.region != null ? builder.region : DefaultAwsRegionProviderChain.builder() - .profileFile(() -> profileFile) + .profileFile(profileFile) .profileName(profileName) .build() .getRegion(); @@ -93,14 +94,14 @@ private DefaultPollyPresigner(BuilderImpl builder) { this.endpointOverride = builder.endpointOverride; this.dualstackEnabled = builder.dualstackEnabled != null ? builder.dualstackEnabled : DualstackEnabledProvider.builder() - .profileFile(() -> profileFile) + .profileFile(profileFile) .profileName(profileName) .build() .isDualstackEnabled() .orElse(false); this.fipsEnabled = builder.fipsEnabled != null ? builder.fipsEnabled : FipsEnabledProvider.builder() - .profileFile(() -> profileFile) + .profileFile(profileFile) .profileName(profileName) .build() .isFipsEnabled() @@ -245,7 +246,7 @@ private URI resolveEndpoint() { return new DefaultServiceEndpointBuilder(SERVICE_NAME, "https") .withRegion(region()) - .withProfileFile(() -> profileFile) + .withProfileFile(profileFile) .withProfileName(profileName) .withDualstackEnabled(dualstackEnabled) .withFipsEnabled(fipsEnabled) diff --git a/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java b/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java index f5a17a07b9bd..4d5cc4973ff3 100644 --- a/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java +++ b/services/polly/src/test/java/software/amazon/awssdk/services/polly/internal/presigner/DefaultPollyPresignerTest.java @@ -41,7 +41,7 @@ /** * Tests for {@link DefaultPollyPresigner}. */ -public class DefaultPollyPresignerTest { +class DefaultPollyPresignerTest { private static final SynthesizeSpeechRequest BASIC_SYNTHESIZE_SPEECH_REQUEST = SynthesizeSpeechRequest.builder() .voiceId("Salli") .outputFormat(OutputFormat.PCM) @@ -56,7 +56,7 @@ public void methodSetup() { } @Test - public void presign_requestLevelCredentials_honored() { + void presign_requestLevelCredentials_honored() { AwsCredentials requestCredentials = AwsBasicCredentials.create("akid2", "skid2"); PollyPresigner presigner = DefaultPollyPresigner.builder() @@ -80,7 +80,7 @@ public void presign_requestLevelCredentials_honored() { } @Test - public void presign_requestLevelHeaders_included() { + void presign_requestLevelHeaders_included() { PollyPresigner presigner = DefaultPollyPresigner.builder() .region(Region.US_EAST_1) .credentialsProvider(credentialsProvider) @@ -104,7 +104,7 @@ public void presign_requestLevelHeaders_included() { } @Test - public void presign_includesRequestLevelHeaders_notBrowserCompatible() { + void presign_includesRequestLevelHeaders_notBrowserCompatible() { PollyPresigner presigner = DefaultPollyPresigner.builder() .region(Region.US_EAST_1) .credentialsProvider(credentialsProvider) @@ -128,7 +128,7 @@ public void presign_includesRequestLevelHeaders_notBrowserCompatible() { } @Test - public void presign_includesRequestLevelQueryParams_included() { + void presign_includesRequestLevelQueryParams_included() { PollyPresigner presigner = DefaultPollyPresigner.builder() .region(Region.US_EAST_1) .credentialsProvider(credentialsProvider) @@ -151,7 +151,7 @@ public void presign_includesRequestLevelQueryParams_included() { } @Test - public void presign_endpointOverriden() { + void presign_endpointOverriden() { PollyPresigner presigner = DefaultPollyPresigner.builder() .region(Region.US_EAST_1) .credentialsProvider(credentialsProvider) @@ -173,7 +173,7 @@ public void presign_endpointOverriden() { } @Test - public void close_closesCustomCloseableCredentialsProvider() throws IOException { + void close_closesCustomCloseableCredentialsProvider() throws IOException { TestCredentialsProvider mockCredentialsProvider = mock(TestCredentialsProvider.class); PollyPresigner presigner = DefaultPollyPresigner.builder() @@ -186,6 +186,19 @@ public void close_closesCustomCloseableCredentialsProvider() throws IOException verify(mockCredentialsProvider).close(); } + @Test + void presigner_credentialsProviderSetToNullByBuilder_createsDefaultCredentialsProvider() { + DefaultPollyPresigner presigner = (DefaultPollyPresigner) + DefaultPollyPresigner.builder() + .region(Region.US_EAST_1) + .credentialsProvider(null) + .build(); + + + AwsCredentialsProvider awsCredentialsProvider = presigner.credentialsProvider(); + assertThat(awsCredentialsProvider).isNotNull(); + } + private interface TestCredentialsProvider extends AwsCredentialsProvider, Closeable { } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolver.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolver.java index 568b47c085cd..7f7b227f5e79 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolver.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolver.java @@ -44,7 +44,9 @@ public UseGlobalEndpointResolver(SdkClientConfiguration config) { String defaultS3UsEast1RegionalEndpointFromSmartDefaults = config.option(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT); this.useUsEast1RegionalEndpoint = - new Lazy<>(() -> useUsEast1RegionalEndpoint(() -> config.option(SdkClientOption.PROFILE_FILE), + new Lazy<>(() -> useUsEast1RegionalEndpoint(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER) != null ? + config.option(SdkClientOption.PROFILE_FILE_SUPPLIER) : + () -> config.option(SdkClientOption.PROFILE_FILE), () -> config.option(SdkClientOption.PROFILE_NAME), defaultS3UsEast1RegionalEndpointFromSmartDefaults)); } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java index 73356356bdef..ec79bb132ffe 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java @@ -129,7 +129,7 @@ private DefaultS3Presigner(Builder b) { S3Configuration serviceConfiguration = b.serviceConfiguration != null ? b.serviceConfiguration : S3Configuration.builder() - .profileFile(profileFile()) + .profileFile(profileFileSupplier()) .profileName(profileName()) .checksumValidationEnabled(false) .build(); @@ -214,7 +214,7 @@ private SdkClientConfiguration createClientConfiguration() { } else { URI defaultEndpoint = new DefaultServiceEndpointBuilder(SERVICE_NAME, "https") .withRegion(region()) - .withProfileFile(this::profileFile) + .withProfileFile(profileFileSupplier()) .withProfileName(profileName()) .withDualstackEnabled(serviceConfiguration.dualstackEnabled()) .withFipsEnabled(fipsEnabled()) @@ -533,7 +533,7 @@ private UseGlobalEndpointResolver createUseGlobalEndpointResolver() { SdkClientConfiguration config = clientConfiguration.toBuilder() .option(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, legacyOption) - .option(SdkClientOption.PROFILE_FILE, profileFile()) + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, profileFileSupplier()) .option(SdkClientOption.PROFILE_NAME, profileName()) .build(); diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultSdkPresigner.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultSdkPresigner.java index 4b8a78ed44ed..b6df6e29fe7b 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultSdkPresigner.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultSdkPresigner.java @@ -16,6 +16,7 @@ package software.amazon.awssdk.services.s3.internal.signing; import java.net.URI; +import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; @@ -30,14 +31,14 @@ /** * The base class implementing the {@link SdkPresigner} interface. - *

+ *

* TODO: This should get moved to aws-core (or split and moved to sdk-core and aws-core) when we support presigning from * multiple services. * TODO: After moving, this should get marked as an @SdkProtectedApi. */ @SdkInternalApi public abstract class DefaultSdkPresigner implements SdkPresigner { - private final ProfileFile profileFile; + private final Supplier profileFile; private final String profileName; private final Region region; private final URI endpointOverride; @@ -46,10 +47,10 @@ public abstract class DefaultSdkPresigner implements SdkPresigner { private final boolean fipsEnabled; protected DefaultSdkPresigner(Builder b) { - this.profileFile = ProfileFile.defaultProfileFile(); + this.profileFile = ProfileFile::defaultProfileFile; this.profileName = ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow(); this.region = b.region != null ? b.region : DefaultAwsRegionProviderChain.builder() - .profileFile(() -> profileFile) + .profileFile(profileFile) .profileName(profileName) .build() .getRegion(); @@ -61,21 +62,21 @@ protected DefaultSdkPresigner(Builder b) { this.endpointOverride = b.endpointOverride; this.dualstackEnabled = b.dualstackEnabled != null ? b.dualstackEnabled : DualstackEnabledProvider.builder() - .profileFile(() -> profileFile) + .profileFile(profileFile) .profileName(profileName) .build() .isDualstackEnabled() .orElse(null); this.fipsEnabled = b.fipsEnabled != null ? b.fipsEnabled : FipsEnabledProvider.builder() - .profileFile(() -> profileFile) + .profileFile(profileFile) .profileName(profileName) .build() .isFipsEnabled() .orElse(false); } - protected ProfileFile profileFile() { + protected Supplier profileFileSupplier() { return profileFile; } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolverProfileFileTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolverProfileFileTest.java new file mode 100644 index 000000000000..dd7ff6106ba4 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolverProfileFileTest.java @@ -0,0 +1,183 @@ +/* + * 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.services.s3.internal.endpoints; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; +import software.amazon.awssdk.utils.StringInputStream; + +class UseGlobalEndpointResolverProfileFileTest { + + @Test + void resolve_nonUsEast1_resolvesToFalse() { + SdkClientConfiguration config = SdkClientConfiguration.builder().build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.AF_SOUTH_1)).isEqualTo(false); + } + + @Test + void resolve_profileFileRegionalEndpointLegacy_resolvesFromPropertyAsTrue() { + ProfileFile file = configuration("[profile regional_s3_endpoint]\n" + + "s3_us_east_1_regional_endpoint = legacy"); + + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(SdkClientOption.PROFILE_FILE, file) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(true); + } + + @Test + void resolve_profileFileRegionalEndpointRegional_resolvesFromPropertyAsFalse() { + ProfileFile file = configuration("[profile regional_s3_endpoint]\n" + + "s3_us_east_1_regional_endpoint = regional"); + + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(SdkClientOption.PROFILE_FILE, file) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(false); + } + + @Test + void resolve_nullProfileFileSupplierAndNullProfileFileAndNullDefaultRegionalEndpoint_resolvesToTrue() { + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, null) + .option(SdkClientOption.PROFILE_FILE, null) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(true); + } + + @Test + void resolve_nullProfileFileSupplierAndNullProfileFileAndDefaultRegionalEndPointLegacy_resolvesToTrue() { + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, "legacy") + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, null) + .option(SdkClientOption.PROFILE_FILE, null) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(true); + } + + @Test + void resolve_nullProfileFileSupplierAndNullProfileFileAndDefaultRegionalEndPointRegional_resolvesToFalse() { + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(ServiceMetadataAdvancedOption.DEFAULT_S3_US_EAST_1_REGIONAL_ENDPOINT, "regional") + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, null) + .option(SdkClientOption.PROFILE_FILE, null) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(false); + } + + @Test + void resolve_nullProfileFileSupplierAndNonNullProfileFileEndpointLegacy_resolvesToTrue() { + Supplier supplier = null; + ProfileFile file = configuration("[profile regional_s3_endpoint]\n" + + "s3_us_east_1_regional_endpoint = legacy"); + + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, supplier) + .option(SdkClientOption.PROFILE_FILE, file) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(true); + } + + @Test + void resolve_nullProfileFileSupplierAndNonNullProfileFileEndpointLegacy_resolvesToFalse() { + Supplier supplier = null; + ProfileFile file = configuration("[profile regional_s3_endpoint]\n" + + "s3_us_east_1_regional_endpoint = regional"); + + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, supplier) + .option(SdkClientOption.PROFILE_FILE, file) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(false); + } + + @Test + void resolve_profileFileSupplierRegionalEndpointLegacy_resolvesToTrue() { + ProfileFile file = configuration("[profile regional_s3_endpoint]\n" + + "s3_us_east_1_regional_endpoint = legacy"); + Supplier supplier = () -> file; + + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, supplier) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(true); + } + + @Test + void resolve_profileFileSupplierRegionalEndpointRegional_resolvesToFalse() { + ProfileFile file = configuration("[profile regional_s3_endpoint]\n" + + "s3_us_east_1_regional_endpoint = regional"); + Supplier supplier = () -> file; + + SdkClientConfiguration config = SdkClientConfiguration + .builder() + .option(SdkClientOption.PROFILE_FILE_SUPPLIER, supplier) + .option(SdkClientOption.PROFILE_NAME, "regional_s3_endpoint") + .build(); + UseGlobalEndpointResolver resolver = new UseGlobalEndpointResolver(config); + + assertThat(resolver.resolve(Region.US_EAST_1)).isEqualTo(false); + } + + private ProfileFile configuration(String string) { + return ProfileFile.builder() + .content(new StringInputStream(string)) + .type(ProfileFile.Type.CONFIGURATION) + .build(); + } + +} diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolverTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolverTest.java index 3588c07f0dd2..3b293e951a42 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolverTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/endpoints/UseGlobalEndpointResolverTest.java @@ -17,11 +17,14 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.net.URI; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; +import java.util.function.Supplier; +import org.apache.hc.core5.http.Chars; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,10 +34,9 @@ import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.regions.ServiceMetadata; import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; -import software.amazon.awssdk.regions.servicemetadata.EnhancedS3ServiceMetadata; import software.amazon.awssdk.testutils.EnvironmentVariableHelper; +import software.amazon.awssdk.utils.StringInputStream; import software.amazon.awssdk.utils.Validate; @RunWith(Parameterized.class)