From bbd8aca58da46645e85d1b9e22e04a7571cb2429 Mon Sep 17 00:00:00 2001 From: MatejNedic Date: Fri, 4 Oct 2024 12:09:11 +0200 Subject: [PATCH] S3 access grants plugin auto-configuration (#1247) --- docs/src/main/asciidoc/s3.adoc | 15 +++++++++++ spring-cloud-aws-autoconfigure/pom.xml | 5 ++++ .../autoconfigure/s3/S3AutoConfiguration.java | 7 +++++ .../s3/properties/S3PluginProperties.java | 18 +++++++++++++ .../s3/properties/S3Properties.java | 11 ++++++++ .../autoconfigure/ConfiguredAwsClient.java | 5 ++++ .../s3/S3AutoConfigurationTests.java | 27 +++++++++++++++++++ spring-cloud-aws-dependencies/pom.xml | 7 +++++ 8 files changed, 95 insertions(+) create mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3PluginProperties.java diff --git a/docs/src/main/asciidoc/s3.adoc b/docs/src/main/asciidoc/s3.adoc index e5c3af98c..055a33c1d 100644 --- a/docs/src/main/asciidoc/s3.adoc +++ b/docs/src/main/asciidoc/s3.adoc @@ -186,6 +186,21 @@ public class SimpleResourceLoadingBean { Resolving resources throughout all buckets can be very time consuming depending on the number of buckets a user owns. ==== +=== Using S3 Access grants + +Sometimes there is a need to make access control to S3 bucket contents fine grained. +Since IAM polices and S3 Policies only support 10kbs size, S3 Access Grant is solving this by allowing fine grained access control over content in bucket. + +To use S3 Access Grants out of the box with `S3Client` and `S3Template` introduce following plugin: + +[source,xml] +---- + + software.amazon.s3.accessgrants + aws-s3-accessgrants-java-plugin + +---- + === Using S3Template Spring Cloud AWS provides a higher abstraction on the top of `S3Client` providing methods for the most common use cases when working with S3. diff --git a/spring-cloud-aws-autoconfigure/pom.xml b/spring-cloud-aws-autoconfigure/pom.xml index e841d3b88..7f17e16ee 100644 --- a/spring-cloud-aws-autoconfigure/pom.xml +++ b/spring-cloud-aws-autoconfigure/pom.xml @@ -91,6 +91,11 @@ amazon-dax-client true + + software.amazon.s3.accessgrants + aws-s3-accessgrants-java-plugin + true + software.amazon.awssdk s3-transfer-manager diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfiguration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfiguration.java index ceb0295d0..dee0e3234 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfiguration.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfiguration.java @@ -43,8 +43,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.util.ClassUtils; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.regions.providers.AwsRegionProvider; +import software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.presigner.S3Presigner; @@ -78,6 +80,11 @@ S3ClientBuilder s3ClientBuilder(AwsClientBuilderConfigurer awsClientBuilderConfi connectionDetails.getIfAvailable(), configurer.getIfAvailable(), s3ClientCustomizers.orderedStream(), awsSyncClientCustomizers.orderedStream()); + if (ClassUtils.isPresent("software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin", null)) { + S3AccessGrantsPlugin s3AccessGrantsPlugin = S3AccessGrantsPlugin.builder() + .enableFallback(properties.getPlugin().getEnableFallback()).build(); + builder.addPlugin(s3AccessGrantsPlugin); + } Optional.ofNullable(this.properties.getCrossRegionEnabled()).ifPresent(builder::crossRegionAccessEnabled); builder.serviceConfiguration(this.properties.toS3Configuration()); diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3PluginProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3PluginProperties.java new file mode 100644 index 000000000..c636d89db --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3PluginProperties.java @@ -0,0 +1,18 @@ +package io.awspring.cloud.autoconfigure.s3.properties; + +public class S3PluginProperties { + + /** + * If set to false if Access Grants does not find/return permissions, S3Client won't try to determine if policies + * grant access If set to true fallback policies S3/IAM will be evaluated. + */ + private boolean enableFallback; + + public boolean getEnableFallback() { + return enableFallback; + } + + public void setEnableFallback(boolean enableFallback) { + this.enableFallback = enableFallback; + } +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3Properties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3Properties.java index 5a422c843..307caeac9 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3Properties.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3Properties.java @@ -93,6 +93,9 @@ public class S3Properties extends AwsClientProperties { @NestedConfigurationProperty private S3CrtClientProperties crt; + @NestedConfigurationProperty + private S3PluginProperties plugin = new S3PluginProperties(); + @Nullable public Boolean getAccelerateModeEnabled() { return this.accelerateModeEnabled; @@ -175,4 +178,12 @@ public S3Configuration toS3Configuration() { propertyMapper.from(this::getUseArnRegionEnabled).whenNonNull().to(config::useArnRegionEnabled); return config.build(); } + + public S3PluginProperties getPlugin() { + return plugin; + } + + public void setPlugin(S3PluginProperties plugin) { + this.plugin = plugin; + } } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/ConfiguredAwsClient.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/ConfiguredAwsClient.java index a4cb66ec7..ed32385ab 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/ConfiguredAwsClient.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/ConfiguredAwsClient.java @@ -27,6 +27,7 @@ import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.utils.AttributeMap; @@ -82,6 +83,10 @@ public Boolean getDualstackEnabled() { return clientConfigurationAttributes.get(AwsClientOption.DUALSTACK_ENDPOINT_ENABLED); } + public IdentityProvider getIdentityProviders() { + return clientConfigurationAttributes.get(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER); + } + public DefaultsMode getDefaultsMode() { return clientConfigurationAttributes.get(AwsClientOption.DEFAULTS_MODE); } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfigurationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfigurationTests.java index 487496418..edfb39392 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfigurationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3AutoConfigurationTests.java @@ -52,6 +52,8 @@ import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsIdentityProvider; +import software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.presigner.S3Presigner; @@ -69,6 +71,30 @@ class S3AutoConfigurationTests { .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)); + private final ApplicationContextRunner contextRunnerWithoutGrant = new ApplicationContextRunner() + .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") + .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, + CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(S3AccessGrantsPlugin.class)); + + @Test + void setsS3AccessGrantIdentityProvider() { + contextRunner.run(context -> { + S3ClientBuilder builder = context.getBean(S3ClientBuilder.class); + ConfiguredAwsClient client = new ConfiguredAwsClient(builder.build()); + assertThat(client.getIdentityProviders()).isInstanceOf(S3AccessGrantsIdentityProvider.class); + }); + } + + @Test + void doesNotSetS3AccessGrantIdentityProvider() { + contextRunnerWithoutGrant.run(context -> { + S3ClientBuilder builder = context.getBean(S3ClientBuilder.class); + ConfiguredAwsClient client = new ConfiguredAwsClient(builder.build()); + assertThat(client.getIdentityProviders()).isNotInstanceOf(S3AccessGrantsIdentityProvider.class); + }); + } + @Test void createsS3ClientBean() { this.contextRunner.run(context -> { @@ -149,6 +175,7 @@ void withCustomGlobalEndpointAndS3Endpoint() { "spring.cloud.aws.s3.endpoint:http://localhost:9999").run(context -> { S3ClientBuilder builder = context.getBean(S3ClientBuilder.class); ConfiguredAwsClient client = new ConfiguredAwsClient(builder.build()); + assertThat(client.getIdentityProviders()).isInstanceOf(S3AccessGrantsIdentityProvider.class); assertThat(client.getEndpoint()).isEqualTo(URI.create("http://localhost:9999")); assertThat(client.isEndpointOverridden()).isTrue(); }); diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml index b245d9c09..0c3d7d77f 100644 --- a/spring-cloud-aws-dependencies/pom.xml +++ b/spring-cloud-aws-dependencies/pom.xml @@ -33,6 +33,7 @@ 1.14.9 1.2.3 3.3.1 + 2.2.0 @@ -68,6 +69,12 @@ true + + software.amazon.s3.accessgrants + aws-s3-accessgrants-java-plugin + ${amazon.s3.accessgrants} + true + software.amazon.awssdk s3-transfer-manager