diff --git a/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java b/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java index f101495eb66..0c333affca9 100644 --- a/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java @@ -1,5 +1,8 @@ package org.prebid.server.spring.config; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.log.Logger; +import org.prebid.server.log.LoggerFactory; import io.vertx.core.Vertx; import io.vertx.core.file.FileSystem; import lombok.Data; @@ -40,9 +43,13 @@ import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; +import software.amazon.awssdk.core.exception.SdkClientException; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; @@ -58,6 +65,8 @@ @UtilityClass public class SettingsConfiguration { + private static final Logger logger = LoggerFactory.getLogger(SettingsConfiguration.class); + @Configuration @ConditionalOnProperty(prefix = "settings.filesystem", name = {"settings-filename", "stored-requests-dir", "stored-imps-dir"}) @@ -233,18 +242,30 @@ static class S3SettingsConfiguration { @Component @ConfigurationProperties(prefix = "settings.s3") - @ConditionalOnProperty(prefix = "settings.s3", name = {"accessKeyId", "secretAccessKey"}) @Validated @Data @NoArgsConstructor protected static class S3ConfigurationProperties { - @NotBlank + /** + * If accessKeyId and secretAccessKey are provided in the + * configuration file then they will be used. Otherwise, the + * DefaultCredentialsProvider will look for credentials in this order: + * + * - Java System Properties + * - Environment Variables + * - Web Identity Token + * - AWS credentials file (~/.aws/credentials) + * - ECS container credentials + * - EC2 instance profile + */ private String accessKeyId; - - @NotBlank private String secretAccessKey; + private boolean useStaticCredentials() { + return StringUtils.isNotBlank(accessKeyId) && StringUtils.isNotBlank(secretAccessKey); + } + /** * If not provided AWS_GLOBAL will be used as a region */ @@ -274,20 +295,32 @@ protected static class S3ConfigurationProperties { @Bean S3AsyncClient s3AsyncClient(S3ConfigurationProperties s3ConfigurationProperties) throws URISyntaxException { - final AwsBasicCredentials credentials = AwsBasicCredentials.create( - s3ConfigurationProperties.getAccessKeyId(), - s3ConfigurationProperties.getSecretAccessKey()); + final Region awsRegion = Optional.ofNullable(s3ConfigurationProperties.getRegion()) .map(Region::of) .orElse(Region.AWS_GLOBAL); - return S3AsyncClient - .builder() - .credentialsProvider(StaticCredentialsProvider.create(credentials)) + final S3AsyncClientBuilder clientBuilder = S3AsyncClient.builder() .endpointOverride(new URI(s3ConfigurationProperties.getEndpoint())) .forcePathStyle(s3ConfigurationProperties.getForcePathStyle()) - .region(awsRegion) - .build(); + .region(awsRegion); + final AwsCredentialsProvider credentialsProvider; + if (s3ConfigurationProperties.useStaticCredentials()) { + final AwsBasicCredentials basicCredentials = AwsBasicCredentials.create( + s3ConfigurationProperties.getAccessKeyId(), + s3ConfigurationProperties.getSecretAccessKey()); + credentialsProvider = StaticCredentialsProvider.create(basicCredentials); + } else { + credentialsProvider = DefaultCredentialsProvider.create(); + } + try { + credentialsProvider.resolveCredentials(); + } catch (SdkClientException e) { + logger.error("Failed to resolve AWS credentials", e); + } + clientBuilder.credentialsProvider(credentialsProvider); + + return clientBuilder.build(); } @Bean diff --git a/src/main/java/org/prebid/server/spring/config/SettingsConfigurationTest.java b/src/main/java/org/prebid/server/spring/config/SettingsConfigurationTest.java new file mode 100644 index 00000000000..af74e48460c --- /dev/null +++ b/src/main/java/org/prebid/server/spring/config/SettingsConfigurationTest.java @@ -0,0 +1,74 @@ +package org.prebid.server.spring.config; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.services.s3.S3AsyncClient; + +import java.net.URISyntaxException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SettingsConfigurationTest { + + @Test + public void useStaticCredentialsShouldReturnTrueWhenStaticCredentialsAreUsed() { + // given + SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties s3Properties = new SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties(); + s3Properties.setAccessKeyId("testAccessKeyId"); + s3Properties.setSecretAccessKey("testSecretAccessKey"); + + // when + final boolean result = s3Properties.useStaticCredentials(); + + // then + assertThat(result).isTrue(); + } + + @Test + public void useStaticCredentialsShouldReturnFalseWhenStaticCredentialsAreNotUsed() { + // given + SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties s3Properties = new SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties(); + + // when + final boolean result = s3Properties.useStaticCredentials(); + + // then + assertThat(result).isFalse(); + } + + @Test + void shouldCreateClientWhenStaticCredentialsNotUsed() throws URISyntaxException { + // given + SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties s3Properties = new SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties(); + s3Properties.setEndpoint("http://example.com"); + s3Properties.setForcePathStyle(true); + s3Properties.setRegion("us-east-1"); + // Not setting access key and secret key properties should + // cause the S3AsyncClient to use DefaultCredentialsProvider. + + // when + S3AsyncClient client = new SettingsConfiguration.S3SettingsConfiguration().s3AsyncClient(s3Properties); + + // then + assertThat(client).isNotNull(); + } + + @Test + void shouldCreateClientWhenStaticCredentialsUsed() throws URISyntaxException { + // given + SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties s3Properties = new SettingsConfiguration.S3SettingsConfiguration.S3ConfigurationProperties(); + s3Properties.setEndpoint("http://localhost:4566"); + s3Properties.setForcePathStyle(true); + s3Properties.setRegion("us-east-1"); + // Setting access key and secret key properties should cause + // the S3AsyncClient to use StaticCredentialsProvider. + s3Properties.setAccessKeyId("testAccessKeyId"); + s3Properties.setSecretAccessKey("testSecretAccessKey"); + + // when + S3AsyncClient client = new SettingsConfiguration.S3SettingsConfiguration().s3AsyncClient(s3Properties); + + // then + assertThat(client).isNotNull(); + } + +}