From f7fa715067487dc4a9f92b66169a56d132da26ec Mon Sep 17 00:00:00 2001 From: matejnedic Date: Tue, 30 Jan 2024 21:45:51 +0100 Subject: [PATCH 01/19] Initial commit --- spring-cloud-aws-autoconfigure/pom.xml | 5 ++ .../autoconfigure/s3/S3AutoConfiguration.java | 49 ++++++++++- .../s3/properties/S3EncryptionProperties.java | 88 +++++++++++++++++++ .../s3/properties/S3EncryptionType.java | 33 +++++++ .../s3/properties/S3Properties.java | 17 ++++ .../s3/S3AutoConfigurationTests.java | 22 ++++- spring-cloud-aws-dependencies/pom.xml | 8 ++ 7 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java create mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java diff --git a/spring-cloud-aws-autoconfigure/pom.xml b/spring-cloud-aws-autoconfigure/pom.xml index 60e8ba41e..fa84b1a35 100644 --- a/spring-cloud-aws-autoconfigure/pom.xml +++ b/spring-cloud-aws-autoconfigure/pom.xml @@ -154,5 +154,10 @@ sts true + + software.amazon.encryption.s3 + amazon-s3-encryption-client-java + true + 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 a6c1a219b..51c9dfce3 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 @@ -19,6 +19,8 @@ import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer; import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer; import io.awspring.cloud.autoconfigure.core.AwsProperties; +import io.awspring.cloud.autoconfigure.s3.properties.S3EncryptionProperties; +import io.awspring.cloud.autoconfigure.s3.properties.S3EncryptionType; import io.awspring.cloud.autoconfigure.s3.properties.S3Properties; import io.awspring.cloud.s3.InMemoryBufferingS3OutputStreamProvider; import io.awspring.cloud.s3.Jackson2JsonS3ObjectConverter; @@ -30,7 +32,10 @@ import io.awspring.cloud.s3.S3ProtocolResolver; import io.awspring.cloud.s3.S3Template; import io.awspring.cloud.s3.crossregion.CrossRegionS3Client; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; import java.util.Optional; +import javax.crypto.KeyGenerator; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -40,19 +45,23 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.context.properties.PropertyMapper; 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.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.encryption.s3.S3EncryptionClient; /** * {@link EnableAutoConfiguration} for {@link S3Client} and {@link S3ProtocolResolver}. * * @author Maciej Walkowiak + * @author Matej Nedic */ @AutoConfiguration @ConditionalOnClass({ S3Client.class, S3OutputStreamProvider.class }) @@ -125,10 +134,48 @@ static class StandardS3ClientConfiguration { @Bean @ConditionalOnMissingBean - S3Client s3Client(S3ClientBuilder s3ClientBuilder) { + S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder) throws NoSuchAlgorithmException { + if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null)) { + return s3EncClient(properties, s3ClientBuilder); + } return s3ClientBuilder.build(); } + S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder) throws NoSuchAlgorithmException { + PropertyMapper propertyMapper = PropertyMapper.get(); + S3EncryptionProperties encryptionProperties = properties.getEncryption(); + S3EncryptionClient.Builder s3EncryptionBuilder = S3EncryptionClient.builder(); + + s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); + propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) + .to(s3EncryptionBuilder::enableDelayedAuthenticationMode); + propertyMapper.from(encryptionProperties::isEnableLegacyUnauthenticatedModes) + .to(s3EncryptionBuilder::enableLegacyUnauthenticatedModes); + propertyMapper.from(encryptionProperties::isEnableMultipartPutObject) + .to(s3EncryptionBuilder::enableMultipartPutObject); + + if (!encryptionProperties.getType().getType().equals(S3EncryptionType.KMS_ID.getType()) + && encryptionProperties.isAutoGenerateKey()) { + if (encryptionProperties.getType().equals(S3EncryptionType.AES)) { + KeyGenerator keyGenerator = KeyGenerator.getInstance(encryptionProperties.getType().getType()); + keyGenerator + .init(encryptionProperties.getKeyLength() > 0 ? encryptionProperties.getKeyLength() : 256); + s3EncryptionBuilder.aesKey(keyGenerator.generateKey()); + } + else { + KeyPairGenerator keyPairGenerator = KeyPairGenerator + .getInstance(encryptionProperties.getType().getType()); + keyPairGenerator.initialize( + encryptionProperties.getKeyLength() > 0 ? encryptionProperties.getKeyLength() : 2048); + s3EncryptionBuilder.rsaKeyPair(keyPairGenerator.generateKeyPair()); + + } + return s3EncryptionBuilder.build(); + } + propertyMapper.from(encryptionProperties::getKmsId).to(s3EncryptionBuilder::kmsKeyId); + return s3EncryptionBuilder.build(); + } + } @Configuration diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java new file mode 100644 index 000000000..450ea3412 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3.properties; + +/** + * Properties to configure {@link software.amazon.encryption.s3.S3EncryptionClient} + * @author Matej Nedic + */ +public class S3EncryptionProperties { + + private boolean autoGenerateKey = false; + private int keyLength; + private boolean enableLegacyUnauthenticatedModes = false; + private boolean enableDelayedAuthenticationMode = false; + private boolean enableMultipartPutObject = false; + private S3EncryptionType type; + + public String getKmsId() { + return kmsId; + } + + public void setKmsId(String kmsId) { + this.kmsId = kmsId; + } + + private String kmsId; + + public boolean isEnableLegacyUnauthenticatedModes() { + return enableLegacyUnauthenticatedModes; + } + + public void setEnableLegacyUnauthenticatedModes(boolean enableLegacyUnauthenticatedModes) { + this.enableLegacyUnauthenticatedModes = enableLegacyUnauthenticatedModes; + } + + public boolean isEnableDelayedAuthenticationMode() { + return enableDelayedAuthenticationMode; + } + + public void setEnableDelayedAuthenticationMode(boolean enableDelayedAuthenticationMode) { + this.enableDelayedAuthenticationMode = enableDelayedAuthenticationMode; + } + + public boolean isEnableMultipartPutObject() { + return enableMultipartPutObject; + } + + public void setEnableMultipartPutObject(boolean enableMultipartPutObject) { + this.enableMultipartPutObject = enableMultipartPutObject; + } + + public S3EncryptionType getType() { + return type; + } + + public void setType(S3EncryptionType type) { + this.type = type; + } + + public boolean isAutoGenerateKey() { + return autoGenerateKey; + } + + public void setAutoGenerateKey(boolean autoGenerateKey) { + this.autoGenerateKey = autoGenerateKey; + } + + public int getKeyLength() { + return keyLength; + } + + public void setKeyLength(int keyLength) { + this.keyLength = keyLength; + } +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java new file mode 100644 index 000000000..a69add95c --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3.properties; + +/** + * @author Matej Nedic + */ +public enum S3EncryptionType { + AES("AES"), RSA("RSA"), KMS_ID("KMS_ID"); + + S3EncryptionType(String type) { + this.type = type; + } + + private String type; + + public String getType() { + return type; + } +} 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..03b8b7eba 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 @@ -23,11 +23,13 @@ import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.internal.crt.S3CrtAsyncClient; import software.amazon.awssdk.transfer.s3.S3TransferManager; +import software.amazon.encryption.s3.S3EncryptionClient; /** * Properties related to AWS S3. * * @author Maciej Walkowiak + * @author Matej Nedic */ @ConfigurationProperties(prefix = S3Properties.PREFIX) public class S3Properties extends AwsClientProperties { @@ -93,6 +95,21 @@ public class S3Properties extends AwsClientProperties { @NestedConfigurationProperty private S3CrtClientProperties crt; + /** + * Configuration properties for {@link S3EncryptionClient} integration + */ + @Nullable + @NestedConfigurationProperty + private S3EncryptionProperties encryption; + + public S3EncryptionProperties getEncryption() { + return encryption; + } + + public void setEncryption(S3EncryptionProperties encryption) { + this.encryption = encryption; + } + @Nullable public Boolean getAccelerateModeEnabled() { return this.accelerateModeEnabled; 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 87034f298..d1bd431ef 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 @@ -56,11 +56,13 @@ import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.encryption.s3.S3EncryptionClient; /** * Tests for {@link S3AutoConfiguration}. * * @author Maciej Walkowiak + * @author Matej Nedic */ class S3AutoConfigurationTests { @@ -118,10 +120,22 @@ void s3ClientCanBeOverwritten() { @Test void createsStandardClientWhenCrossRegionModuleIsNotInClasspath() { - contextRunner.withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)).run(context -> { - assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); - assertThat(context).hasSingleBean(S3Client.class); - }); + contextRunner.withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class, S3EncryptionClient.class)) + .run(context -> { + assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + assertThat(context).hasSingleBean(S3Client.class); + }); + } + + @Test + void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { + contextRunner + .withPropertyValues("spring.cloud.aws.s3.encryption.type:RSA", + "spring.cloud.aws.s3.encryption.autoGenerateKey:true") + .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)).run(context -> { + assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + assertThat(context).hasSingleBean(S3EncryptionClient.class); + }); } } diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml index eed07ec72..91ea183ad 100644 --- a/spring-cloud-aws-dependencies/pom.xml +++ b/spring-cloud-aws-dependencies/pom.xml @@ -27,6 +27,7 @@ 2.31.0 2.21.12 2.0.4 + 3.0.1 1.6 4.1.0 2.1.0 @@ -54,6 +55,13 @@ import + + software.amazon.encryption.s3 + amazon-s3-encryption-client-java + ${amazon.encryption.s3} + true + + software.amazon.dax amazon-dax-client From 69d96fbebcfdf592e9d6eb6b692e9da85fe0f5a8 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Sun, 31 Mar 2024 21:52:03 +0200 Subject: [PATCH 02/19] Refactor integration with S3 --- .idea/vcs.xml | 2 +- spring-cloud-aws-autoconfigure/pom.xml | 5 ++ .../cloud/autoconfigure/s3/S3AesProvider.java | 9 ++++ .../autoconfigure/s3/S3AutoConfiguration.java | 33 +++++-------- .../cloud/autoconfigure/s3/S3RsaProvider.java | 8 ++++ .../s3/properties/S3EncryptionProperties.java | 38 ++------------- .../s3/properties/S3EncryptionType.java | 33 ------------- .../s3/properties/S3Properties.java | 3 +- .../sns/SnsAutoConfiguration.java | 1 + .../s3/S3AutoConfigurationTests.java | 48 +++++++++++++++++-- .../s3/provider/MyAesProvider.java | 19 ++++++++ .../s3/provider/MyRsaProvider.java | 20 ++++++++ 12 files changed, 127 insertions(+), 92 deletions(-) create mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java create mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java delete mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java create mode 100644 spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java create mode 100644 spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4..35eb1ddfb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/spring-cloud-aws-autoconfigure/pom.xml b/spring-cloud-aws-autoconfigure/pom.xml index fa84b1a35..17ee096a6 100644 --- a/spring-cloud-aws-autoconfigure/pom.xml +++ b/spring-cloud-aws-autoconfigure/pom.xml @@ -154,6 +154,11 @@ sts true + + software.amazon.awssdk + kms + true + software.amazon.encryption.s3 amazon-s3-encryption-client-java diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java new file mode 100644 index 000000000..51b10b586 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java @@ -0,0 +1,9 @@ +package io.awspring.cloud.autoconfigure.s3; + +import javax.crypto.SecretKey; +import java.security.KeyPair; + +public interface S3AesProvider { + + SecretKey generateSecretKey(); +} 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 51c9dfce3..bede39928 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 @@ -20,7 +20,6 @@ import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer; import io.awspring.cloud.autoconfigure.core.AwsProperties; import io.awspring.cloud.autoconfigure.s3.properties.S3EncryptionProperties; -import io.awspring.cloud.autoconfigure.s3.properties.S3EncryptionType; import io.awspring.cloud.autoconfigure.s3.properties.S3Properties; import io.awspring.cloud.s3.InMemoryBufferingS3OutputStreamProvider; import io.awspring.cloud.s3.Jackson2JsonS3ObjectConverter; @@ -32,10 +31,10 @@ import io.awspring.cloud.s3.S3ProtocolResolver; import io.awspring.cloud.s3.S3Template; import io.awspring.cloud.s3.crossregion.CrossRegionS3Client; -import java.security.KeyPairGenerator; + import java.security.NoSuchAlgorithmException; import java.util.Optional; -import javax.crypto.KeyGenerator; + import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -50,6 +49,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.regions.providers.AwsRegionProvider; import software.amazon.awssdk.services.s3.S3Client; @@ -134,14 +134,16 @@ static class StandardS3ClientConfiguration { @Bean @ConditionalOnMissingBean - S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder) throws NoSuchAlgorithmException { + S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, ObjectProvider rsaProvider, + ObjectProvider aesProvider) throws NoSuchAlgorithmException { if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null)) { - return s3EncClient(properties, s3ClientBuilder); + return s3EncClient(properties, s3ClientBuilder, rsaProvider, aesProvider); } return s3ClientBuilder.build(); } - S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder) throws NoSuchAlgorithmException { + S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, ObjectProvider rsaProvider, + ObjectProvider aesProvider) throws NoSuchAlgorithmException { PropertyMapper propertyMapper = PropertyMapper.get(); S3EncryptionProperties encryptionProperties = properties.getEncryption(); S3EncryptionClient.Builder s3EncryptionBuilder = S3EncryptionClient.builder(); @@ -154,25 +156,16 @@ S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder) t propertyMapper.from(encryptionProperties::isEnableMultipartPutObject) .to(s3EncryptionBuilder::enableMultipartPutObject); - if (!encryptionProperties.getType().getType().equals(S3EncryptionType.KMS_ID.getType()) - && encryptionProperties.isAutoGenerateKey()) { - if (encryptionProperties.getType().equals(S3EncryptionType.AES)) { - KeyGenerator keyGenerator = KeyGenerator.getInstance(encryptionProperties.getType().getType()); - keyGenerator - .init(encryptionProperties.getKeyLength() > 0 ? encryptionProperties.getKeyLength() : 256); - s3EncryptionBuilder.aesKey(keyGenerator.generateKey()); + if (!StringUtils.hasText(encryptionProperties.getKeyId())) { + if (aesProvider.getIfAvailable() != null) { + s3EncryptionBuilder.aesKey(aesProvider.getObject().generateSecretKey()); } else { - KeyPairGenerator keyPairGenerator = KeyPairGenerator - .getInstance(encryptionProperties.getType().getType()); - keyPairGenerator.initialize( - encryptionProperties.getKeyLength() > 0 ? encryptionProperties.getKeyLength() : 2048); - s3EncryptionBuilder.rsaKeyPair(keyPairGenerator.generateKeyPair()); - + s3EncryptionBuilder.rsaKeyPair(rsaProvider.getObject().generateKeyPair()); } return s3EncryptionBuilder.build(); } - propertyMapper.from(encryptionProperties::getKmsId).to(s3EncryptionBuilder::kmsKeyId); + propertyMapper.from(encryptionProperties::getKeyId).to(s3EncryptionBuilder::kmsKeyId); return s3EncryptionBuilder.build(); } diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java new file mode 100644 index 000000000..4ebc60107 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java @@ -0,0 +1,8 @@ +package io.awspring.cloud.autoconfigure.s3; + +import java.security.KeyPair; + +public interface S3RsaProvider { + + KeyPair generateKeyPair(); +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java index 450ea3412..e4b910b79 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionProperties.java @@ -20,24 +20,19 @@ * @author Matej Nedic */ public class S3EncryptionProperties { - - private boolean autoGenerateKey = false; - private int keyLength; private boolean enableLegacyUnauthenticatedModes = false; private boolean enableDelayedAuthenticationMode = false; private boolean enableMultipartPutObject = false; - private S3EncryptionType type; + private String keyId; - public String getKmsId() { - return kmsId; + public String getKeyId() { + return keyId; } - public void setKmsId(String kmsId) { - this.kmsId = kmsId; + public void setKeyId(String keyId) { + this.keyId = keyId; } - private String kmsId; - public boolean isEnableLegacyUnauthenticatedModes() { return enableLegacyUnauthenticatedModes; } @@ -62,27 +57,4 @@ public void setEnableMultipartPutObject(boolean enableMultipartPutObject) { this.enableMultipartPutObject = enableMultipartPutObject; } - public S3EncryptionType getType() { - return type; - } - - public void setType(S3EncryptionType type) { - this.type = type; - } - - public boolean isAutoGenerateKey() { - return autoGenerateKey; - } - - public void setAutoGenerateKey(boolean autoGenerateKey) { - this.autoGenerateKey = autoGenerateKey; - } - - public int getKeyLength() { - return keyLength; - } - - public void setKeyLength(int keyLength) { - this.keyLength = keyLength; - } } diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java deleted file mode 100644 index a69add95c..000000000 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/properties/S3EncryptionType.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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 io.awspring.cloud.autoconfigure.s3.properties; - -/** - * @author Matej Nedic - */ -public enum S3EncryptionType { - AES("AES"), RSA("RSA"), KMS_ID("KMS_ID"); - - S3EncryptionType(String type) { - this.type = type; - } - - private String type; - - public String getType() { - return type; - } -} 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 03b8b7eba..fb6af5f2b 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 @@ -98,9 +98,8 @@ public class S3Properties extends AwsClientProperties { /** * Configuration properties for {@link S3EncryptionClient} integration */ - @Nullable @NestedConfigurationProperty - private S3EncryptionProperties encryption; + private S3EncryptionProperties encryption = new S3EncryptionProperties(); public S3EncryptionProperties getEncryption() { return encryption; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java index 7c4fe48ba..a6d2fa767 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java @@ -87,6 +87,7 @@ public SnsSmsTemplate snsSmsTemplate(SnsClient snsClient) { return new SnsSmsTemplate(snsClient); } + @Configuration(proxyBeanMethods = false) @ConditionalOnClass(WebMvcConfigurer.class) static class SnsWebConfiguration { 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 d1bd431ef..be3d5ef30 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 @@ -26,6 +26,8 @@ import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration; import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration; import io.awspring.cloud.autoconfigure.s3.properties.S3Properties; +import io.awspring.cloud.autoconfigure.s3.provider.MyAesProvider; +import io.awspring.cloud.autoconfigure.s3.provider.MyRsaProvider; import io.awspring.cloud.s3.InMemoryBufferingS3OutputStreamProvider; import io.awspring.cloud.s3.ObjectMetadata; import io.awspring.cloud.s3.S3ObjectConverter; @@ -119,7 +121,7 @@ void s3ClientCanBeOverwritten() { } @Test - void createsStandardClientWhenCrossRegionModuleIsNotInClasspath() { + void createsStandardClientWhenCrossRegionAndEncryptionModuleIsNotInClasspath() { contextRunner.withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class, S3EncryptionClient.class)) .run(context -> { assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); @@ -130,13 +132,37 @@ void createsStandardClientWhenCrossRegionModuleIsNotInClasspath() { @Test void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { contextRunner - .withPropertyValues("spring.cloud.aws.s3.encryption.type:RSA", - "spring.cloud.aws.s3.encryption.autoGenerateKey:true") + .withPropertyValues( + "spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)).run(context -> { assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); assertThat(context).hasSingleBean(S3EncryptionClient.class); }); } + + @Test + void createsEncryptionClientBackedByRsa() { + contextRunner + .withPropertyValues() + .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) + .withUserConfiguration(CustomRsaProvider.class).run(context -> { + assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + assertThat(context).hasSingleBean(S3EncryptionClient.class); + assertThat(context).hasSingleBean(S3RsaProvider.class); + }); + } + + @Test + void createsEncryptionClientBackedByAes() { + contextRunner + .withPropertyValues() + .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) + .withUserConfiguration(CustomAesProvider.class).run(context -> { + assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + assertThat(context).hasSingleBean(S3EncryptionClient.class); + assertThat(context).hasSingleBean(S3AesProvider.class); + }); + } } @Nested @@ -326,6 +352,22 @@ S3Client customS3Client() { } + @Configuration + static class CustomRsaProvider { + @Bean + S3RsaProvider rsaProvider() { + return new MyRsaProvider(); + } + } + + @Configuration + static class CustomAesProvider { + @Bean + S3AesProvider aesProvider() { + return new MyAesProvider(); + } + } + @Configuration(proxyBeanMethods = false) static class CustomAwsConfigurerClient { diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java new file mode 100644 index 000000000..a3979a123 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java @@ -0,0 +1,19 @@ +package io.awspring.cloud.autoconfigure.s3.provider; + +import io.awspring.cloud.autoconfigure.s3.S3AesProvider; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +public class MyAesProvider implements S3AesProvider { + @Override + public SecretKey generateSecretKey() { + try { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(256); + return keyGenerator.generateKey(); + } catch (Exception e) { + return null; + } + } +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java new file mode 100644 index 000000000..cad04b1db --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java @@ -0,0 +1,20 @@ +package io.awspring.cloud.autoconfigure.s3.provider; + +import io.awspring.cloud.autoconfigure.s3.S3RsaProvider; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; + +public class MyRsaProvider implements S3RsaProvider { + @Override + public KeyPair generateKeyPair() { + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator + .getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } catch (Exception e) { + return null; + } + } +} From cea4107b41d79957406e9c0a3e04b5d83d1d21fb Mon Sep 17 00:00:00 2001 From: matejnedic Date: Sun, 31 Mar 2024 21:52:56 +0200 Subject: [PATCH 03/19] Spotless --- .idea/vcs.xml | 4 +-- .../cloud/autoconfigure/s3/S3AesProvider.java | 16 +++++++++- .../autoconfigure/s3/S3AutoConfiguration.java | 12 +++---- .../cloud/autoconfigure/s3/S3RsaProvider.java | 15 +++++++++ .../sns/SnsAutoConfiguration.java | 1 - .../s3/S3AutoConfigurationTests.java | 32 ++++++++----------- .../s3/provider/MyAesProvider.java | 21 ++++++++++-- .../s3/provider/MyRsaProvider.java | 22 ++++++++++--- 8 files changed, 87 insertions(+), 36 deletions(-) diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1ddfb..5ace414d8 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + - \ No newline at end of file + diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java index 51b10b586..94d128542 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java @@ -1,7 +1,21 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3; import javax.crypto.SecretKey; -import java.security.KeyPair; public interface S3AesProvider { 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 bede39928..e65df4963 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 @@ -31,10 +31,8 @@ import io.awspring.cloud.s3.S3ProtocolResolver; import io.awspring.cloud.s3.S3Template; import io.awspring.cloud.s3.crossregion.CrossRegionS3Client; - import java.security.NoSuchAlgorithmException; import java.util.Optional; - import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -134,16 +132,18 @@ static class StandardS3ClientConfiguration { @Bean @ConditionalOnMissingBean - S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, ObjectProvider rsaProvider, - ObjectProvider aesProvider) throws NoSuchAlgorithmException { + S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, + ObjectProvider rsaProvider, ObjectProvider aesProvider) + throws NoSuchAlgorithmException { if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null)) { return s3EncClient(properties, s3ClientBuilder, rsaProvider, aesProvider); } return s3ClientBuilder.build(); } - S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, ObjectProvider rsaProvider, - ObjectProvider aesProvider) throws NoSuchAlgorithmException { + S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, + ObjectProvider rsaProvider, ObjectProvider aesProvider) + throws NoSuchAlgorithmException { PropertyMapper propertyMapper = PropertyMapper.get(); S3EncryptionProperties encryptionProperties = properties.getEncryption(); S3EncryptionClient.Builder s3EncryptionBuilder = S3EncryptionClient.builder(); diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java index 4ebc60107..734b2fd5c 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3; import java.security.KeyPair; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java index a6d2fa767..7c4fe48ba 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sns/SnsAutoConfiguration.java @@ -87,7 +87,6 @@ public SnsSmsTemplate snsSmsTemplate(SnsClient snsClient) { return new SnsSmsTemplate(snsClient); } - @Configuration(proxyBeanMethods = false) @ConditionalOnClass(WebMvcConfigurer.class) static class SnsWebConfiguration { 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 be3d5ef30..93a079587 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 @@ -131,9 +131,7 @@ void createsStandardClientWhenCrossRegionAndEncryptionModuleIsNotInClasspath() { @Test void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { - contextRunner - .withPropertyValues( - "spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") + contextRunner.withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)).run(context -> { assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); assertThat(context).hasSingleBean(S3EncryptionClient.class); @@ -142,26 +140,22 @@ void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { @Test void createsEncryptionClientBackedByRsa() { - contextRunner - .withPropertyValues() - .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) - .withUserConfiguration(CustomRsaProvider.class).run(context -> { - assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); - assertThat(context).hasSingleBean(S3EncryptionClient.class); - assertThat(context).hasSingleBean(S3RsaProvider.class); - }); + contextRunner.withPropertyValues().withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) + .withUserConfiguration(CustomRsaProvider.class).run(context -> { + assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + assertThat(context).hasSingleBean(S3EncryptionClient.class); + assertThat(context).hasSingleBean(S3RsaProvider.class); + }); } @Test void createsEncryptionClientBackedByAes() { - contextRunner - .withPropertyValues() - .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) - .withUserConfiguration(CustomAesProvider.class).run(context -> { - assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); - assertThat(context).hasSingleBean(S3EncryptionClient.class); - assertThat(context).hasSingleBean(S3AesProvider.class); - }); + contextRunner.withPropertyValues().withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) + .withUserConfiguration(CustomAesProvider.class).run(context -> { + assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + assertThat(context).hasSingleBean(S3EncryptionClient.class); + assertThat(context).hasSingleBean(S3AesProvider.class); + }); } } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java index a3979a123..a4d407bab 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyAesProvider.java @@ -1,7 +1,21 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3.provider; import io.awspring.cloud.autoconfigure.s3.S3AesProvider; - import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -11,8 +25,9 @@ public SecretKey generateSecretKey() { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(256); - return keyGenerator.generateKey(); - } catch (Exception e) { + return keyGenerator.generateKey(); + } + catch (Exception e) { return null; } } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java index cad04b1db..d9f5bdfcc 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/provider/MyRsaProvider.java @@ -1,7 +1,21 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3.provider; import io.awspring.cloud.autoconfigure.s3.S3RsaProvider; - import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -9,11 +23,11 @@ public class MyRsaProvider implements S3RsaProvider { @Override public KeyPair generateKeyPair() { try { - KeyPairGenerator keyPairGenerator = KeyPairGenerator - .getInstance("RSA"); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); return keyPairGenerator.generateKeyPair(); - } catch (Exception e) { + } + catch (Exception e) { return null; } } From f257ea8368fae8e8c5d921d3365349cca775d2c0 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Wed, 31 Jul 2024 09:38:28 +0200 Subject: [PATCH 04/19] Merge main into branch --- .../autoconfigure/s3/S3AutoConfiguration.java | 3 -- .../s3/S3AutoConfigurationTests.java | 32 ++++++++----------- spring-cloud-aws-dependencies/pom.xml | 7 ++++ 3 files changed, 21 insertions(+), 21 deletions(-) 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 23abcfafd..74c22de38 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 @@ -48,7 +48,6 @@ import org.springframework.context.annotation.Import; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; -import org.springframework.util.ClassUtils; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.regions.providers.AwsRegionProvider; import software.amazon.awssdk.services.s3.S3Client; @@ -156,8 +155,6 @@ S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, return s3EncryptionBuilder.build(); } - } - @Configuration @ConditionalOnClass(ObjectMapper.class) static class Jackson2JsonS3ObjectConverterConfiguration { 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 7a2dd6022..bfe957c08 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 @@ -70,9 +70,15 @@ class S3AutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") + .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)) .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)); + private final ApplicationContextRunner contextRunnerEncryption = new ApplicationContextRunner() + .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") + .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, + CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)); + @Test void createsS3ClientBean() { this.contextRunner.run(context -> { @@ -103,43 +109,34 @@ void autoconfigurationIsNotTriggeredWhenS3ModuleIsNotOnClasspath() { @Nested class S3ClientTests { - @Test - void byDefaultCreatesCrossRegionS3Client() { - contextRunner.run( - context -> assertThat(context).getBean(S3Client.class).isInstanceOf(CrossRegionS3Client.class)); - } - @Test void s3ClientCanBeOverwritten() { - contextRunner.withUserConfiguration(CustomS3ClientConfiguration.class).run(context -> { + contextRunnerEncryption.withUserConfiguration(CustomS3ClientConfiguration.class).run(context -> { assertThat(context).hasSingleBean(S3Client.class); - assertThat(context).getBean(S3Client.class).isNotInstanceOf(CrossRegionS3Client.class); }); } @Test void createsStandardClientWhenCrossRegionAndEncryptionModuleIsNotInClasspath() { - contextRunner.withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class, S3EncryptionClient.class)) + contextRunnerEncryption.withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)) .run(context -> { - assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + assertThat(context).doesNotHaveBean(S3EncryptionClient.class); assertThat(context).hasSingleBean(S3Client.class); }); } @Test void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { - contextRunner.withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") - .withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)).run(context -> { - assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); + contextRunnerEncryption.withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab").run(context -> { + assertThat(context).hasSingleBean(S3EncryptionClient.class); }); } @Test void createsEncryptionClientBackedByRsa() { - contextRunner.withPropertyValues().withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) + contextRunnerEncryption.withPropertyValues() .withUserConfiguration(CustomRsaProvider.class).run(context -> { - assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); assertThat(context).hasSingleBean(S3EncryptionClient.class); assertThat(context).hasSingleBean(S3RsaProvider.class); }); @@ -147,9 +144,8 @@ void createsEncryptionClientBackedByRsa() { @Test void createsEncryptionClientBackedByAes() { - contextRunner.withPropertyValues().withClassLoader(new FilteredClassLoader(CrossRegionS3Client.class)) + contextRunnerEncryption.withPropertyValues() .withUserConfiguration(CustomAesProvider.class).run(context -> { - assertThat(context).doesNotHaveBean(CrossRegionS3Client.class); assertThat(context).hasSingleBean(S3EncryptionClient.class); assertThat(context).hasSingleBean(S3AesProvider.class); }); @@ -219,7 +215,7 @@ void withJacksonOnClasspathAutoconfiguresObjectConverter() { @Test void withoutJacksonOnClasspathDoesNotConfigureObjectConverter() { - contextRunner.withClassLoader(new FilteredClassLoader(ObjectMapper.class)).run(context -> { + contextRunner.withClassLoader(new FilteredClassLoader(ObjectMapper.class, S3EncryptionClient.class)).run(context -> { assertThat(context).doesNotHaveBean(S3ObjectConverter.class); assertThat(context).doesNotHaveBean(S3Template.class); }); diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml index 6bc350b75..e05a09104 100644 --- a/spring-cloud-aws-dependencies/pom.xml +++ b/spring-cloud-aws-dependencies/pom.xml @@ -68,6 +68,13 @@ true + + software.amazon.encryption.s3 + amazon-s3-encryption-client-java + ${amazon.encryption.s3} + true + + software.amazon.awssdk s3-transfer-manager From 78ef972091d3b8889fdaf81a9f6b053977ec9ef2 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Wed, 31 Jul 2024 09:39:13 +0200 Subject: [PATCH 05/19] Merge main into branch --- .idea/vcs.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/vcs.xml b/.idea/vcs.xml index dcb6b8c4c..db755a432 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - - - + + + From c9beff027a1f6216def488872f4c5c31a84de05f Mon Sep 17 00:00:00 2001 From: matejnedic Date: Tue, 17 Sep 2024 20:21:11 +0200 Subject: [PATCH 06/19] revert --- .idea/vcs.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/vcs.xml b/.idea/vcs.xml index db755a432..702dda0cc 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + From 240f22882f8a78830de7a9ee2fe4449b70584dc3 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Wed, 18 Sep 2024 22:14:04 +0200 Subject: [PATCH 07/19] Change if statement --- .../awspring/cloud/autoconfigure/s3/S3AutoConfiguration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 74c22de38..195d97198 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 @@ -121,7 +121,8 @@ else if (awsProperties.getEndpoint() != null) { S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, ObjectProvider rsaProvider, ObjectProvider aesProvider) throws NoSuchAlgorithmException { - if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null)) { + if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null) + || aesProvider.getIfAvailable() != null || rsaProvider.getIfAvailable() != null) { return s3EncClient(properties, s3ClientBuilder, rsaProvider, aesProvider); } return s3ClientBuilder.build(); From 373420638b848a192e9c8c6e80829a04ba83d57a Mon Sep 17 00:00:00 2001 From: matejnedic Date: Wed, 18 Sep 2024 22:15:08 +0200 Subject: [PATCH 08/19] Change if statement --- .../awspring/cloud/autoconfigure/s3/S3AutoConfiguration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 195d97198..8a69f741c 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 @@ -122,7 +122,8 @@ S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, ObjectProvider rsaProvider, ObjectProvider aesProvider) throws NoSuchAlgorithmException { if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null) - || aesProvider.getIfAvailable() != null || rsaProvider.getIfAvailable() != null) { + && (aesProvider.getIfAvailable() != null || rsaProvider.getIfAvailable() != null || + StringUtils.hasText(properties.getEncryption().getKeyId()))) { return s3EncClient(properties, s3ClientBuilder, rsaProvider, aesProvider); } return s3ClientBuilder.build(); From 3d9e73d1e2d3fc30c7be65b6755e76c7702ae5b4 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Thu, 19 Sep 2024 16:44:25 +0200 Subject: [PATCH 09/19] Add docs --- docs/src/main/asciidoc/s3.adoc | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/docs/src/main/asciidoc/s3.adoc b/docs/src/main/asciidoc/s3.adoc index 8a7df66a7..168e72a29 100644 --- a/docs/src/main/asciidoc/s3.adoc +++ b/docs/src/main/asciidoc/s3.adoc @@ -127,6 +127,78 @@ try (OutputStream outputStream = s3Resource.getOutputStream()) { } ---- +=== S3 Client Side Encryption + +AWS offers encryption library which is integrated inside of S3 Client called https://docs.aws.amazon.com/amazon-s3-encryption-client/latest/developerguide/what-is-s3-encryption-client.html [S3EncryptionClient]. +With encryption client you are going to encrypt your files before sending them to S3 bucket. + +To autoconfigure Encryption Client simply add the following dependency. + +[source,xml] +---- + + software.amazon.encryption.s3 + amazon-s3-encryption-client-java + +---- + +We are supporting 3 types of encryption. + +1. To configure encryption via KMS key specify 'spring.cloud.aws.s3.encryption.keyId' with KMS key arn and this key will be used to encrypt your files. + +2. Asymmetric encryption is possible via RSA to enable it you will have to implement 'io.awspring.cloud.autoconfigure.s3.S3RsaProvider' + +!Note you will have to manage storing private and public keys yourself otherwise you won't be able to decrypt the data later. +Example of simple RSAProvider: + +[source,java,indent=0] +---- +import io.awspring.cloud.autoconfigure.s3.S3RsaProvider; +import java.security.KeyPair; +import java.security.KeyPairGenerator; + +public class MyRsaProvider implements S3RsaProvider { + @Override + public KeyPair generateKeyPair() { + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } + catch (Exception e) { + return null; + } + } +} +---- + +3. Last option is if you want to use symmetric algorithm, this is possible via `io.awspring.cloud.autoconfigure.s3.S3AesProvider` + +!Note you will have to manage storing storing private key! +Example of simple AESProvider: + +[source,java,indent=0] +---- +import io.awspring.cloud.autoconfigure.s3.S3AesProvider; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +public class MyAesProvider implements S3AesProvider { + @Override + public SecretKey generateSecretKey() { + try { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(256); + return keyGenerator.generateKey(); + } + catch (Exception e) { + return null; + } + } +} +---- + + ==== S3 Output Stream Under the hood by default `S3Resource` uses a `io.awspring.cloud.s3.InMemoryBufferingS3OutputStream`. When data is written to the resource, is gets sent to S3 using multipart upload. From edd077ebd941ac375d2f5ef34d53734b1901469d Mon Sep 17 00:00:00 2001 From: matejnedic Date: Thu, 19 Sep 2024 16:48:56 +0200 Subject: [PATCH 10/19] Add docs --- docs/src/main/asciidoc/s3.adoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/src/main/asciidoc/s3.adoc b/docs/src/main/asciidoc/s3.adoc index 168e72a29..52f7267b1 100644 --- a/docs/src/main/asciidoc/s3.adoc +++ b/docs/src/main/asciidoc/s3.adoc @@ -142,10 +142,22 @@ To autoconfigure Encryption Client simply add the following dependency. ---- + We are supporting 3 types of encryption. 1. To configure encryption via KMS key specify 'spring.cloud.aws.s3.encryption.keyId' with KMS key arn and this key will be used to encrypt your files. +Also, following dependency is required. +[source,xml] +---- + + software.amazon.awssdk + kms + true + +---- + + 2. Asymmetric encryption is possible via RSA to enable it you will have to implement 'io.awspring.cloud.autoconfigure.s3.S3RsaProvider' !Note you will have to manage storing private and public keys yourself otherwise you won't be able to decrypt the data later. From a54f02ef8295826491d862703a70cd67e11dce2c Mon Sep 17 00:00:00 2001 From: matejnedic Date: Thu, 19 Sep 2024 16:53:38 +0200 Subject: [PATCH 11/19] remove throw that does not exist --- .../awspring/cloud/autoconfigure/s3/S3AutoConfiguration.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 8a69f741c..bb9e34409 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 @@ -130,8 +130,7 @@ S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, } S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, - ObjectProvider rsaProvider, ObjectProvider aesProvider) - throws NoSuchAlgorithmException { + ObjectProvider rsaProvider, ObjectProvider aesProvider) { PropertyMapper propertyMapper = PropertyMapper.get(); S3EncryptionProperties encryptionProperties = properties.getEncryption(); S3EncryptionClient.Builder s3EncryptionBuilder = S3EncryptionClient.builder(); From 230889f5ece71391f72f6201892c7dfe61a76f01 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Fri, 4 Oct 2024 18:33:29 +0200 Subject: [PATCH 12/19] Merge main --- .../autoconfigure/s3/S3AutoConfiguration.java | 56 +++++++++---------- .../s3/properties/S3PluginProperties.java | 15 +++++ .../s3/S3AutoConfigurationTests.java | 49 ++++++++-------- .../cloud/s3/ObjectMetadataTests.java | 2 +- .../cloud/s3/S3ResourceIntegrationTests.java | 2 +- 5 files changed, 68 insertions(+), 56 deletions(-) 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 e314ce3e7..e894588d8 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 @@ -32,7 +32,6 @@ import io.awspring.cloud.s3.S3OutputStreamProvider; import io.awspring.cloud.s3.S3ProtocolResolver; import io.awspring.cloud.s3.S3Template; - import java.security.NoSuchAlgorithmException; import java.util.Optional; import org.springframework.beans.factory.ObjectProvider; @@ -49,7 +48,6 @@ import org.springframework.context.annotation.Import; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; -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; @@ -130,42 +128,42 @@ else if (awsProperties.getEndpoint() != null) { @Bean @ConditionalOnMissingBean S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, - ObjectProvider rsaProvider, ObjectProvider aesProvider) - throws NoSuchAlgorithmException { + ObjectProvider rsaProvider, ObjectProvider aesProvider) + throws NoSuchAlgorithmException { if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null) - && (aesProvider.getIfAvailable() != null || rsaProvider.getIfAvailable() != null || - StringUtils.hasText(properties.getEncryption().getKeyId()))) { + && (aesProvider.getIfAvailable() != null || rsaProvider.getIfAvailable() != null + || StringUtils.hasText(properties.getEncryption().getKeyId()))) { return s3EncClient(properties, s3ClientBuilder, rsaProvider, aesProvider); } return s3ClientBuilder.build(); } - S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, - ObjectProvider rsaProvider, ObjectProvider aesProvider) { - PropertyMapper propertyMapper = PropertyMapper.get(); - S3EncryptionProperties encryptionProperties = properties.getEncryption(); - S3EncryptionClient.Builder s3EncryptionBuilder = S3EncryptionClient.builder(); - - s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); - propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) - .to(s3EncryptionBuilder::enableDelayedAuthenticationMode); - propertyMapper.from(encryptionProperties::isEnableLegacyUnauthenticatedModes) - .to(s3EncryptionBuilder::enableLegacyUnauthenticatedModes); - propertyMapper.from(encryptionProperties::isEnableMultipartPutObject) - .to(s3EncryptionBuilder::enableMultipartPutObject); - - if (!StringUtils.hasText(encryptionProperties.getKeyId())) { - if (aesProvider.getIfAvailable() != null) { - s3EncryptionBuilder.aesKey(aesProvider.getObject().generateSecretKey()); - } - else { - s3EncryptionBuilder.rsaKeyPair(rsaProvider.getObject().generateKeyPair()); - } - return s3EncryptionBuilder.build(); + S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, + ObjectProvider rsaProvider, ObjectProvider aesProvider) { + PropertyMapper propertyMapper = PropertyMapper.get(); + S3EncryptionProperties encryptionProperties = properties.getEncryption(); + S3EncryptionClient.Builder s3EncryptionBuilder = S3EncryptionClient.builder(); + + s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); + propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) + .to(s3EncryptionBuilder::enableDelayedAuthenticationMode); + propertyMapper.from(encryptionProperties::isEnableLegacyUnauthenticatedModes) + .to(s3EncryptionBuilder::enableLegacyUnauthenticatedModes); + propertyMapper.from(encryptionProperties::isEnableMultipartPutObject) + .to(s3EncryptionBuilder::enableMultipartPutObject); + + if (!StringUtils.hasText(encryptionProperties.getKeyId())) { + if (aesProvider.getIfAvailable() != null) { + s3EncryptionBuilder.aesKey(aesProvider.getObject().generateSecretKey()); + } + else { + s3EncryptionBuilder.rsaKeyPair(rsaProvider.getObject().generateKeyPair()); } - propertyMapper.from(encryptionProperties::getKeyId).to(s3EncryptionBuilder::kmsKeyId); return s3EncryptionBuilder.build(); } + propertyMapper.from(encryptionProperties::getKeyId).to(s3EncryptionBuilder::kmsKeyId); + return s3EncryptionBuilder.build(); + } @Configuration @ConditionalOnClass(ObjectMapper.class) 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 index c636d89db..15c4afdaa 100644 --- 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 @@ -1,3 +1,18 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3.properties; public class S3PluginProperties { 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 c967afa66..de483c6d9 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 @@ -40,7 +40,6 @@ import java.util.Objects; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.internal.util.MockUtil; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -59,8 +58,8 @@ import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.presigner.S3Presigner; -import software.amazon.encryption.s3.S3EncryptionClient; import software.amazon.awssdk.utils.AttributeMap; +import software.amazon.encryption.s3.S3EncryptionClient; /** * Tests for {@link S3AutoConfiguration}. @@ -77,9 +76,9 @@ class S3AutoConfigurationTests { CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)); private final ApplicationContextRunner contextRunnerEncryption = new ApplicationContextRunner() - .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") - .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, - CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)); + .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") + .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") @@ -144,16 +143,17 @@ void s3ClientCanBeOverwritten() { @Test void createsStandardClientWhenCrossRegionAndEncryptionModuleIsNotInClasspath() { - contextRunnerEncryption.withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)) - .run(context -> { - assertThat(context).doesNotHaveBean(S3EncryptionClient.class); - assertThat(context).hasSingleBean(S3Client.class); - }); + contextRunnerEncryption.withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)).run(context -> { + assertThat(context).doesNotHaveBean(S3EncryptionClient.class); + assertThat(context).hasSingleBean(S3Client.class); + }); } @Test void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { - contextRunnerEncryption.withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab").run(context -> { + contextRunnerEncryption + .withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") + .run(context -> { assertThat(context).hasSingleBean(S3EncryptionClient.class); }); @@ -161,20 +161,18 @@ void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { @Test void createsEncryptionClientBackedByRsa() { - contextRunnerEncryption.withPropertyValues() - .withUserConfiguration(CustomRsaProvider.class).run(context -> { - assertThat(context).hasSingleBean(S3EncryptionClient.class); - assertThat(context).hasSingleBean(S3RsaProvider.class); - }); + contextRunnerEncryption.withPropertyValues().withUserConfiguration(CustomRsaProvider.class).run(context -> { + assertThat(context).hasSingleBean(S3EncryptionClient.class); + assertThat(context).hasSingleBean(S3RsaProvider.class); + }); } @Test void createsEncryptionClientBackedByAes() { - contextRunnerEncryption.withPropertyValues() - .withUserConfiguration(CustomAesProvider.class).run(context -> { - assertThat(context).hasSingleBean(S3EncryptionClient.class); - assertThat(context).hasSingleBean(S3AesProvider.class); - }); + contextRunnerEncryption.withPropertyValues().withUserConfiguration(CustomAesProvider.class).run(context -> { + assertThat(context).hasSingleBean(S3EncryptionClient.class); + assertThat(context).hasSingleBean(S3AesProvider.class); + }); } } @@ -242,10 +240,11 @@ void withJacksonOnClasspathAutoconfiguresObjectConverter() { @Test void withoutJacksonOnClasspathDoesNotConfigureObjectConverter() { - contextRunner.withClassLoader(new FilteredClassLoader(ObjectMapper.class, S3EncryptionClient.class)).run(context -> { - assertThat(context).doesNotHaveBean(S3ObjectConverter.class); - assertThat(context).doesNotHaveBean(S3Template.class); - }); + contextRunner.withClassLoader(new FilteredClassLoader(ObjectMapper.class, S3EncryptionClient.class)) + .run(context -> { + assertThat(context).doesNotHaveBean(S3ObjectConverter.class); + assertThat(context).doesNotHaveBean(S3Template.class); + }); } @Test diff --git a/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/ObjectMetadataTests.java b/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/ObjectMetadataTests.java index f6213dc3d..568f88ed6 100644 --- a/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/ObjectMetadataTests.java +++ b/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/ObjectMetadataTests.java @@ -81,7 +81,7 @@ void mapsEnumsToString() { @Test void doesNotApplyContentLengthForPartUpload() { - long objectContentLength = 16L; + long objectContentLength = 16L; long partContentLength = 8L; ObjectMetadata metadata = ObjectMetadata.builder().contentLength(objectContentLength).build(); diff --git a/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3ResourceIntegrationTests.java b/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3ResourceIntegrationTests.java index 599509340..0017bf96c 100644 --- a/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3ResourceIntegrationTests.java +++ b/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3ResourceIntegrationTests.java @@ -236,7 +236,7 @@ void contentLengthCanBeSetForLargeFiles(S3OutputStreamProvider s3OutputStreamPro outputStream.write(Files.toByteArray(file)); } GetObjectResponse result = client - .getObject(request -> request.bucket("first-bucket").key("new-file" + i + ".txt").build()).response(); + .getObject(request -> request.bucket("first-bucket").key("new-file" + i + ".txt").build()).response(); assertThat(result.contentType()).isEqualTo("text/plain"); } From 62650237b9408deafe07605772bb80aa448775bd Mon Sep 17 00:00:00 2001 From: matejnedic Date: Thu, 10 Oct 2024 19:52:03 +0200 Subject: [PATCH 13/19] Align to current strategy --- .../autoconfigure/s3/S3AutoConfiguration.java | 41 ++++++++++++------- .../s3/S3EncryptionClientCustomizer.java | 14 +++++++ .../s3/S3AutoConfigurationTests.java | 6 +-- spring-cloud-aws-dependencies/pom.xml | 2 +- 4 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java 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 e894588d8..b05f52b89 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 @@ -37,10 +37,7 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.*; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; @@ -71,6 +68,8 @@ public class S3AutoConfiguration { private final S3Properties properties; + private S3EncryptionClient.Builder encryptionBuilder; + public S3AutoConfiguration(S3Properties properties) { this.properties = properties; } @@ -86,17 +85,35 @@ 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()); return builder; } + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass(value = S3EncryptionClient.class ) + S3EncryptionClient.Builder s3EncrpytionClientBuilder(AwsClientBuilderConfigurer awsClientBuilderConfigurer, + ObjectProvider> configurer, + ObjectProvider connectionDetails, + ObjectProvider s3ClientCustomizers, + ObjectProvider awsSyncClientCustomizers) { + S3EncryptionClient.Builder builder = awsClientBuilderConfigurer.configureSyncClient(S3EncryptionClient.builder(), this.properties, + connectionDetails.getIfAvailable(), configurer. getIfAvailable(), s3ClientCustomizers.orderedStream(), + awsSyncClientCustomizers.orderedStream()); + Optional.ofNullable(this.properties.getCrossRegionEnabled()).ifPresent(builder::crossRegionAccessEnabled); + builder.serviceConfiguration(this.properties.toS3Configuration()); + return builder; + } + @Bean @ConditionalOnMissingBean(S3Operations.class) @ConditionalOnBean(S3ObjectConverter.class) @@ -127,22 +144,18 @@ else if (awsProperties.getEndpoint() != null) { @Bean @ConditionalOnMissingBean - S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder, - ObjectProvider rsaProvider, ObjectProvider aesProvider) - throws NoSuchAlgorithmException { - if (ClassUtils.isPresent("software.amazon.encryption.s3.S3EncryptionClient", null) - && (aesProvider.getIfAvailable() != null || rsaProvider.getIfAvailable() != null - || StringUtils.hasText(properties.getEncryption().getKeyId()))) { - return s3EncClient(properties, s3ClientBuilder, rsaProvider, aesProvider); - } + @ConditionalOnMissingClass(value = {"software.amazon.encryption.s3.S3EncryptionClient"}) + S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder) { return s3ClientBuilder.build(); } - S3Client s3EncClient(S3Properties properties, S3ClientBuilder s3ClientBuilder, + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass(name = {"software.amazon.encryption.s3.S3EncryptionClient"}) + S3Client s3EncryptionClient(S3Properties properties,S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder, ObjectProvider rsaProvider, ObjectProvider aesProvider) { PropertyMapper propertyMapper = PropertyMapper.get(); S3EncryptionProperties encryptionProperties = properties.getEncryption(); - S3EncryptionClient.Builder s3EncryptionBuilder = S3EncryptionClient.builder(); s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java new file mode 100644 index 000000000..570b9e45b --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java @@ -0,0 +1,14 @@ +package io.awspring.cloud.autoconfigure.s3; + +import io.awspring.cloud.autoconfigure.AwsClientCustomizer; +import software.amazon.encryption.s3.S3EncryptionClient; + +/** + * Callback interface that can be used to customize a {@link S3EncryptionClient.Builder}. + * + * @author Matej Nedic + * @since 3.3.0 + */ +@FunctionalInterface +public interface S3EncryptionClientCustomizer extends AwsClientCustomizer { +} 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 de483c6d9..fcbe68394 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 @@ -73,7 +73,8 @@ class S3AutoConfigurationTests { .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)) .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, - CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)); + CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)); private final ApplicationContextRunner contextRunnerEncryption = new ApplicationContextRunner() .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") @@ -84,7 +85,7 @@ class S3AutoConfigurationTests { .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)); + .withClassLoader(new FilteredClassLoader(S3AccessGrantsPlugin.class, S3EncryptionClient.class)); @Test void setsS3AccessGrantIdentityProvider() { @@ -154,7 +155,6 @@ void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { contextRunnerEncryption .withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") .run(context -> { - assertThat(context).hasSingleBean(S3EncryptionClient.class); }); } diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml index 9631a70ad..5a0b95bdd 100644 --- a/spring-cloud-aws-dependencies/pom.xml +++ b/spring-cloud-aws-dependencies/pom.xml @@ -26,7 +26,7 @@ 2.31.0 2.28.2 2.0.4 - 3.0.1 + 3.2.3 1.6 4.1.4 2.1.0 From 399cc3c76728ac7405b2c123449a6ea1e79d5285 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Thu, 10 Oct 2024 20:25:15 +0200 Subject: [PATCH 14/19] Test --- .../autoconfigure/s3/S3AutoConfiguration.java | 57 +++++++++++-------- .../s3/S3AutoConfigurationTests.java | 14 ++--- 2 files changed, 37 insertions(+), 34 deletions(-) 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 b05f52b89..f6d8d5a78 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 @@ -52,6 +52,8 @@ import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.encryption.s3.S3EncryptionClient; +import software.amazon.encryption.s3.materials.AesKeyring; +import software.amazon.encryption.s3.materials.DefaultCryptoMaterialsManager; /** * {@link EnableAutoConfiguration} for {@link S3Client} and {@link S3ProtocolResolver}. @@ -105,15 +107,19 @@ S3EncryptionClient.Builder s3EncrpytionClientBuilder(AwsClientBuilderConfigurer ObjectProvider> configurer, ObjectProvider connectionDetails, ObjectProvider s3ClientCustomizers, - ObjectProvider awsSyncClientCustomizers) { + ObjectProvider awsSyncClientCustomizers, + ObjectProvider rsaProvider, ObjectProvider aesProvider) { S3EncryptionClient.Builder builder = awsClientBuilderConfigurer.configureSyncClient(S3EncryptionClient.builder(), this.properties, connectionDetails.getIfAvailable(), configurer. getIfAvailable(), s3ClientCustomizers.orderedStream(), awsSyncClientCustomizers.orderedStream()); + Optional.ofNullable(this.properties.getCrossRegionEnabled()).ifPresent(builder::crossRegionAccessEnabled); builder.serviceConfiguration(this.properties.toS3Configuration()); + + configureEncryptionProperties(rsaProvider, aesProvider, builder); return builder; } - + @Bean @ConditionalOnMissingBean(S3Operations.class) @ConditionalOnBean(S3ObjectConverter.class) @@ -152,29 +158,8 @@ S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder) { @Bean @ConditionalOnMissingBean @ConditionalOnClass(name = {"software.amazon.encryption.s3.S3EncryptionClient"}) - S3Client s3EncryptionClient(S3Properties properties,S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder, - ObjectProvider rsaProvider, ObjectProvider aesProvider) { - PropertyMapper propertyMapper = PropertyMapper.get(); - S3EncryptionProperties encryptionProperties = properties.getEncryption(); - + S3Client s3EncryptionClient(S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder) { s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); - propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) - .to(s3EncryptionBuilder::enableDelayedAuthenticationMode); - propertyMapper.from(encryptionProperties::isEnableLegacyUnauthenticatedModes) - .to(s3EncryptionBuilder::enableLegacyUnauthenticatedModes); - propertyMapper.from(encryptionProperties::isEnableMultipartPutObject) - .to(s3EncryptionBuilder::enableMultipartPutObject); - - if (!StringUtils.hasText(encryptionProperties.getKeyId())) { - if (aesProvider.getIfAvailable() != null) { - s3EncryptionBuilder.aesKey(aesProvider.getObject().generateSecretKey()); - } - else { - s3EncryptionBuilder.rsaKeyPair(rsaProvider.getObject().generateKeyPair()); - } - return s3EncryptionBuilder.build(); - } - propertyMapper.from(encryptionProperties::getKeyId).to(s3EncryptionBuilder::kmsKeyId); return s3EncryptionBuilder.build(); } @@ -197,4 +182,28 @@ S3OutputStreamProvider inMemoryBufferingS3StreamProvider(S3Client s3Client, contentTypeResolver.orElseGet(PropertiesS3ObjectContentTypeResolver::new)); } + + private void configureEncryptionProperties(ObjectProvider rsaProvider, ObjectProvider aesProvider, S3EncryptionClient.Builder builder) { + PropertyMapper propertyMapper = PropertyMapper.get(); + var encryptionProperties = properties.getEncryption(); + + propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) + .to(builder::enableDelayedAuthenticationMode); + propertyMapper.from(encryptionProperties::isEnableLegacyUnauthenticatedModes) + .to(builder::enableLegacyUnauthenticatedModes); + propertyMapper.from(encryptionProperties::isEnableMultipartPutObject) + .to(builder::enableMultipartPutObject); + + if (!StringUtils.hasText(properties.getEncryption().getKeyId())) { + if (aesProvider.getIfAvailable() != null) { + builder.aesKey(aesProvider.getObject().generateSecretKey()); + } + else { + builder.rsaKeyPair(rsaProvider.getObject().generateKeyPair()); + } + } else { + propertyMapper.from(encryptionProperties::getKeyId).to(builder::kmsKeyId); + } + } + } 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 fcbe68394..9d082c4f8 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 @@ -36,8 +36,11 @@ import io.awspring.cloud.s3.S3Template; import java.io.IOException; import java.net.URI; +import java.security.KeyPair; import java.time.Duration; import java.util.Objects; + +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -137,7 +140,7 @@ void autoconfigurationIsNotTriggeredWhenS3ModuleIsNotOnClasspath() { class S3ClientTests { @Test void s3ClientCanBeOverwritten() { - contextRunnerEncryption.withUserConfiguration(CustomS3ClientConfiguration.class).run(context -> { + contextRunnerEncryption.withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab").withUserConfiguration(CustomS3ClientConfiguration.class).run(context -> { assertThat(context).hasSingleBean(S3Client.class); }); } @@ -150,15 +153,6 @@ void createsStandardClientWhenCrossRegionAndEncryptionModuleIsNotInClasspath() { }); } - @Test - void createsEncryptionClientWhenCrossRegionModuleIsNotInClasspath() { - contextRunnerEncryption - .withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") - .run(context -> { - assertThat(context).hasSingleBean(S3EncryptionClient.class); - }); - } - @Test void createsEncryptionClientBackedByRsa() { contextRunnerEncryption.withPropertyValues().withUserConfiguration(CustomRsaProvider.class).run(context -> { From edd803ccfba326fdb5f222da639105c44fb7bf06 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Thu, 10 Oct 2024 20:28:47 +0200 Subject: [PATCH 15/19] Remove unused classes --- .../cloud/autoconfigure/s3/S3AutoConfiguration.java | 6 ------ 1 file changed, 6 deletions(-) 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 f6d8d5a78..e95677a9c 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 @@ -21,7 +21,6 @@ import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer; import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails; import io.awspring.cloud.autoconfigure.core.AwsProperties; -import io.awspring.cloud.autoconfigure.s3.properties.S3EncryptionProperties; import io.awspring.cloud.autoconfigure.s3.properties.S3Properties; import io.awspring.cloud.s3.InMemoryBufferingS3OutputStreamProvider; import io.awspring.cloud.s3.Jackson2JsonS3ObjectConverter; @@ -32,7 +31,6 @@ import io.awspring.cloud.s3.S3OutputStreamProvider; import io.awspring.cloud.s3.S3ProtocolResolver; import io.awspring.cloud.s3.S3Template; -import java.security.NoSuchAlgorithmException; import java.util.Optional; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -52,8 +50,6 @@ import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.encryption.s3.S3EncryptionClient; -import software.amazon.encryption.s3.materials.AesKeyring; -import software.amazon.encryption.s3.materials.DefaultCryptoMaterialsManager; /** * {@link EnableAutoConfiguration} for {@link S3Client} and {@link S3ProtocolResolver}. @@ -70,8 +66,6 @@ public class S3AutoConfiguration { private final S3Properties properties; - private S3EncryptionClient.Builder encryptionBuilder; - public S3AutoConfiguration(S3Properties properties) { this.properties = properties; } From 125a19d764404f28f35af2d2ff1d44880fa03e97 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Thu, 17 Oct 2024 22:18:54 +0200 Subject: [PATCH 16/19] Modify tests --- .../s3/S3EncryptionClientCustomizer.java | 15 +++++++++++++++ ...eterStoreConfigDataLoaderIntegrationTests.java | 8 ++++++++ ...tsManagerConfigDataLoaderIntegrationTests.java | 8 ++++++++ ...chExportAutoConfigurationIntegrationTests.java | 1 + .../s3/S3AutoConfigurationTests.java | 13 ++++++------- .../autoconfigure/s3/S3ClientCustomizerTests.java | 5 ++++- 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java index 570b9e45b..0367badd0 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionClientCustomizer.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3; import io.awspring.cloud.autoconfigure.AwsClientCustomizer; diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java index 60a488005..cd7a5f390 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java @@ -102,6 +102,7 @@ void propertyIsNotResolvedWhenIntegrationIsDisabled() { "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.enabled=false", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1")) { assertThat(context.getEnvironment().getProperty("message")).isNull(); @@ -241,6 +242,7 @@ void serviceSpecificEndpointTakesPrecedenceOverGlobalAwsRegion() { "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=http://non-existing-host/", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1", @@ -257,6 +259,7 @@ void parameterStoreClientUsesGlobalRegion() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=" + REGION, "--logging.level.io.awspring.cloud.parameterstore=debug")) { @@ -307,6 +310,7 @@ void reloadsPropertiesWhenPropertyValueChanges() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.reload.strategy=refresh", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.reload.period=PT1S", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), @@ -334,6 +338,7 @@ void reloadsPropertiesWhenNewPropertyIsAdded() { "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.reload.strategy=refresh", "--spring.cloud.aws.parameterstore.reload.period=PT1S", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -359,6 +364,7 @@ void doesNotReloadPropertiesWhenReloadStrategyIsNotSet() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.reload.period=PT1S", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -386,6 +392,7 @@ void reloadsPropertiesWithRestartContextStrategy() { "--spring.cloud.aws.parameterstore.reload.strategy=restart_context", "--spring.cloud.aws.parameterstore.reload.period=PT1S", "--spring.cloud.aws.parameterstore.region=" + REGION, + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--management.endpoint.restart.enabled=true", "--management.endpoints.web.exposure.include=restart", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -409,6 +416,7 @@ private ConfigurableApplicationContext runApplication(SpringApplication applicat return application.run("--spring.config.import=" + springConfigImport, "--spring.cloud.aws.parameterstore.region=" + REGION, "--" + endpointProperty + "=" + localstack.getEndpoint(), + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1", "--logging.level.io.awspring.cloud.parameterstore=debug"); } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java index 27e898315..9cddc013c 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java @@ -296,6 +296,7 @@ void propertyIsNotResolvedWhenIntegrationIsDisabled() { "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.enabled=false", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1")) { assertThat(context.getEnvironment().getProperty("message")).isNull(); @@ -312,6 +313,7 @@ void serviceSpecificEndpointTakesPrecedenceOverGlobalAwsRegion() { "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.region=" + REGION, "--spring.cloud.aws.endpoint=http://non-existing-host/", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1")) { @@ -331,6 +333,7 @@ void secretsManagerClientUsesStsCredentials() throws IOException { "--spring.config.import=optional:aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.region.static=" + REGION, "--spring.cloud.aws.credentials.sts.role-arn=develop", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.sts.enabled=true", "--spring.cloud.aws.credentials.sts.web-identity-token-file=" + tempFile.getAbsolutePath())) { assertThat(context.getBean(AwsCredentialsProvider.class)) @@ -346,6 +349,7 @@ void secretsManagerClientUsesGlobalRegion() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=" + REGION)) { assertThat(context.getEnvironment().getProperty("message")).isEqualTo("value from tests"); @@ -370,6 +374,7 @@ void reloadsProperties() { "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.region=" + REGION, "--spring.cloud.aws.secretsmanager.reload.strategy=refresh", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.reload.period=PT1S", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -396,6 +401,7 @@ void doesNotReloadPropertiesWhenMonitoringIsDisabled() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.region=" + REGION, + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.reload.period=PT1S", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -424,6 +430,7 @@ void reloadsPropertiesWithRestartContextStrategy() { "--spring.cloud.aws.secretsmanager.region=" + REGION, "--spring.cloud.aws.secretsmanager.reload.strategy=RESTART_CONTEXT", "--spring.cloud.aws.secretsmanager.reload.period=PT1S", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.reload.max-wait-for-restart=PT1S", "--management.endpoint.restart.enabled=true", "--management.endpoints.web.exposure.include=restart", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), @@ -452,6 +459,7 @@ private ConfigurableApplicationContext runApplication(SpringApplication applicat String endpointProperty) { return application.run("--spring.config.import=" + springConfigImport, "--spring.cloud.aws.secretsmanager.region=" + REGION, + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--" + endpointProperty + "=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1", "--logging.level.io.awspring.cloud.secretsmanager=debug"); diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java index d4f12cfa6..21b0c2683 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java @@ -68,6 +68,7 @@ void testCounter() { try (ConfigurableApplicationContext context = application.run( "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=us-east-1", "--management.cloudwatch.metrics.export.step=5s", "--management.cloudwatch.metrics.export.namespace=awspring/spring-cloud-aws", 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 9d082c4f8..c1a1ca055 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 @@ -36,11 +36,8 @@ import io.awspring.cloud.s3.S3Template; import java.io.IOException; import java.net.URI; -import java.security.KeyPair; import java.time.Duration; import java.util.Objects; - -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -77,7 +74,7 @@ class S3AutoConfigurationTests { .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)) .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)) - .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)); + .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)); private final ApplicationContextRunner contextRunnerEncryption = new ApplicationContextRunner() .withPropertyValues("spring.cloud.aws.region.static:eu-west-1") @@ -140,9 +137,11 @@ void autoconfigurationIsNotTriggeredWhenS3ModuleIsNotOnClasspath() { class S3ClientTests { @Test void s3ClientCanBeOverwritten() { - contextRunnerEncryption.withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab").withUserConfiguration(CustomS3ClientConfiguration.class).run(context -> { - assertThat(context).hasSingleBean(S3Client.class); - }); + contextRunnerEncryption + .withPropertyValues("spring.cloud.aws.s3.encryption.keyId:234abcd-12ab-34cd-56ef-1234567890ab") + .withUserConfiguration(CustomS3ClientConfiguration.class).run(context -> { + assertThat(context).hasSingleBean(S3Client.class); + }); } @Test diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java index 2679effd9..8d6542269 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java @@ -25,12 +25,14 @@ import java.time.Duration; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.encryption.s3.S3EncryptionClient; /** * Tests for {@link S3ClientCustomizer}. @@ -43,7 +45,8 @@ class S3ClientCustomizerTests { .withPropertyValues("spring.cloud.aws.region.static:eu-west-1", "spring.cloud.aws.credentials.access-key:noop", "spring.cloud.aws.credentials.secret-key:noop") .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, - CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)); + CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)); @Test void customClientCustomizer() { From 2968e66fdacbac0d8a51ccbd611e28e8cca8893b Mon Sep 17 00:00:00 2001 From: matejnedic Date: Sun, 20 Oct 2024 12:27:19 +0200 Subject: [PATCH 17/19] refactor --- .../autoconfigure/s3/S3AutoConfiguration.java | 111 +++++++++--------- ...portAutoConfigurationIntegrationTests.java | 2 +- .../s3/S3ClientCustomizerTests.java | 2 +- 3 files changed, 60 insertions(+), 55 deletions(-) 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 e95677a9c..930695805 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 @@ -81,7 +81,6 @@ 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(); @@ -94,26 +93,6 @@ S3ClientBuilder s3ClientBuilder(AwsClientBuilderConfigurer awsClientBuilderConfi return builder; } - @Bean - @ConditionalOnMissingBean - @ConditionalOnClass(value = S3EncryptionClient.class ) - S3EncryptionClient.Builder s3EncrpytionClientBuilder(AwsClientBuilderConfigurer awsClientBuilderConfigurer, - ObjectProvider> configurer, - ObjectProvider connectionDetails, - ObjectProvider s3ClientCustomizers, - ObjectProvider awsSyncClientCustomizers, - ObjectProvider rsaProvider, ObjectProvider aesProvider) { - S3EncryptionClient.Builder builder = awsClientBuilderConfigurer.configureSyncClient(S3EncryptionClient.builder(), this.properties, - connectionDetails.getIfAvailable(), configurer. getIfAvailable(), s3ClientCustomizers.orderedStream(), - awsSyncClientCustomizers.orderedStream()); - - Optional.ofNullable(this.properties.getCrossRegionEnabled()).ifPresent(builder::crossRegionAccessEnabled); - builder.serviceConfiguration(this.properties.toS3Configuration()); - - configureEncryptionProperties(rsaProvider, aesProvider, builder); - return builder; - } - @Bean @ConditionalOnMissingBean(S3Operations.class) @ConditionalOnBean(S3ObjectConverter.class) @@ -144,17 +123,67 @@ else if (awsProperties.getEndpoint() != null) { @Bean @ConditionalOnMissingBean - @ConditionalOnMissingClass(value = {"software.amazon.encryption.s3.S3EncryptionClient"}) - S3Client s3Client(S3Properties properties, S3ClientBuilder s3ClientBuilder) { + @ConditionalOnMissingClass(value = { "software.amazon.encryption.s3.S3EncryptionClient" }) + S3Client s3Client(S3ClientBuilder s3ClientBuilder) { return s3ClientBuilder.build(); } - @Bean - @ConditionalOnMissingBean - @ConditionalOnClass(name = {"software.amazon.encryption.s3.S3EncryptionClient"}) - S3Client s3EncryptionClient(S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder) { - s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); - return s3EncryptionBuilder.build(); + @Configuration + @ConditionalOnClass(name = { "software.amazon.encryption.s3.S3EncryptionClient" }) + static class S3EncryptionConfiguration { + + @Bean + @ConditionalOnMissingBean + S3Client s3EncryptionClient(S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder) { + s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); + return s3EncryptionBuilder.build(); + } + + @Bean + @ConditionalOnMissingBean + S3EncryptionClient.Builder s3EncrpytionClientBuilder(S3Properties properties, + AwsClientBuilderConfigurer awsClientBuilderConfigurer, + ObjectProvider> configurer, + ObjectProvider connectionDetails, + ObjectProvider s3ClientCustomizers, + ObjectProvider awsSyncClientCustomizers, + ObjectProvider rsaProvider, ObjectProvider aesProvider) { + S3EncryptionClient.Builder builder = awsClientBuilderConfigurer.configureSyncClient( + S3EncryptionClient.builder(), properties, connectionDetails.getIfAvailable(), + configurer.getIfAvailable(), s3ClientCustomizers.orderedStream(), + awsSyncClientCustomizers.orderedStream()); + + Optional.ofNullable(properties.getCrossRegionEnabled()).ifPresent(builder::crossRegionAccessEnabled); + builder.serviceConfiguration(properties.toS3Configuration()); + + configureEncryptionProperties(properties, rsaProvider, aesProvider, builder); + return builder; + } + + private static void configureEncryptionProperties(S3Properties properties, + ObjectProvider rsaProvider, ObjectProvider aesProvider, + S3EncryptionClient.Builder builder) { + PropertyMapper propertyMapper = PropertyMapper.get(); + var encryptionProperties = properties.getEncryption(); + + propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) + .to(builder::enableDelayedAuthenticationMode); + propertyMapper.from(encryptionProperties::isEnableLegacyUnauthenticatedModes) + .to(builder::enableLegacyUnauthenticatedModes); + propertyMapper.from(encryptionProperties::isEnableMultipartPutObject).to(builder::enableMultipartPutObject); + + if (!StringUtils.hasText(properties.getEncryption().getKeyId())) { + if (aesProvider.getIfAvailable() != null) { + builder.aesKey(aesProvider.getObject().generateSecretKey()); + } + else { + builder.rsaKeyPair(rsaProvider.getObject().generateKeyPair()); + } + } + else { + propertyMapper.from(encryptionProperties::getKeyId).to(builder::kmsKeyId); + } + } } @Configuration @@ -176,28 +205,4 @@ S3OutputStreamProvider inMemoryBufferingS3StreamProvider(S3Client s3Client, contentTypeResolver.orElseGet(PropertiesS3ObjectContentTypeResolver::new)); } - - private void configureEncryptionProperties(ObjectProvider rsaProvider, ObjectProvider aesProvider, S3EncryptionClient.Builder builder) { - PropertyMapper propertyMapper = PropertyMapper.get(); - var encryptionProperties = properties.getEncryption(); - - propertyMapper.from(encryptionProperties::isEnableDelayedAuthenticationMode) - .to(builder::enableDelayedAuthenticationMode); - propertyMapper.from(encryptionProperties::isEnableLegacyUnauthenticatedModes) - .to(builder::enableLegacyUnauthenticatedModes); - propertyMapper.from(encryptionProperties::isEnableMultipartPutObject) - .to(builder::enableMultipartPutObject); - - if (!StringUtils.hasText(properties.getEncryption().getKeyId())) { - if (aesProvider.getIfAvailable() != null) { - builder.aesKey(aesProvider.getObject().generateSecretKey()); - } - else { - builder.rsaKeyPair(rsaProvider.getObject().generateKeyPair()); - } - } else { - propertyMapper.from(encryptionProperties::getKeyId).to(builder::kmsKeyId); - } - } - } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java index 21b0c2683..1e9f96f17 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java @@ -68,7 +68,7 @@ void testCounter() { try (ConfigurableApplicationContext context = application.run( "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", + "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=us-east-1", "--management.cloudwatch.metrics.export.step=5s", "--management.cloudwatch.metrics.export.namespace=awspring/spring-cloud-aws", diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java index 8d6542269..1b5208169 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/s3/S3ClientCustomizerTests.java @@ -46,7 +46,7 @@ class S3ClientCustomizerTests { "spring.cloud.aws.credentials.access-key:noop", "spring.cloud.aws.credentials.secret-key:noop") .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, CredentialsProviderAutoConfiguration.class, S3AutoConfiguration.class)) - .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)); + .withClassLoader(new FilteredClassLoader(S3EncryptionClient.class)); @Test void customClientCustomizer() { From 2aef40b93bc2ea89a05c60a81f3970dc7f4c7cd9 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Wed, 30 Oct 2024 23:27:21 +0100 Subject: [PATCH 18/19] Amend PR comments and refactor --- docs/src/main/asciidoc/s3.adoc | 10 +++---- .../cloud/autoconfigure/s3/S3AesProvider.java | 12 ++++++++ .../autoconfigure/s3/S3AutoConfiguration.java | 26 ++++++++--------- .../s3/S3EncryptionConditional.java | 29 +++++++++++++++++++ .../cloud/autoconfigure/s3/S3RsaProvider.java | 13 +++++++++ ...StoreConfigDataLoaderIntegrationTests.java | 8 ----- ...nagerConfigDataLoaderIntegrationTests.java | 8 ----- ...portAutoConfigurationIntegrationTests.java | 1 - spring-cloud-aws-dependencies/pom.xml | 4 +-- 9 files changed, 73 insertions(+), 38 deletions(-) create mode 100644 spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java diff --git a/docs/src/main/asciidoc/s3.adoc b/docs/src/main/asciidoc/s3.adoc index 4c8cdd529..702873605 100644 --- a/docs/src/main/asciidoc/s3.adoc +++ b/docs/src/main/asciidoc/s3.adoc @@ -173,9 +173,8 @@ public class MyRsaProvider implements S3RsaProvider { @Override public KeyPair generateKeyPair() { try { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - return keyPairGenerator.generateKeyPair(); + // fetch key pair from secure location such as Secrets Manager + // access to KeyPair is required to decrypt objects when fetching, so it is advised to keep them stored securely } catch (Exception e) { return null; @@ -199,9 +198,8 @@ public class MyAesProvider implements S3AesProvider { @Override public SecretKey generateSecretKey() { try { - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - keyGenerator.init(256); - return keyGenerator.generateKey(); + // fetch secret key from secure location such as Secrets Manager + // access to secret key is required to decrypt objects when fetching, so it is advised to keep them stored securely } catch (Exception e) { return null; diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java index 94d128542..5a59970da 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java @@ -17,7 +17,19 @@ import javax.crypto.SecretKey; +/** + * Interface for providing {@link SecretKey} when configuring {@link software.amazon.encryption.s3.S3EncryptionClient}. + * Required when encrypting files server side with AES. + * Secret Key should be stored in secure storage, for example AWS Secrets Manager. + * @author Matej Nedic + * @since 3.3.0 + */ public interface S3AesProvider { + /** + * Provides SecretKey that will be used to configure {@link software.amazon.encryption.s3.S3EncryptionClient}. + * Advised to fetch and return SecretKey in this method from Secured Storage. + * @return KeyPair that will be used for encryption/decryption. + */ SecretKey generateSecretKey(); } 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 930695805..c85fd6c1f 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 @@ -39,6 +39,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.util.ClassUtils; @@ -120,28 +121,21 @@ else if (awsProperties.getEndpoint() != null) { Optional.ofNullable(awsProperties.getDualstackEnabled()).ifPresent(builder::dualstackEnabled); return builder.build(); } - - @Bean - @ConditionalOnMissingBean - @ConditionalOnMissingClass(value = { "software.amazon.encryption.s3.S3EncryptionClient" }) - S3Client s3Client(S3ClientBuilder s3ClientBuilder) { - return s3ClientBuilder.build(); - } - + @Conditional(S3EncryptionConditional.class) + @ConditionalOnClass(name = "software.amazon.encryption.s3.S3EncryptionClient") @Configuration - @ConditionalOnClass(name = { "software.amazon.encryption.s3.S3EncryptionClient" }) - static class S3EncryptionConfiguration { + public static class S3EncryptionConfiguration { @Bean @ConditionalOnMissingBean - S3Client s3EncryptionClient(S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder) { + public static S3Client s3EncryptionClient(S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder) { s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); return s3EncryptionBuilder.build(); } @Bean @ConditionalOnMissingBean - S3EncryptionClient.Builder s3EncrpytionClientBuilder(S3Properties properties, + public static S3EncryptionClient.Builder s3EncrpytionClientBuilder(S3Properties properties, AwsClientBuilderConfigurer awsClientBuilderConfigurer, ObjectProvider> configurer, ObjectProvider connectionDetails, @@ -186,6 +180,13 @@ private static void configureEncryptionProperties(S3Properties properties, } } + @Bean + @ConditionalOnMissingBean + S3Client s3Client(S3ClientBuilder s3ClientBuilder) { + return s3ClientBuilder.build(); + } + + @Configuration @ConditionalOnClass(ObjectMapper.class) static class Jackson2JsonS3ObjectConverterConfiguration { @@ -204,5 +205,4 @@ S3OutputStreamProvider inMemoryBufferingS3StreamProvider(S3Client s3Client, return new InMemoryBufferingS3OutputStreamProvider(s3Client, contentTypeResolver.orElseGet(PropertiesS3ObjectContentTypeResolver::new)); } - } diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java new file mode 100644 index 000000000..1b3a43857 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java @@ -0,0 +1,29 @@ +package io.awspring.cloud.autoconfigure.s3; + +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +/** + * Conditional for creating {@link software.amazon.encryption.s3.S3EncryptionClient}. + * Will only create S3EncryptionClient if one of following is true. + * @author Matej Nedic + * @since 3.3.0 + */ +public class S3EncryptionConditional extends AnyNestedCondition { + public S3EncryptionConditional() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnBean(S3RsaProvider.class) + static class RSAProviderCondition { + } + + @ConditionalOnBean(S3AesProvider.class) + static class AESProviderCondition { + } + + @ConditionalOnProperty(name = "spring.cloud.aws.s3.encryption.keyId") + static class KmsKeyProperty { + } +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java index 734b2fd5c..a48e8b3ce 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java @@ -17,7 +17,20 @@ import java.security.KeyPair; + +/** + * Interface for providing {@link KeyPair} when configuring {@link software.amazon.encryption.s3.S3EncryptionClient}. + * Required for encrypting/decrypting files server side with RSA. + * Key pair should be stored in secure storage, for example AWS Secrets Manager. + * @author Matej Nedic + * @since 3.3.0 + */ public interface S3RsaProvider { + /** + * Provides KeyPair that will be used to configure {@link software.amazon.encryption.s3.S3EncryptionClient}. + * Advised to fetch and return KeyPair in this method from Secured Storage. + * @return KeyPair that will be used for encryption/decryption. + */ KeyPair generateKeyPair(); } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java index cd7a5f390..60a488005 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java @@ -102,7 +102,6 @@ void propertyIsNotResolvedWhenIntegrationIsDisabled() { "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.enabled=false", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1")) { assertThat(context.getEnvironment().getProperty("message")).isNull(); @@ -242,7 +241,6 @@ void serviceSpecificEndpointTakesPrecedenceOverGlobalAwsRegion() { "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=http://non-existing-host/", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1", @@ -259,7 +257,6 @@ void parameterStoreClientUsesGlobalRegion() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=" + REGION, "--logging.level.io.awspring.cloud.parameterstore=debug")) { @@ -310,7 +307,6 @@ void reloadsPropertiesWhenPropertyValueChanges() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.reload.strategy=refresh", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.reload.period=PT1S", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), @@ -338,7 +334,6 @@ void reloadsPropertiesWhenNewPropertyIsAdded() { "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.reload.strategy=refresh", "--spring.cloud.aws.parameterstore.reload.period=PT1S", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -364,7 +359,6 @@ void doesNotReloadPropertiesWhenReloadStrategyIsNotSet() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-parameterstore:/config/spring/", "--spring.cloud.aws.parameterstore.reload.period=PT1S", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.parameterstore.region=" + REGION, "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -392,7 +386,6 @@ void reloadsPropertiesWithRestartContextStrategy() { "--spring.cloud.aws.parameterstore.reload.strategy=restart_context", "--spring.cloud.aws.parameterstore.reload.period=PT1S", "--spring.cloud.aws.parameterstore.region=" + REGION, - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--management.endpoint.restart.enabled=true", "--management.endpoints.web.exposure.include=restart", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -416,7 +409,6 @@ private ConfigurableApplicationContext runApplication(SpringApplication applicat return application.run("--spring.config.import=" + springConfigImport, "--spring.cloud.aws.parameterstore.region=" + REGION, "--" + endpointProperty + "=" + localstack.getEndpoint(), - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1", "--logging.level.io.awspring.cloud.parameterstore=debug"); } diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java index 9cddc013c..27e898315 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/secretsmanager/SecretsManagerConfigDataLoaderIntegrationTests.java @@ -296,7 +296,6 @@ void propertyIsNotResolvedWhenIntegrationIsDisabled() { "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.enabled=false", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1")) { assertThat(context.getEnvironment().getProperty("message")).isNull(); @@ -313,7 +312,6 @@ void serviceSpecificEndpointTakesPrecedenceOverGlobalAwsRegion() { "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.region=" + REGION, "--spring.cloud.aws.endpoint=http://non-existing-host/", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1")) { @@ -333,7 +331,6 @@ void secretsManagerClientUsesStsCredentials() throws IOException { "--spring.config.import=optional:aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.region.static=" + REGION, "--spring.cloud.aws.credentials.sts.role-arn=develop", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.sts.enabled=true", "--spring.cloud.aws.credentials.sts.web-identity-token-file=" + tempFile.getAbsolutePath())) { assertThat(context.getBean(AwsCredentialsProvider.class)) @@ -349,7 +346,6 @@ void secretsManagerClientUsesGlobalRegion() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=" + REGION)) { assertThat(context.getEnvironment().getProperty("message")).isEqualTo("value from tests"); @@ -374,7 +370,6 @@ void reloadsProperties() { "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.region=" + REGION, "--spring.cloud.aws.secretsmanager.reload.strategy=refresh", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.reload.period=PT1S", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -401,7 +396,6 @@ void doesNotReloadPropertiesWhenMonitoringIsDisabled() { try (ConfigurableApplicationContext context = application.run( "--spring.config.import=aws-secretsmanager:/config/spring;/config/second", "--spring.cloud.aws.secretsmanager.region=" + REGION, - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.reload.period=PT1S", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", @@ -430,7 +424,6 @@ void reloadsPropertiesWithRestartContextStrategy() { "--spring.cloud.aws.secretsmanager.region=" + REGION, "--spring.cloud.aws.secretsmanager.reload.strategy=RESTART_CONTEXT", "--spring.cloud.aws.secretsmanager.reload.period=PT1S", - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.secretsmanager.reload.max-wait-for-restart=PT1S", "--management.endpoint.restart.enabled=true", "--management.endpoints.web.exposure.include=restart", "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), @@ -459,7 +452,6 @@ private ConfigurableApplicationContext runApplication(SpringApplication applicat String endpointProperty) { return application.run("--spring.config.import=" + springConfigImport, "--spring.cloud.aws.secretsmanager.region=" + REGION, - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--" + endpointProperty + "=" + localstack.getEndpoint(), "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=eu-west-1", "--logging.level.io.awspring.cloud.secretsmanager=debug"); diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java index 1e9f96f17..d4f12cfa6 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/metrics/CloudWatchExportAutoConfigurationIntegrationTests.java @@ -68,7 +68,6 @@ void testCounter() { try (ConfigurableApplicationContext context = application.run( "--spring.cloud.aws.endpoint=" + localstack.getEndpoint(), - "--spring.cloud.aws.s3.encryption.keyId=234abcd-12ab-34cd-56ef-1234567890ab", "--spring.cloud.aws.credentials.access-key=noop", "--spring.cloud.aws.credentials.secret-key=noop", "--spring.cloud.aws.region.static=us-east-1", "--management.cloudwatch.metrics.export.step=5s", "--management.cloudwatch.metrics.export.namespace=awspring/spring-cloud-aws", diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml index 5a0b95bdd..9959c5e32 100644 --- a/spring-cloud-aws-dependencies/pom.xml +++ b/spring-cloud-aws-dependencies/pom.xml @@ -26,7 +26,7 @@ 2.31.0 2.28.2 2.0.4 - 3.2.3 + 3.2.3 1.6 4.1.4 2.1.0 @@ -73,7 +73,7 @@ software.amazon.encryption.s3 amazon-s3-encryption-client-java - ${amazon.encryption.s3} + ${amazon.encryption.s3.version} true From 960af297896f6b19416d1b41524df72ba4226029 Mon Sep 17 00:00:00 2001 From: matejnedic Date: Sat, 2 Nov 2024 08:32:46 +0100 Subject: [PATCH 19/19] Format --- .../cloud/autoconfigure/s3/S3AesProvider.java | 4 ++-- .../autoconfigure/s3/S3AutoConfiguration.java | 6 +++--- .../s3/S3EncryptionConditional.java | 19 +++++++++++++++++-- .../cloud/autoconfigure/s3/S3RsaProvider.java | 9 ++++----- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java index 5a59970da..44d01084e 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3AesProvider.java @@ -19,8 +19,8 @@ /** * Interface for providing {@link SecretKey} when configuring {@link software.amazon.encryption.s3.S3EncryptionClient}. - * Required when encrypting files server side with AES. - * Secret Key should be stored in secure storage, for example AWS Secrets Manager. + * Required when encrypting files server side with AES. Secret Key should be stored in secure storage, for example AWS + * Secrets Manager. * @author Matej Nedic * @since 3.3.0 */ 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 c85fd6c1f..e2a91f384 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 @@ -121,6 +121,7 @@ else if (awsProperties.getEndpoint() != null) { Optional.ofNullable(awsProperties.getDualstackEnabled()).ifPresent(builder::dualstackEnabled); return builder.build(); } + @Conditional(S3EncryptionConditional.class) @ConditionalOnClass(name = "software.amazon.encryption.s3.S3EncryptionClient") @Configuration @@ -128,14 +129,14 @@ public static class S3EncryptionConfiguration { @Bean @ConditionalOnMissingBean - public static S3Client s3EncryptionClient(S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder) { + S3Client s3EncryptionClient(S3EncryptionClient.Builder s3EncryptionBuilder, S3ClientBuilder s3ClientBuilder) { s3EncryptionBuilder.wrappedClient(s3ClientBuilder.build()); return s3EncryptionBuilder.build(); } @Bean @ConditionalOnMissingBean - public static S3EncryptionClient.Builder s3EncrpytionClientBuilder(S3Properties properties, + S3EncryptionClient.Builder s3EncrpytionClientBuilder(S3Properties properties, AwsClientBuilderConfigurer awsClientBuilderConfigurer, ObjectProvider> configurer, ObjectProvider connectionDetails, @@ -186,7 +187,6 @@ S3Client s3Client(S3ClientBuilder s3ClientBuilder) { return s3ClientBuilder.build(); } - @Configuration @ConditionalOnClass(ObjectMapper.class) static class Jackson2JsonS3ObjectConverterConfiguration { diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java index 1b3a43857..f49d0e4ad 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3EncryptionConditional.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 io.awspring.cloud.autoconfigure.s3; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; @@ -5,8 +20,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; /** - * Conditional for creating {@link software.amazon.encryption.s3.S3EncryptionClient}. - * Will only create S3EncryptionClient if one of following is true. + * Conditional for creating {@link software.amazon.encryption.s3.S3EncryptionClient}. Will only create + * S3EncryptionClient if one of following is true. * @author Matej Nedic * @since 3.3.0 */ diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java index a48e8b3ce..cc3daa08a 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/s3/S3RsaProvider.java @@ -17,19 +17,18 @@ import java.security.KeyPair; - /** * Interface for providing {@link KeyPair} when configuring {@link software.amazon.encryption.s3.S3EncryptionClient}. - * Required for encrypting/decrypting files server side with RSA. - * Key pair should be stored in secure storage, for example AWS Secrets Manager. + * Required for encrypting/decrypting files server side with RSA. Key pair should be stored in secure storage, for + * example AWS Secrets Manager. * @author Matej Nedic * @since 3.3.0 */ public interface S3RsaProvider { /** - * Provides KeyPair that will be used to configure {@link software.amazon.encryption.s3.S3EncryptionClient}. - * Advised to fetch and return KeyPair in this method from Secured Storage. + * Provides KeyPair that will be used to configure {@link software.amazon.encryption.s3.S3EncryptionClient}. Advised + * to fetch and return KeyPair in this method from Secured Storage. * @return KeyPair that will be used for encryption/decryption. */ KeyPair generateKeyPair();