From 205edebb520d566aa425b94976016d88fd1fc889 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Sun, 9 Oct 2016 08:30:32 +0200 Subject: [PATCH] Polishing. Rename SecureBackend to SecretBackend. Refactor SecureBackendAccessor to SecretBackendMetadata. Move Property Transformation into PropertyTransformer. Enhance JavaDoc. Remove Properties beans and use EnableProperties annotation. --- .../vault/config/aws/VaultAwsProperties.java | 4 +- .../VaultConfigAwsBootstrapConfiguration.java | 66 ++--- .../config/aws/AwsSecretIntegrationTests.java | 2 +- ...ultConfigConsulBootstrapConfiguration.java | 59 +++-- .../config/consul/VaultConsulProperties.java | 4 +- .../consul/ConsulSecretIntegrationTests.java | 4 +- .../databases/DatabaseSecretProperties.java | 4 +- ...tConfigDatabaseBootstrapConfiguration.java | 56 +++-- .../databases/VaultMySqlProperties.java | 4 +- .../CassandraSecretIntegrationTests.java | 2 +- .../MongoSecretIntegrationTests.java | 2 +- .../MySqlSecretIntegrationTests.java | 2 +- .../PostgreSqlSecretIntegrationTests.java | 2 +- ...tConfigRabbitMqBootstrapConfiguration.java | 59 +++-- .../rabbitmq/VaultRabbitMqProperties.java | 4 +- .../RabbitMqSecretIntegrationTests.java | 2 +- .../config/GenericSecretBackendMetadata.java | 79 ++++++ .../config/LeasingVaultPropertySource.java | 10 +- .../LeasingVaultPropertySourceLocator.java | 11 +- .../vault/config/PropertyNameTransformer.java | 81 ++++++ ...Accessor.java => PropertyTransformer.java} | 27 +- .../vault/config/SecretBackendFactories.java | 76 ++++++ ...actory.java => SecretBackendMetadata.java} | 34 ++- .../config/SecretBackendMetadataFactory.java | 58 +++++ .../vault/config/SecureBackendAccessors.java | 66 ----- .../vault/config/SecureBackendFactories.java | 69 ------ .../config/VaultBootstrapConfiguration.java | 231 +++++++++--------- .../vault/config/VaultConfigOperations.java | 9 +- .../vault/config/VaultConfigTemplate.java | 18 +- .../vault/config/VaultPropertySource.java | 39 ++- .../config/VaultPropertySourceLocator.java | 23 +- ...java => VaultSecretBackendDescriptor.java} | 15 +- .../config/GenericSecretIntegrationTests.java | 6 +- ...ngVaultPropertySourceLocatorUnitTests.java | 4 +- .../LeasingVaultPropertySourceUnitTests.java | 14 +- .../PropertyNameTransformerUnitTests.java | 54 ++++ .../VaultPropertySourceIntegrationTests.java | 3 +- 37 files changed, 724 insertions(+), 479 deletions(-) create mode 100644 spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/GenericSecretBackendMetadata.java create mode 100644 spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/PropertyNameTransformer.java rename spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/{SecureBackendAccessor.java => PropertyTransformer.java} (67%) create mode 100644 spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendFactories.java rename spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/{SecureBackendAccessorFactory.java => SecretBackendMetadata.java} (50%) create mode 100644 spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendMetadataFactory.java delete mode 100644 spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessors.java delete mode 100644 spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendFactories.java rename spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/{VaultSecretBackend.java => VaultSecretBackendDescriptor.java} (63%) create mode 100644 spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/PropertyNameTransformerUnitTests.java diff --git a/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultAwsProperties.java b/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultAwsProperties.java index 1c71dadd..2152c07a 100644 --- a/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultAwsProperties.java +++ b/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultAwsProperties.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.aws; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import lombok.Data; @@ -28,7 +28,7 @@ import lombok.Data; */ @ConfigurationProperties("spring.cloud.vault.aws") @Data -public class VaultAwsProperties implements VaultSecretBackend { +public class VaultAwsProperties implements VaultSecretBackendDescriptor { /** * Enable aws backend usage. diff --git a/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultConfigAwsBootstrapConfiguration.java b/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultConfigAwsBootstrapConfiguration.java index 715cb49a..3c35e42f 100644 --- a/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultConfigAwsBootstrapConfiguration.java +++ b/spring-cloud-vault-config-aws/src/main/java/org/springframework/cloud/vault/config/aws/VaultConfigAwsBootstrapConfiguration.java @@ -19,65 +19,73 @@ import java.util.HashMap; import java.util.Map; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.vault.config.SecureBackendAccessor; -import org.springframework.cloud.vault.config.SecureBackendAccessorFactory; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.PropertyNameTransformer; +import org.springframework.cloud.vault.config.PropertyTransformer; +import org.springframework.cloud.vault.config.SecretBackendMetadata; +import org.springframework.cloud.vault.config.SecretBackendMetadataFactory; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.Assert; /** + * Bootstrap configuration providing support for the AWS secret backend. + * * @author Mark Paluch */ @Configuration -@EnableConfigurationProperties +@EnableConfigurationProperties(VaultAwsProperties.class) public class VaultConfigAwsBootstrapConfiguration { @Bean - public SecureBackendAccessorFactory secureBackendAccessorFactory() { - return new AwsSecureBackendAccessorFactory(); + public SecretBackendMetadataFactory secretBackendMetadataFactory() { + return new AwsSecretBackendMetadataFactory(); } - @Bean - public VaultAwsProperties awsProperties() { - return new VaultAwsProperties(); - } - - static class AwsSecureBackendAccessorFactory - implements SecureBackendAccessorFactory { + static class AwsSecretBackendMetadataFactory + implements SecretBackendMetadataFactory { @Override - public SecureBackendAccessor createSecureBackendAccessor( - VaultAwsProperties properties) { - return forAws(properties); + public SecretBackendMetadata createMetadata( + VaultAwsProperties backendDescriptor) { + return forAws(backendDescriptor); } @Override - public boolean supports(VaultSecretBackend secretBackend) { - return secretBackend instanceof VaultAwsProperties; + public boolean supports(VaultSecretBackendDescriptor backendDescriptor) { + return backendDescriptor instanceof VaultAwsProperties; } /** - * Creates a {@link SecureBackendAccessor} for a secure backend using + * Creates {@link SecretBackendMetadata} for a secret backend using * {@link VaultAwsProperties}. This accessor transforms Vault's username/password * property names to names provided with * {@link VaultAwsProperties#getAccessKeyProperty()} and * {@link VaultAwsProperties#getSecretKeyProperty()}. * * @param properties must not be {@literal null}. - * @return the {@link SecureBackendAccessor} + * @return the {@link SecretBackendMetadata} */ - public static SecureBackendAccessor forAws(final VaultAwsProperties properties) { + public static SecretBackendMetadata forAws(final VaultAwsProperties properties) { + Assert.notNull(properties, "VaultAwsProperties must not be null"); - return new SecureBackendAccessor() { + final PropertyNameTransformer transformer = new PropertyNameTransformer(); + transformer.addKeyTransformation("access_key", + properties.getAccessKeyProperty()); + transformer.addKeyTransformation("secret_key", + properties.getSecretKeyProperty()); + + return new SecretBackendMetadata() { @Override - public Map variables() { + public Map getVariables() { Map variables = new HashMap<>(); + variables.put("backend", properties.getBackend()); variables.put("key", String.format("creds/%s", properties.getRole())); + return variables; } @@ -88,16 +96,8 @@ public class VaultConfigAwsBootstrapConfiguration { } @Override - public Map transformProperties( - Map input) { - - Map result = new HashMap(); - result.put(properties.getAccessKeyProperty(), - input.get("access_key")); - result.put(properties.getSecretKeyProperty(), - input.get("secret_key")); - - return result; + public PropertyTransformer getPropertyTransformer() { + return transformer; } }; } diff --git a/spring-cloud-vault-config-aws/src/test/java/org/springframework/cloud/vault/config/aws/AwsSecretIntegrationTests.java b/spring-cloud-vault-config-aws/src/test/java/org/springframework/cloud/vault/config/aws/AwsSecretIntegrationTests.java index 819b0fda..a909341c 100644 --- a/spring-cloud-vault-config-aws/src/test/java/org/springframework/cloud/vault/config/aws/AwsSecretIntegrationTests.java +++ b/spring-cloud-vault-config-aws/src/test/java/org/springframework/cloud/vault/config/aws/AwsSecretIntegrationTests.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.aws; import static org.assertj.core.api.Assertions.*; import static org.junit.Assume.*; -import static org.springframework.cloud.vault.config.aws.VaultConfigAwsBootstrapConfiguration.AwsSecureBackendAccessorFactory.*; +import static org.springframework.cloud.vault.config.aws.VaultConfigAwsBootstrapConfiguration.AwsSecretBackendMetadataFactory.*; import java.util.Collections; import java.util.HashMap; diff --git a/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConfigConsulBootstrapConfiguration.java b/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConfigConsulBootstrapConfiguration.java index 8cc0a345..c2796ce3 100644 --- a/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConfigConsulBootstrapConfiguration.java +++ b/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConfigConsulBootstrapConfiguration.java @@ -19,65 +19,69 @@ import java.util.HashMap; import java.util.Map; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.vault.config.SecureBackendAccessor; -import org.springframework.cloud.vault.config.SecureBackendAccessorFactory; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.PropertyNameTransformer; +import org.springframework.cloud.vault.config.PropertyTransformer; +import org.springframework.cloud.vault.config.SecretBackendMetadata; +import org.springframework.cloud.vault.config.SecretBackendMetadataFactory; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.Assert; /** + * Bootstrap configuration providing support for the Consul secret backend. + * * @author Mark Paluch */ @Configuration -@EnableConfigurationProperties +@EnableConfigurationProperties(VaultConsulProperties.class) public class VaultConfigConsulBootstrapConfiguration { @Bean - public SecureBackendAccessorFactory secureBackendAccessorFactory() { - return new ConsulSecureBackendAccessorFactory(); + public SecretBackendMetadataFactory secretBackendAccessorFactory() { + return new ConsulSecretBackendMetadataFactory(); } - @Bean - public VaultConsulProperties vaultConsulProperties() { - return new VaultConsulProperties(); - } - - static class ConsulSecureBackendAccessorFactory - implements SecureBackendAccessorFactory { + static class ConsulSecretBackendMetadataFactory + implements SecretBackendMetadataFactory { @Override - public SecureBackendAccessor createSecureBackendAccessor( - VaultConsulProperties properties) { - return forConsul(properties); + public SecretBackendMetadata createMetadata( + VaultConsulProperties backendDescriptor) { + return forConsul(backendDescriptor); } @Override - public boolean supports(VaultSecretBackend secretBackend) { - return secretBackend instanceof VaultConsulProperties; + public boolean supports(VaultSecretBackendDescriptor backendDescriptor) { + return backendDescriptor instanceof VaultConsulProperties; } /** - * Creates a {@link SecureBackendAccessor} for a secure backend using + * Creates a {@link SecretBackendMetadata} for a secret backend using * {@link VaultConsulProperties}. This accessor transforms Vault's token property * names to names provided with {@link VaultConsulProperties#getTokenProperty()}. * * @param properties must not be {@literal null}. - * @return the {@link SecureBackendAccessor} + * @return the {@link SecretBackendMetadata} */ - public static SecureBackendAccessor forConsul( + public static SecretBackendMetadata forConsul( final VaultConsulProperties properties) { Assert.notNull(properties, "VaultConsulProperties must not be null"); - return new SecureBackendAccessor() { + final PropertyNameTransformer transformer = new PropertyNameTransformer(); + transformer.addKeyTransformation("token", properties.getTokenProperty()); + + return new SecretBackendMetadata() { @Override - public Map variables() { + public Map getVariables() { Map variables = new HashMap<>(); + variables.put("backend", properties.getBackend()); variables.put("key", String.format("creds/%s", properties.getRole())); + return variables; } @@ -88,13 +92,8 @@ public class VaultConfigConsulBootstrapConfiguration { } @Override - public Map transformProperties( - Map input) { - - Map result = new HashMap(); - result.put(properties.getTokenProperty(), input.get("token")); - - return result; + public PropertyTransformer getPropertyTransformer() { + return transformer; } }; } diff --git a/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConsulProperties.java b/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConsulProperties.java index fe5fdc2d..8b451ee6 100644 --- a/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConsulProperties.java +++ b/spring-cloud-vault-config-consul/src/main/java/org/springframework/cloud/vault/config/consul/VaultConsulProperties.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.consul; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import lombok.Data; @@ -28,7 +28,7 @@ import lombok.Data; */ @ConfigurationProperties("spring.cloud.vault.consul") @Data -public class VaultConsulProperties implements VaultSecretBackend { +public class VaultConsulProperties implements VaultSecretBackendDescriptor { /** * Enable consul backend usage. diff --git a/spring-cloud-vault-config-consul/src/test/java/org/springframework/cloud/vault/config/consul/ConsulSecretIntegrationTests.java b/spring-cloud-vault-config-consul/src/test/java/org/springframework/cloud/vault/config/consul/ConsulSecretIntegrationTests.java index 22752843..b5311c62 100644 --- a/spring-cloud-vault-config-consul/src/test/java/org/springframework/cloud/vault/config/consul/ConsulSecretIntegrationTests.java +++ b/spring-cloud-vault-config-consul/src/test/java/org/springframework/cloud/vault/config/consul/ConsulSecretIntegrationTests.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.consul; import static org.assertj.core.api.Assertions.*; import static org.junit.Assume.*; -import static org.springframework.cloud.vault.config.consul.VaultConfigConsulBootstrapConfiguration.ConsulSecureBackendAccessorFactory.*; +import static org.springframework.cloud.vault.config.consul.VaultConfigConsulBootstrapConfiguration.ConsulSecretBackendMetadataFactory.*; import java.net.InetSocketAddress; import java.util.Collections; @@ -26,7 +26,7 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; -import org.springframework.boot.test.TestRestTemplate; + import org.springframework.cloud.vault.config.VaultConfigOperations; import org.springframework.cloud.vault.config.VaultConfigTemplate; import org.springframework.cloud.vault.config.VaultProperties; diff --git a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/DatabaseSecretProperties.java b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/DatabaseSecretProperties.java index d9fc5cb4..d0d0cda8 100644 --- a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/DatabaseSecretProperties.java +++ b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/DatabaseSecretProperties.java @@ -15,14 +15,14 @@ */ package org.springframework.cloud.vault.config.databases; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; /** * Configuration properties interface for database secrets. * * @author Mark Paluch */ -public interface DatabaseSecretProperties extends VaultSecretBackend { +public interface DatabaseSecretProperties extends VaultSecretBackendDescriptor { /** * Role name. diff --git a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java index 829ea1e4..f8dc4abe 100644 --- a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java +++ b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java @@ -19,14 +19,19 @@ import java.util.HashMap; import java.util.Map; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.vault.config.SecureBackendAccessor; -import org.springframework.cloud.vault.config.SecureBackendAccessorFactory; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.PropertyNameTransformer; +import org.springframework.cloud.vault.config.PropertyTransformer; +import org.springframework.cloud.vault.config.SecretBackendMetadata; +import org.springframework.cloud.vault.config.SecretBackendMetadataFactory; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.Assert; /** + * Bootstrap configuration providing support for the Database secret backends such as + * MySQL, PostreSQL, Apache Cassandra and MongoDB. + * * @author Mark Paluch */ @Configuration @@ -36,42 +41,49 @@ import org.springframework.util.Assert; public class VaultConfigDatabaseBootstrapConfiguration { @Bean - public SecureBackendAccessorFactory secureBackendAccessorFactory() { - return new DatabaseSecureBackendAccessorFactory(); + public SecretBackendMetadataFactory secretBackendMetadataFactory() { + return new DatabaseSecretBackendMetadataFactory(); } - static class DatabaseSecureBackendAccessorFactory - implements SecureBackendAccessorFactory { + static class DatabaseSecretBackendMetadataFactory + implements SecretBackendMetadataFactory { @Override - public SecureBackendAccessor createSecureBackendAccessor( - DatabaseSecretProperties configurationProperties) { - return forDatabase(configurationProperties); + public SecretBackendMetadata createMetadata( + DatabaseSecretProperties backendDescriptor) { + return forDatabase(backendDescriptor); } @Override - public boolean supports(VaultSecretBackend secretBackend) { - return secretBackend instanceof DatabaseSecretProperties; + public boolean supports(VaultSecretBackendDescriptor backendDescriptor) { + return backendDescriptor instanceof DatabaseSecretProperties; } /** - * Creates a {@link SecureBackendAccessor} for a secure backend using + * Creates a {@link SecretBackendMetadata} for a secret backend using * {@link DatabaseSecretProperties}. This accessor transforms Vault's * username/password property names to names provided with * {@link DatabaseSecretProperties#getUsernameProperty()} and * {@link DatabaseSecretProperties#getPasswordProperty()}. * * @param properties must not be {@literal null}. - * @return the {@link SecureBackendAccessor} + * @return the {@link SecretBackendMetadata} */ - public static SecureBackendAccessor forDatabase( + public static SecretBackendMetadata forDatabase( final DatabaseSecretProperties properties) { + Assert.notNull(properties, "DatabaseSecretProperties must not be null"); - return new SecureBackendAccessor() { + final PropertyNameTransformer transformer = new PropertyNameTransformer(); + transformer.addKeyTransformation("username", + properties.getUsernameProperty()); + transformer.addKeyTransformation("password", + properties.getPasswordProperty()); + + return new SecretBackendMetadata() { @Override - public Map variables() { + public Map getVariables() { Map variables = new HashMap<>(); variables.put("backend", properties.getBackend()); @@ -86,14 +98,8 @@ public class VaultConfigDatabaseBootstrapConfiguration { } @Override - public Map transformProperties( - Map input) { - - Map result = new HashMap<>(); - result.put(properties.getUsernameProperty(), input.get("username")); - result.put(properties.getPasswordProperty(), input.get("password")); - - return result; + public PropertyTransformer getPropertyTransformer() { + return transformer; } }; } diff --git a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java index f216f117..78e1b310 100644 --- a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java +++ b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java @@ -2,7 +2,7 @@ package org.springframework.cloud.vault.config.databases; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import lombok.Data; @@ -14,7 +14,7 @@ import lombok.Data; @ConfigurationProperties("spring.cloud.vault.mysql") @Data public class VaultMySqlProperties - implements DatabaseSecretProperties, VaultSecretBackend { + implements DatabaseSecretProperties, VaultSecretBackendDescriptor { /** * Enable mysql backend usage. diff --git a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/CassandraSecretIntegrationTests.java b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/CassandraSecretIntegrationTests.java index 92c55905..04157239 100644 --- a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/CassandraSecretIntegrationTests.java +++ b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/CassandraSecretIntegrationTests.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.databases; import static org.assertj.core.api.Assertions.*; import static org.junit.Assume.*; -import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*; +import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecretBackendMetadataFactory.*; import java.net.InetSocketAddress; import java.util.Collections; diff --git a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MongoSecretIntegrationTests.java b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MongoSecretIntegrationTests.java index be9e31d7..10674c2e 100644 --- a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MongoSecretIntegrationTests.java +++ b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MongoSecretIntegrationTests.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.databases; import static org.assertj.core.api.Assertions.*; import static org.junit.Assume.*; -import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*; +import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecretBackendMetadataFactory.*; import java.net.InetSocketAddress; import java.util.Collections; diff --git a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MySqlSecretIntegrationTests.java b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MySqlSecretIntegrationTests.java index 547e96d7..871add22 100644 --- a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MySqlSecretIntegrationTests.java +++ b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/MySqlSecretIntegrationTests.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.databases; import static org.assertj.core.api.Assertions.*; import static org.junit.Assume.*; -import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*; +import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecretBackendMetadataFactory.*; import java.net.InetSocketAddress; import java.util.Collections; diff --git a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/PostgreSqlSecretIntegrationTests.java b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/PostgreSqlSecretIntegrationTests.java index 2a37e3f4..a52c3f3c 100644 --- a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/PostgreSqlSecretIntegrationTests.java +++ b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/PostgreSqlSecretIntegrationTests.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.databases; import static org.assertj.core.api.Assertions.*; import static org.junit.Assume.*; -import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*; +import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecretBackendMetadataFactory.*; import java.net.InetSocketAddress; import java.util.Collections; diff --git a/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultConfigRabbitMqBootstrapConfiguration.java b/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultConfigRabbitMqBootstrapConfiguration.java index d0015d1c..dedc5db3 100644 --- a/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultConfigRabbitMqBootstrapConfiguration.java +++ b/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultConfigRabbitMqBootstrapConfiguration.java @@ -19,14 +19,18 @@ import java.util.HashMap; import java.util.Map; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.vault.config.SecureBackendAccessor; -import org.springframework.cloud.vault.config.SecureBackendAccessorFactory; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.PropertyNameTransformer; +import org.springframework.cloud.vault.config.PropertyTransformer; +import org.springframework.cloud.vault.config.SecretBackendMetadata; +import org.springframework.cloud.vault.config.SecretBackendMetadataFactory; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.Assert; /** + * Bootstrap configuration providing support for the RabbitMQ secret backend. + * * @author Mark Paluch */ @Configuration @@ -34,8 +38,8 @@ import org.springframework.util.Assert; public class VaultConfigRabbitMqBootstrapConfiguration { @Bean - public SecureBackendAccessorFactory secureBackendAccessorFactory() { - return new RabbitMqSecureBackendAccessorFactory(); + public SecretBackendMetadataFactory secureBackendAccessorFactory() { + return new RabbitMqSecretBackendMetadataFactory(); } @Bean @@ -43,42 +47,51 @@ public class VaultConfigRabbitMqBootstrapConfiguration { return new VaultRabbitMqProperties(); } - static class RabbitMqSecureBackendAccessorFactory - implements SecureBackendAccessorFactory { + static class RabbitMqSecretBackendMetadataFactory + implements SecretBackendMetadataFactory { @Override - public SecureBackendAccessor createSecureBackendAccessor( - VaultRabbitMqProperties properties) { - return forRabbitMq(properties); + public SecretBackendMetadata createMetadata( + VaultRabbitMqProperties backendDescriptor) { + return forRabbitMq(backendDescriptor); } @Override - public boolean supports(VaultSecretBackend secretBackend) { - return secretBackend instanceof VaultRabbitMqProperties; + public boolean supports(VaultSecretBackendDescriptor backendDescriptor) { + return backendDescriptor instanceof VaultRabbitMqProperties; } /** - * Creates a {@link SecureBackendAccessor} for a secure backend using + * Creates a {@link SecretBackendMetadata} for a secret backend using * {@link VaultRabbitMqProperties}. This accessor transforms Vault's * username/password property names to names provided with * {@link VaultRabbitMqProperties#getUsernameProperty()} and * {@link VaultRabbitMqProperties#getPasswordProperty()}. * * @param properties must not be {@literal null}. - * @return the {@link SecureBackendAccessor} + * @return the {@link SecretBackendMetadata} */ - public static SecureBackendAccessor forRabbitMq( + public static SecretBackendMetadata forRabbitMq( final VaultRabbitMqProperties properties) { - Assert.notNull(properties, "DatabaseSecretProperties must not be null"); - return new SecureBackendAccessor() { + Assert.notNull(properties, "VaultRabbitMqProperties must not be null"); + + final PropertyNameTransformer transformer = new PropertyNameTransformer(); + transformer.addKeyTransformation("username", + properties.getUsernameProperty()); + transformer.addKeyTransformation("password", + properties.getPasswordProperty()); + + return new SecretBackendMetadata() { @Override - public Map variables() { + public Map getVariables() { Map variables = new HashMap<>(); + variables.put("backend", properties.getBackend()); variables.put("key", String.format("creds/%s", properties.getRole())); + return variables; } @@ -89,14 +102,8 @@ public class VaultConfigRabbitMqBootstrapConfiguration { } @Override - public Map transformProperties( - Map input) { - - Map result = new HashMap(); - result.put(properties.getUsernameProperty(), input.get("username")); - result.put(properties.getPasswordProperty(), input.get("password")); - - return result; + public PropertyTransformer getPropertyTransformer() { + return transformer; } }; } diff --git a/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultRabbitMqProperties.java b/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultRabbitMqProperties.java index 98788560..cd84e60f 100644 --- a/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultRabbitMqProperties.java +++ b/spring-cloud-vault-config-rabbitmq/src/main/java/org/springframework/cloud/vault/config/rabbitmq/VaultRabbitMqProperties.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.rabbitmq; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.vault.config.VaultSecretBackend; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import lombok.Data; @@ -28,7 +28,7 @@ import lombok.Data; */ @ConfigurationProperties("spring.cloud.vault.rabbitmq") @Data -public class VaultRabbitMqProperties implements VaultSecretBackend { +public class VaultRabbitMqProperties implements VaultSecretBackendDescriptor { /** * Enable rabbitmq backend usage. diff --git a/spring-cloud-vault-config-rabbitmq/src/test/java/org/springframework/cloud/vault/config/rabbitmq/RabbitMqSecretIntegrationTests.java b/spring-cloud-vault-config-rabbitmq/src/test/java/org/springframework/cloud/vault/config/rabbitmq/RabbitMqSecretIntegrationTests.java index 38866399..be6037b3 100644 --- a/spring-cloud-vault-config-rabbitmq/src/test/java/org/springframework/cloud/vault/config/rabbitmq/RabbitMqSecretIntegrationTests.java +++ b/spring-cloud-vault-config-rabbitmq/src/test/java/org/springframework/cloud/vault/config/rabbitmq/RabbitMqSecretIntegrationTests.java @@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.rabbitmq; import static org.assertj.core.api.Assertions.*; import static org.junit.Assume.*; -import static org.springframework.cloud.vault.config.rabbitmq.VaultConfigRabbitMqBootstrapConfiguration.RabbitMqSecureBackendAccessorFactory.*; +import static org.springframework.cloud.vault.config.rabbitmq.VaultConfigRabbitMqBootstrapConfiguration.RabbitMqSecretBackendMetadataFactory.*; import java.net.InetSocketAddress; import java.util.Collections; diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/GenericSecretBackendMetadata.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/GenericSecretBackendMetadata.java new file mode 100644 index 00000000..914c7ff7 --- /dev/null +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/GenericSecretBackendMetadata.java @@ -0,0 +1,79 @@ +/* + * Copyright 2016 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 + * + * http://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 org.springframework.cloud.vault.config; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.util.Assert; + +/** + * {@link SecretBackendMetadata} for the {@code generic} secret backend. + * + * @author Mark Paluch + */ +class GenericSecretBackendMetadata implements SecretBackendMetadata { + + private final String secretBackendPath; + + private final String key; + + private GenericSecretBackendMetadata(String secretBackendPath, String key) { + + Assert.hasText(secretBackendPath, "Secret backend path must not be empty"); + Assert.hasText(key, "Key must not be empty"); + + this.key = key; + this.secretBackendPath = secretBackendPath; + } + + /** + * Create a {@link SecretBackendMetadata} for the {@code generic} secret backend given + * a {@code secretBackendPath} and {@code key}. + * + * @param secretBackendPath the secret backend mount path without leading/trailing + * slashes, must not be empty or {@literal null}. + * @param key the key within the secret backend. May contain slashes but not + * leading/trailing slashes, must not be empty or {@literal null}. + * @return the {@link SecretBackendMetadata} + */ + public static SecretBackendMetadata create(final String secretBackendPath, + final String key) { + return new GenericSecretBackendMetadata(secretBackendPath, key); + } + + @Override + public String getName() { + return String.format("%s/%s", secretBackendPath, key); + } + + @Override + public PropertyTransformer getPropertyTransformer() { + return null; + } + + @Override + public Map getVariables() { + + Map variables = new HashMap<>(); + + variables.put("backend", secretBackendPath); + variables.put("key", key); + + return variables; + } +} diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySource.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySource.java index cf6e3313..7d15cc35 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySource.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySource.java @@ -66,15 +66,15 @@ class LeasingVaultPropertySource extends VaultPropertySource implements Disposab * Creates a new {@link VaultPropertySource}. * * @param operations must not be {@literal null}. - * @param properties must not be {@literal null}. - * @param secureBackendAccessor must not be {@literal null}. + * @param failFast fail if properties could not be read because of access errors. + * @param secretBackendMetadata must not be {@literal null}. * @param taskScheduler must not be {@literal null}. */ - public LeasingVaultPropertySource(VaultConfigTemplate operations, - VaultProperties properties, SecureBackendAccessor secureBackendAccessor, + public LeasingVaultPropertySource(VaultConfigOperations operations, boolean failFast, + SecretBackendMetadata secretBackendMetadata, TaskScheduler taskScheduler) { - super(operations, properties, secureBackendAccessor); + super(operations, failFast, secretBackendMetadata); Assert.notNull(taskScheduler, "TaskScheduler must not be null"); diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocator.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocator.java index dec6f9dd..95d55f4e 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocator.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocator.java @@ -37,7 +37,7 @@ import lombok.extern.slf4j.Slf4j; class LeasingVaultPropertySourceLocator extends VaultPropertySourceLocator implements DisposableBean { - private final VaultConfigTemplate operations; + private final VaultConfigOperations operations; private final VaultProperties properties; @@ -53,10 +53,10 @@ class LeasingVaultPropertySourceLocator extends VaultPropertySourceLocator * @param backendAccessors must not be {@literal null}. * @param taskScheduler must not be {@literal null}. */ - public LeasingVaultPropertySourceLocator(VaultConfigTemplate operations, + public LeasingVaultPropertySourceLocator(VaultConfigOperations operations, VaultProperties properties, VaultGenericBackendProperties genericBackendProperties, - Collection backendAccessors, + Collection backendAccessors, TaskScheduler taskScheduler) { super(operations, properties, genericBackendProperties, backendAccessors); @@ -72,9 +72,10 @@ class LeasingVaultPropertySourceLocator extends VaultPropertySourceLocator @Override protected VaultPropertySource createVaultPropertySource( - SecureBackendAccessor accessor) { + SecretBackendMetadata accessor) { + LeasingVaultPropertySource propertySource = new LeasingVaultPropertySource( - this.operations, this.properties, accessor, taskScheduler); + this.operations, this.properties.isFailFast(), accessor, taskScheduler); locatedPropertySources.add(propertySource); diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/PropertyNameTransformer.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/PropertyNameTransformer.java new file mode 100644 index 00000000..83b695e9 --- /dev/null +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/PropertyNameTransformer.java @@ -0,0 +1,81 @@ +/* + * Copyright 2016 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 + * + * http://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 org.springframework.cloud.vault.config; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.util.Assert; + +/** + * {@link PropertyTransformer} to transform a {@link Map} of properties by applying key + * name translation. + *

+ * Existing keys will be transformed to a target key name while retaining the original + * value. Key name translation will leave other, not specified key names untouched. + * + * @author Mark Paluch + */ +public class PropertyNameTransformer implements PropertyTransformer { + + private final Map nameMapping = new HashMap<>(); + + /** + * Create a new {@link PropertyNameTransformer}. + */ + public PropertyNameTransformer() { + } + + /** + * Adds a key name transformation by providing a {@code sourceKeyName} and a + * {@code targetKeyName}. + * + * @param sourceKeyName must not be empty or {@literal null}. + * @param targetKeyName must not be empty or {@literal null}. + */ + public void addKeyTransformation(String sourceKeyName, String targetKeyName) { + + Assert.hasText(sourceKeyName, "Source key name must not be empty"); + Assert.hasText(targetKeyName, "Target key name must not be empty"); + + nameMapping.put(sourceKeyName, targetKeyName); + } + + @Override + public Map transformProperties(Map input) { + + if (input == null) { + return null; + } + + Map transformed = new LinkedHashMap<>(input.size(), 1); + + for (String key : input.keySet()) { + + String value = input.get(key); + String translatedKey = key; + + if (nameMapping.containsKey(key)) { + translatedKey = nameMapping.get(key); + } + + transformed.put(translatedKey, value); + } + + return transformed; + } +} diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessor.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/PropertyTransformer.java similarity index 67% rename from spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessor.java rename to spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/PropertyTransformer.java index 437a07d3..24aee77d 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessor.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/PropertyTransformer.java @@ -13,34 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.cloud.vault.config; import java.util.Map; /** - * Accessor for a secure backend. Provides URL path variables and can transform - * properties. + * Strategy interface to transform properties to a new key-value {@link Map}. Property + * transformation can remap property names, adjust values or change the property map + * entirely. + *

+ * Implementors usually transform property names to target property names by retaining the + * value. + * * @author Mark Paluch */ -public interface SecureBackendAccessor { +public interface PropertyTransformer { /** - * - * @return URL template variables. - */ - Map variables(); - - /** - * + * Transform properties by creating a new map using the transformed property set. + * * @param input must not be {@literal null}. * @return transformed properties. */ Map transformProperties(Map input); - - /** - * - * @return the name for this accessor. - */ - String getName(); } diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendFactories.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendFactories.java new file mode 100644 index 00000000..45154eae --- /dev/null +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendFactories.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 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 + * + * http://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 org.springframework.cloud.vault.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; + +/** + * Utility class to create {@link SecretBackendMetadata} from a + * {@link SecretBackendMetadataFactory}. + * + * @author Mark Paluch + */ +@Slf4j +@UtilityClass +class SecretBackendFactories { + + public static Collection createSecretBackendMetadata( + Collection vaultSecretBackendDescriptors, + Collection> factories) { + + List accessors = new ArrayList<>(); + + for (VaultSecretBackendDescriptor vaultSecretBackendDescriptor : vaultSecretBackendDescriptors) { + + if (!vaultSecretBackendDescriptor.isEnabled()) { + continue; + } + + SecretBackendMetadata metadata = createSecretBackendMetadata(factories, + vaultSecretBackendDescriptor); + + if (metadata == null) { + log.warn(String.format("Cannot create SecretBackendMetadata for %s", + vaultSecretBackendDescriptor)); + continue; + } + + accessors.add(metadata); + } + + return accessors; + } + + private static SecretBackendMetadata createSecretBackendMetadata( + Collection> factories, + VaultSecretBackendDescriptor vaultSecretBackendDescriptor) { + + SecretBackendMetadata accessor = null; + for (SecretBackendMetadataFactory factory : factories) { + + if (factory.supports(vaultSecretBackendDescriptor)) { + accessor = factory.createMetadata(vaultSecretBackendDescriptor); + break; + } + } + return accessor; + } +} diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessorFactory.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendMetadata.java similarity index 50% rename from spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessorFactory.java rename to spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendMetadata.java index cac68d6e..d08c32f3 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessorFactory.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendMetadata.java @@ -13,28 +13,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.cloud.vault.config; +import java.util.Map; + /** - * Factory to convert {@link VaultSecretBackend} instance to a - * {@link SecureBackendAccessor}. + * Interface specifying the API to obtain URL variables and optionally a + * {@link PropertyTransformer}. Typically used by {@link VaultPropertySource}. * * @author Mark Paluch + * @see PropertyTransformer */ -public interface SecureBackendAccessorFactory { +public interface SecretBackendMetadata { /** - * Converts a {@link VaultSecretBackend} into a {@link SecureBackendAccessor}. - * @param configurationProperties - * @return the {@link SecureBackendAccessor}. + * Return a readable name of this secret backend. + * + * @return the name of this secret backend. */ - SecureBackendAccessor createSecureBackendAccessor(T configurationProperties); + String getName(); /** - * Checks whether the {@link VaultSecretBackend} is supported by this - * {@link SecureBackendAccessorFactory}. - * @param secretBackend must not be {@literal null}. - * @return {@literal true} if the given {@link VaultSecretBackend} is supported + * Return a {@link PropertyTransformer} to post-process properties retrieved from + * Vault. + * + * @return the property transformer or {@literal null} if there's no property + * transformer. */ - boolean supports(VaultSecretBackend secretBackend); + PropertyTransformer getPropertyTransformer(); + + /** + * @return URL template variables. + */ + Map getVariables(); } diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendMetadataFactory.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendMetadataFactory.java new file mode 100644 index 00000000..61bfa65e --- /dev/null +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecretBackendMetadataFactory.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 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 + * + * http://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 org.springframework.cloud.vault.config; + +/** + * Strategy interface to create {@link SecretBackendMetadata} from + * {@link VaultSecretBackendDescriptor} properties. + * + *

+ * Classes implementing this interface must implement + * {@link #supports(VaultSecretBackendDescriptor)} to determine whether a particular + * {@link VaultSecretBackendDescriptor} is supported by this implementation. If a + * {@link VaultSecretBackendDescriptor} instance is supported by the implementation, it + * must be able to create {@link SecretBackendMetadata}, see + * {@link #createMetadata(VaultSecretBackendDescriptor)}. + * + *

+ * Typically implemented by secret backend providers that implement access to a particular + * backend using read operations. + * + * @author Mark Paluch + * @see SecretBackendMetadata + * @see VaultSecretBackendDescriptor + */ +public interface SecretBackendMetadataFactory { + + /** + * Converts a {@link VaultSecretBackendDescriptor} into a + * {@link SecretBackendMetadata}. + * + * @param backendDescriptor must not be {@literal null}. + * @return the {@link SecretBackendMetadata}. + */ + SecretBackendMetadata createMetadata(T backendDescriptor); + + /** + * Checks whether the {@link VaultSecretBackendDescriptor} is supported by this + * {@link SecretBackendMetadataFactory}. + * + * @param backendDescriptor must not be {@literal null}. + * @return {@literal true} if the given {@link VaultSecretBackendDescriptor} is + * supported. + */ + boolean supports(VaultSecretBackendDescriptor backendDescriptor); +} diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessors.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessors.java deleted file mode 100644 index 48beb94c..00000000 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendAccessors.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2016 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 - * - * http://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 org.springframework.cloud.vault.config; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.util.Assert; - -/** - * Collection of common used {@link SecureBackendAccessor accessors} to access secure - * backends. - * - * @author Mark Paluch - */ -class SecureBackendAccessors { - - /** - * Creates a {@link SecureBackendAccessor} for the {@code generic} secure backend. - * - * @param secretBackendPath must not be {@literal null} and not empty. - * @param key must not be {@literal null} and not empty. - * @return the {@link SecureBackendAccessor} - */ - public static SecureBackendAccessor generic(final String secretBackendPath, - final String key) { - - Assert.hasText(secretBackendPath, "Secret Backend Path must not be empty"); - Assert.hasText(key, "Key must not be empty"); - - return new SecureBackendAccessor() { - - @Override - public Map variables() { - Map variables = new HashMap<>(); - variables.put("backend", secretBackendPath); - variables.put("key", key); - return variables; - } - - @Override - public String getName() { - return String.format("%s/%s", secretBackendPath, key); - } - - @Override - public Map transformProperties(Map input) { - return input; - } - }; - } -} diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendFactories.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendFactories.java deleted file mode 100644 index 82b7e0f6..00000000 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/SecureBackendFactories.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016 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 - * - * http://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 org.springframework.cloud.vault.config; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -/** - * @author Mark Paluch - */ -@Slf4j -public class SecureBackendFactories { - - public static Collection createBackendAcessors( - Collection vaultSecretBackends, - Collection> factories) { - - List accessors = new ArrayList<>(); - - for (VaultSecretBackend vaultSecretBackend : vaultSecretBackends) { - - if (!vaultSecretBackend.isEnabled()) { - continue; - } - - SecureBackendAccessor accessor = createSecureBackendAccessor(factories, - vaultSecretBackend); - if (accessor == null) { - log.warn(String.format("Cannot create SecureBackendAccessor for %s", - vaultSecretBackend)); - continue; - } - - accessors.add(accessor); - } - - return accessors; - } - - private static SecureBackendAccessor createSecureBackendAccessor( - Collection> factories, - VaultSecretBackend vaultSecretBackend) { - SecureBackendAccessor accessor = null; - for (SecureBackendAccessorFactory factory : factories) { - - if (factory.supports(vaultSecretBackend)) { - accessor = factory.createSecureBackendAccessor(vaultSecretBackend); - break; - } - } - return accessor; - } -} diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java index d648a157..30aa75de 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java @@ -81,20 +81,21 @@ public class VaultBootstrapConfiguration { private final VaultProperties vaultProperties; - private final Collection vaultSecretBackends; + private final Collection vaultSecretBackendDescriptors; - private final Collection> factories; + private final Collection> factories; + @SuppressWarnings("unchecked") public VaultBootstrapConfiguration(ConfigurableApplicationContext applicationContext, VaultProperties vaultProperties) { this.applicationContext = applicationContext; this.vaultProperties = vaultProperties; - this.vaultSecretBackends = applicationContext - .getBeansOfType(VaultSecretBackend.class).values(); + this.vaultSecretBackendDescriptors = applicationContext + .getBeansOfType(VaultSecretBackendDescriptor.class).values(); this.factories = (Collection) applicationContext - .getBeansOfType(SecureBackendAccessorFactory.class).values(); + .getBeansOfType(SecretBackendMetadataFactory.class).values(); } @Bean @@ -103,8 +104,8 @@ public class VaultBootstrapConfiguration { VaultGenericBackendProperties vaultGenericBackendProperties, ObjectProvider> taskSchedulerProvider) { - Collection backendAccessors = SecureBackendFactories - .createBackendAcessors(vaultSecretBackends, factories); + Collection backendAccessors = SecretBackendFactories + .createSecretBackendMetadata(vaultSecretBackendDescriptors, factories); VaultConfigTemplate vaultConfigTemplate = new VaultConfigTemplate(operations, vaultProperties); @@ -123,114 +124,6 @@ public class VaultBootstrapConfiguration { vaultGenericBackendProperties, backendAccessors); } - @Bean - @ConditionalOnMissingBean - public ClientAuthentication clientAuthentication() { - - VaultClient vaultClient = vaultClient(); - - switch (vaultProperties.getAuthentication()) { - - case TOKEN: - Assert.hasText(vaultProperties.getToken(), - "Token (spring.cloud.vault.token) must not be empty"); - return new TokenAuthentication(vaultProperties.getToken()); - - case APPID: - return appIdAuthentication(vaultProperties, vaultClient); - - case CERT: - return new ClientCertificateAuthentication(vaultClient); - - case AWS_EC2: - return awsEc2Authentication(vaultProperties, vaultClient); - - case CUBBYHOLE: - return cubbyholeAuthentication(vaultClient); - - } - - throw new UnsupportedOperationException( - String.format("Client authentication %s not supported", - vaultProperties.getAuthentication())); - } - - private ClientAuthentication appIdAuthentication(VaultProperties vaultProperties, - VaultClient vaultClient) { - - VaultProperties.AppIdProperties appId = vaultProperties.getAppId(); - Assert.hasText(appId.getUserId(), - "UserId (spring.cloud.vault.app-id.user-id) must not be empty"); - - AppIdAuthenticationOptions authenticationOptions = AppIdAuthenticationOptions - .builder().appId(vaultProperties.getApplicationName()) // - .path(appId.getAppIdPath()) // - .userIdMechanism(getClientAuthentication(appId)).build(); - - return new AppIdAuthentication(authenticationOptions, vaultClient); - } - - private AppIdUserIdMechanism getClientAuthentication( - VaultProperties.AppIdProperties appId) { - - try { - Class userIdClass = ClassUtils.forName(appId.getUserId(), null); - return (AppIdUserIdMechanism) BeanUtils.instantiateClass(userIdClass); - } - catch (ClassNotFoundException ex) { - - switch (appId.getUserId().toUpperCase()) { - - case VaultProperties.AppIdProperties.IP_ADDRESS: - return new IpAddressUserId(); - - case VaultProperties.AppIdProperties.MAC_ADDRESS: - - if (StringUtils.hasText(appId.getNetworkInterface())) { - try { - return new MacAddressUserId( - Integer.parseInt(appId.getNetworkInterface())); - } - catch (NumberFormatException e) { - return new MacAddressUserId(appId.getNetworkInterface()); - } - } - - return new MacAddressUserId(); - default: - return new StaticUserId(appId.getUserId()); - } - } - } - - private ClientAuthentication awsEc2Authentication(VaultProperties vaultProperties, - VaultClient vaultClient) { - - VaultProperties.AwsEc2Properties awsEc2 = vaultProperties.getAwsEc2(); - - AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions - .builder().role(awsEc2.getRole()) // - .path(awsEc2.getAwsEc2Path()) // - .identityDocumentUri(URI.create(awsEc2.getIdentityDocument())) // - .build(); - - return new AwsEc2Authentication(authenticationOptions, vaultClient, - vaultClient.getRestTemplate()); - } - - private ClientAuthentication cubbyholeAuthentication(VaultClient vaultClient) { - - Assert.hasText(vaultProperties.getToken(), - "Initial Token (spring.cloud.vault.token) for Cubbyhole authentication must not be empty"); - - CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions.builder() // - .wrapped() // - .initialToken(VaultToken.of(vaultProperties.getToken())) // - .build(); - - return new CubbyholeAuthentication(options, vaultClient); - } - /** * Creates a {@link ClientFactoryWrapper} containing a * {@link ClientHttpRequestFactory}. {@link ClientHttpRequestFactory} is not exposed @@ -344,6 +237,114 @@ public class VaultBootstrapConfiguration { return new SimpleSessionManager(clientAuthentication); } + @Bean + @ConditionalOnMissingBean + public ClientAuthentication clientAuthentication() { + + VaultClient vaultClient = vaultClient(); + + switch (vaultProperties.getAuthentication()) { + + case TOKEN: + Assert.hasText(vaultProperties.getToken(), + "Token (spring.cloud.vault.token) must not be empty"); + return new TokenAuthentication(vaultProperties.getToken()); + + case APPID: + return appIdAuthentication(vaultProperties, vaultClient); + + case CERT: + return new ClientCertificateAuthentication(vaultClient); + + case AWS_EC2: + return awsEc2Authentication(vaultProperties, vaultClient); + + case CUBBYHOLE: + return cubbyholeAuthentication(vaultClient); + + } + + throw new UnsupportedOperationException( + String.format("Client authentication %s not supported", + vaultProperties.getAuthentication())); + } + + private ClientAuthentication appIdAuthentication(VaultProperties vaultProperties, + VaultClient vaultClient) { + + VaultProperties.AppIdProperties appId = vaultProperties.getAppId(); + Assert.hasText(appId.getUserId(), + "UserId (spring.cloud.vault.app-id.user-id) must not be empty"); + + AppIdAuthenticationOptions authenticationOptions = AppIdAuthenticationOptions + .builder().appId(vaultProperties.getApplicationName()) // + .path(appId.getAppIdPath()) // + .userIdMechanism(getClientAuthentication(appId)).build(); + + return new AppIdAuthentication(authenticationOptions, vaultClient); + } + + private AppIdUserIdMechanism getClientAuthentication( + VaultProperties.AppIdProperties appId) { + + try { + Class userIdClass = ClassUtils.forName(appId.getUserId(), null); + return (AppIdUserIdMechanism) BeanUtils.instantiateClass(userIdClass); + } + catch (ClassNotFoundException ex) { + + switch (appId.getUserId().toUpperCase()) { + + case VaultProperties.AppIdProperties.IP_ADDRESS: + return new IpAddressUserId(); + + case VaultProperties.AppIdProperties.MAC_ADDRESS: + + if (StringUtils.hasText(appId.getNetworkInterface())) { + try { + return new MacAddressUserId( + Integer.parseInt(appId.getNetworkInterface())); + } + catch (NumberFormatException e) { + return new MacAddressUserId(appId.getNetworkInterface()); + } + } + + return new MacAddressUserId(); + default: + return new StaticUserId(appId.getUserId()); + } + } + } + + private ClientAuthentication awsEc2Authentication(VaultProperties vaultProperties, + VaultClient vaultClient) { + + VaultProperties.AwsEc2Properties awsEc2 = vaultProperties.getAwsEc2(); + + AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions + .builder().role(awsEc2.getRole()) // + .path(awsEc2.getAwsEc2Path()) // + .identityDocumentUri(URI.create(awsEc2.getIdentityDocument())) // + .build(); + + return new AwsEc2Authentication(authenticationOptions, vaultClient, + vaultClient.getRestTemplate()); + } + + private ClientAuthentication cubbyholeAuthentication(VaultClient vaultClient) { + + Assert.hasText(vaultProperties.getToken(), + "Initial Token (spring.cloud.vault.token) for Cubbyhole authentication must not be empty"); + + CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions.builder() // + .wrapped() // + .initialToken(VaultToken.of(vaultProperties.getToken())) // + .build(); + + return new CubbyholeAuthentication(options, vaultClient); + } + /** * Wrapper to keep {@link TaskScheduler} local to Spring Cloud Vault. * @param diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigOperations.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigOperations.java index b67cc794..3229bb6e 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigOperations.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigOperations.java @@ -22,22 +22,23 @@ import org.springframework.vault.core.VaultOperations; * {@link VaultConfigTemplate}. * * @author Mark Paluch + * @see VaultConfigTemplate + * @see Secrets */ public interface VaultConfigOperations { /** * Read secrets from a secret backend encapsulated within a - * {@link SecureBackendAccessor}. Reading data using this method is suitable for + * {@link SecretBackendMetadata}. Reading data using this method is suitable for * secret backends that do not require a request body. * - * @param secureBackendAccessor must not be {@literal null}. + * @param secretBackendMetadata must not be {@literal null}. * @return the configuration data. May be empty but never {@literal null}. * @throws IllegalStateException if {@link VaultProperties#isFailFast()} is enabled. */ - Secrets read(SecureBackendAccessor secureBackendAccessor); + Secrets read(SecretBackendMetadata secretBackendMetadata); /** - * * @return the underlying {@link VaultOperations}. */ VaultOperations getVaultOperations(); diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java index 3cf98566..891ebf8c 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java @@ -36,7 +36,7 @@ public class VaultConfigTemplate implements VaultConfigOperations { private final VaultProperties properties; /** - * Creates a new {@link VaultConfigTemplate}. + * Create a new {@link VaultConfigTemplate} given {@link VaultOperations}. * * @param vaultOperations must not be {@literal null}. * @param properties must not be {@literal null}. @@ -51,9 +51,10 @@ public class VaultConfigTemplate implements VaultConfigOperations { this.properties = properties; } - public Secrets read(final SecureBackendAccessor secureBackendAccessor) { + @Override + public Secrets read(final SecretBackendMetadata secretBackendMetadata) { - Assert.notNull(secureBackendAccessor, "SecureBackendAccessor must not be null!"); + Assert.notNull(secretBackendMetadata, "SecureBackendAccessor must not be null!"); VaultResponseEntity response = vaultOperations.doWithVault( new VaultOperations.SessionCallback>() { @@ -62,7 +63,7 @@ public class VaultConfigTemplate implements VaultConfigOperations { VaultOperations.VaultSession session) { return session.exchange("{backend}/{key}", HttpMethod.GET, null, - Secrets.class, secureBackendAccessor.variables()); + Secrets.class, secretBackendMetadata.getVariables()); } }); @@ -71,7 +72,14 @@ public class VaultConfigTemplate implements VaultConfigOperations { if (response.getStatusCode() == HttpStatus.OK) { Secrets secrets = response.getBody(); - secrets.setData(secureBackendAccessor.transformProperties(secrets.getData())); + + PropertyTransformer propertyTransformer = secretBackendMetadata + .getPropertyTransformer(); + + if (propertyTransformer != null) { + secrets.setData( + propertyTransformer.transformProperties(secrets.getData())); + } return secrets; } diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySource.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySource.java index 60e216f5..41002314 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySource.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySource.java @@ -31,31 +31,33 @@ import lombok.extern.slf4j.Slf4j; * @author Mark Paluch */ @Slf4j -class VaultPropertySource extends EnumerablePropertySource { +class VaultPropertySource extends EnumerablePropertySource { + + private final boolean failFast; + + private final SecretBackendMetadata secretBackendMetadata; - private final VaultProperties vaultProperties; - private final SecureBackendAccessor secureBackendAccessor; private final Map properties = new LinkedHashMap<>(); + private Secrets secrets; /** * Creates a new {@link VaultPropertySource}. * * @param operations must not be {@literal null}. - * @param properties must not be {@literal null}. - * @param secureBackendAccessor must not be {@literal null}. + * @param failFast fail if properties could not be read because of access errors. + * @param secretBackendMetadata must not be {@literal null}. */ - public VaultPropertySource(VaultConfigTemplate operations, VaultProperties properties, - SecureBackendAccessor secureBackendAccessor) { + public VaultPropertySource(VaultConfigOperations operations, boolean failFast, + SecretBackendMetadata secretBackendMetadata) { - super(secureBackendAccessor.getName(), operations); + super(secretBackendMetadata.getName(), operations); Assert.notNull(operations, "VaultConfigTemplate must not be null!"); - Assert.notNull(properties, "VaultProperties must not be null!"); - Assert.notNull(secureBackendAccessor, "SecureBackendAccessor must not be null!"); + Assert.notNull(secretBackendMetadata, "SecretBackendMetadata must not be null!"); - this.vaultProperties = properties; - this.secureBackendAccessor = secureBackendAccessor; + this.failFast = failFast; + this.secretBackendMetadata = secretBackendMetadata; } /** @@ -64,22 +66,19 @@ class VaultPropertySource extends EnumerablePropertySource public void init() { try { - this.secrets = this.source.read(this.secureBackendAccessor); + this.secrets = this.source.read(this.secretBackendMetadata); if (this.secrets != null) { this.properties.putAll(secrets.getData()); } } - catch (Exception e) { + catch (RuntimeException e) { String message = String.format( "Unable to read properties from Vault using %s for %s ", getName(), - secureBackendAccessor.variables()); - if (vaultProperties.isFailFast()) { - if (e instanceof RuntimeException) { - throw e; - } + secretBackendMetadata.getVariables()); - throw new IllegalStateException(message, e); + if (failFast) { + throw e; } log.error(message, e); diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySourceLocator.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySourceLocator.java index 3de32698..860052a8 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySourceLocator.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultPropertySourceLocator.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.vault.config; -import static org.springframework.cloud.vault.config.SecureBackendAccessors.*; +import static org.springframework.cloud.vault.config.GenericSecretBackendMetadata.*; import java.util.ArrayList; import java.util.Arrays; @@ -41,10 +41,10 @@ import org.springframework.util.StringUtils; */ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrdered { - private final VaultConfigTemplate operations; + private final VaultConfigOperations operations; private final VaultProperties properties; private final VaultGenericBackendProperties genericBackendProperties; - private final Collection backendAccessors; + private final Collection backendAccessors; /** * Creates a new {@link VaultPropertySourceLocator}. @@ -54,10 +54,10 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder * @param genericBackendProperties must not be {@literal null}. * @param backendAccessors must not be {@literal null}. */ - public VaultPropertySourceLocator(VaultConfigTemplate operations, + public VaultPropertySourceLocator(VaultConfigOperations operations, VaultProperties properties, VaultGenericBackendProperties genericBackendProperties, - Collection backendAccessors) { + Collection backendAccessors) { Assert.notNull(operations, "VaultConfigOperations must not be null"); Assert.notNull(properties, "VaultProperties must not be null"); @@ -129,7 +129,7 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder if (StringUtils.hasText(propertySourceContext)) { VaultPropertySource vaultPropertySource = createVaultPropertySource( - generic(genericBackendProperties.getBackend(), + create(genericBackendProperties.getBackend(), propertySourceContext)); propertySources.add(vaultPropertySource); @@ -137,7 +137,7 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder } } - for (SecureBackendAccessor backendAccessor : backendAccessors) { + for (SecretBackendMetadata backendAccessor : backendAccessors) { VaultPropertySource vaultPropertySource = createVaultPropertySource( backendAccessor); @@ -185,14 +185,15 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder /** * Create {@link VaultPropertySource} initialized with a - * {@link SecureBackendAccessor}. + * {@link SecretBackendMetadata}. * - * @param accessor the {@link SecureBackendAccessor}. + * @param accessor the {@link SecretBackendMetadata}. * @return the {@link VaultPropertySource} to use. */ protected VaultPropertySource createVaultPropertySource( - SecureBackendAccessor accessor) { - return new VaultPropertySource(this.operations, this.properties, accessor); + SecretBackendMetadata accessor) { + return new VaultPropertySource(this.operations, this.properties.isFailFast(), + accessor); } private void addProfiles(List contexts, String baseContext, diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultSecretBackend.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultSecretBackendDescriptor.java similarity index 63% rename from spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultSecretBackend.java rename to spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultSecretBackendDescriptor.java index 1932ddf4..e76dff69 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultSecretBackend.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultSecretBackendDescriptor.java @@ -17,21 +17,26 @@ package org.springframework.cloud.vault.config; /** - * A secret backend that can return secrets from Vault. + * Interface to be implemented by objects that describe a Vault secret backend. + * + *

+ * Typically used by {@link SecretBackendMetadataFactory} to provide path and + * configuration to create a {@link SecretBackendMetadata} object. * * @author Mark Paluch + * @see SecretBackendMetadataFactory + * @see SecretBackendMetadata */ -public interface VaultSecretBackend { +public interface VaultSecretBackendDescriptor { /** - * Backend path. + * Backend path without leading/trailing slashes. * - * @return the backend path. + * @return the backend path such as {@code secret} or {@code mysql}. */ String getBackend(); /** - * * @return {@literal true} if the backend is enabled. */ boolean isEnabled(); diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/GenericSecretIntegrationTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/GenericSecretIntegrationTests.java index c1e774b3..8b9c4c76 100644 --- a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/GenericSecretIntegrationTests.java +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/GenericSecretIntegrationTests.java @@ -16,7 +16,7 @@ package org.springframework.cloud.vault.config; import static org.assertj.core.api.Assertions.*; -import static org.springframework.cloud.vault.config.SecureBackendAccessors.*; +import static org.springframework.cloud.vault.config.GenericSecretBackendMetadata.*; import java.util.HashMap; import java.util.Map; @@ -51,7 +51,7 @@ public class GenericSecretIntegrationTests extends IntegrationTestSupport { public void shouldReturnSecretsCorrectly() throws Exception { Map secretProperties = configOperations - .read(generic("secret", "app-name")).getData(); + .read(create("secret", "app-name")).getData(); assertThat(secretProperties).containsAllEntriesOf(createExpectedMap()); } @@ -59,7 +59,7 @@ public class GenericSecretIntegrationTests extends IntegrationTestSupport { @Test public void shouldReturnNullIfNotFound() throws Exception { - Secrets secrets = configOperations.read(generic("secret", "missing")); + Secrets secrets = configOperations.read(create("secret", "missing")); assertThat(secrets).isNull(); } diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocatorUnitTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocatorUnitTests.java index a117023f..aee817ed 100644 --- a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocatorUnitTests.java +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceLocatorUnitTests.java @@ -60,7 +60,7 @@ public class LeasingVaultPropertySourceLocatorUnitTests { propertySourceLocator = new LeasingVaultPropertySourceLocator(operations, new VaultProperties(), new VaultGenericBackendProperties(), - Collections. emptyList(), taskScheduler); + Collections. emptyList(), taskScheduler); } @Test @@ -71,7 +71,7 @@ public class LeasingVaultPropertySourceLocatorUnitTests { propertySourceLocator = new LeasingVaultPropertySourceLocator(operations, vaultProperties, new VaultGenericBackendProperties(), - Collections. emptyList(), taskScheduler); + Collections. emptyList(), taskScheduler); assertThat(propertySourceLocator.getOrder()).isEqualTo(10); } diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceUnitTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceUnitTests.java index 9d120030..b266f33e 100644 --- a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceUnitTests.java +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/LeasingVaultPropertySourceUnitTests.java @@ -53,7 +53,7 @@ public class LeasingVaultPropertySourceUnitTests { private VaultOperations vaultOperations; @Mock - private SecureBackendAccessor secureBackendAccessor; + private SecretBackendMetadata secretBackendMetadata; @Mock private TaskScheduler taskScheduler; @@ -66,11 +66,11 @@ public class LeasingVaultPropertySourceUnitTests { @Before public void before() throws Exception { - when(secureBackendAccessor.getName()).thenReturn("test"); + when(secretBackendMetadata.getName()).thenReturn("test"); when(configOperations.getVaultOperations()).thenReturn(vaultOperations); propertySource = new LeasingVaultPropertySource(configOperations, - new VaultProperties(), secureBackendAccessor, taskScheduler); + false, secretBackendMetadata, taskScheduler); } @Test @@ -87,7 +87,7 @@ public class LeasingVaultPropertySourceUnitTests { Secrets secrets = new Secrets(); secrets.setData(Collections.singletonMap("key", "value")); - when(configOperations.read(secureBackendAccessor)).thenReturn(secrets); + when(configOperations.read(secretBackendMetadata)).thenReturn(secrets); propertySource.init(); @@ -103,7 +103,7 @@ public class LeasingVaultPropertySourceUnitTests { secrets.setRenewable(false); secrets.setData(Collections.singletonMap("key", "value")); - when(configOperations.read(secureBackendAccessor)).thenReturn(secrets); + when(configOperations.read(secretBackendMetadata)).thenReturn(secrets); propertySource.init(); @@ -116,7 +116,7 @@ public class LeasingVaultPropertySourceUnitTests { when(taskScheduler.schedule(any(Runnable.class), any(Trigger.class))) .thenReturn(scheduledFuture); - when(configOperations.read(secureBackendAccessor)).thenReturn(createSecrets()); + when(configOperations.read(secretBackendMetadata)).thenReturn(createSecrets()); propertySource.init(); @@ -237,7 +237,7 @@ public class LeasingVaultPropertySourceUnitTests { when(taskScheduler.schedule(any(Runnable.class), any(Trigger.class))) .thenReturn(scheduledFuture); - when(configOperations.read(secureBackendAccessor)).thenReturn(createSecrets()); + when(configOperations.read(secretBackendMetadata)).thenReturn(createSecrets()); propertySource.init(); when(vaultOperations.doWithVault(any(VaultOperations.SessionCallback.class))) .thenReturn(getResponseEntity("new_lease", true, 70, HttpStatus.OK)); diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/PropertyNameTransformerUnitTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/PropertyNameTransformerUnitTests.java new file mode 100644 index 00000000..09e43071 --- /dev/null +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/PropertyNameTransformerUnitTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016 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 + * + * http://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 org.springframework.cloud.vault.config; + +import static org.assertj.core.api.Assertions.*; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +/** + * Unit tests for {@link PropertyNameTransformer}. + * + * @author Mark Paluch + */ +public class PropertyNameTransformerUnitTests { + + @Test + public void shouldTranslateProperties() throws Exception { + + PropertyNameTransformer transformer = new PropertyNameTransformer(); + transformer.addKeyTransformation("old-key", "new-key"); + + Map map = new HashMap<>(); + map.put("old-key", "value"); + map.put("other-key", "other-value"); + + assertThat(transformer.transformProperties(map)).containsEntry("new-key", "value") + .containsEntry("other-key", "other-value"); + } + + @Test + public void shouldAllowNullInput() throws Exception { + + PropertyNameTransformer transformer = new PropertyNameTransformer(); + transformer.addKeyTransformation("old-key", "new-key"); + + assertThat(transformer.transformProperties(null)).isNull(); + } +} \ No newline at end of file diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java index e116fabd..eb01af89 100644 --- a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java @@ -21,6 +21,7 @@ import java.util.Collections; import org.junit.Before; import org.junit.Test; + import org.springframework.cloud.vault.util.IntegrationTestSupport; import org.springframework.cloud.vault.util.Settings; @@ -44,7 +45,7 @@ public class VaultPropertySourceIntegrationTests extends IntegrationTestSupport VaultPropertySource propertySource = new VaultPropertySource( new VaultConfigTemplate(prepare().getVaultOperations(), vaultProperties), - vaultProperties, SecureBackendAccessors.generic("secret", "myapp")); + false, GenericSecretBackendMetadata.create("secret", "myapp")); propertySource.init();