Polishing.

Rename SecureBackend to SecretBackend. Refactor SecureBackendAccessor to SecretBackendMetadata. Move Property Transformation into PropertyTransformer. Enhance JavaDoc. Remove Properties beans and use EnableProperties annotation.
This commit is contained in:
Mark Paluch
2016-10-09 08:30:32 +02:00
parent 7f4870c0ad
commit 205edebb52
37 changed files with 724 additions and 479 deletions

View File

@@ -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.

View File

@@ -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<VaultAwsProperties> secureBackendAccessorFactory() {
return new AwsSecureBackendAccessorFactory();
public SecretBackendMetadataFactory<VaultAwsProperties> secretBackendMetadataFactory() {
return new AwsSecretBackendMetadataFactory();
}
@Bean
public VaultAwsProperties awsProperties() {
return new VaultAwsProperties();
}
static class AwsSecureBackendAccessorFactory
implements SecureBackendAccessorFactory<VaultAwsProperties> {
static class AwsSecretBackendMetadataFactory
implements SecretBackendMetadataFactory<VaultAwsProperties> {
@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<String, String> variables() {
public Map<String, String> getVariables() {
Map<String, String> 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<String, String> transformProperties(
Map<String, String> input) {
Map<String, String> 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;
}
};
}

View File

@@ -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;

View File

@@ -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<VaultConsulProperties> secureBackendAccessorFactory() {
return new ConsulSecureBackendAccessorFactory();
public SecretBackendMetadataFactory<VaultConsulProperties> secretBackendAccessorFactory() {
return new ConsulSecretBackendMetadataFactory();
}
@Bean
public VaultConsulProperties vaultConsulProperties() {
return new VaultConsulProperties();
}
static class ConsulSecureBackendAccessorFactory
implements SecureBackendAccessorFactory<VaultConsulProperties> {
static class ConsulSecretBackendMetadataFactory
implements SecretBackendMetadataFactory<VaultConsulProperties> {
@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<String, String> variables() {
public Map<String, String> getVariables() {
Map<String, String> 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<String, String> transformProperties(
Map<String, String> input) {
Map<String, String> result = new HashMap();
result.put(properties.getTokenProperty(), input.get("token"));
return result;
public PropertyTransformer getPropertyTransformer() {
return transformer;
}
};
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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.

View File

@@ -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<DatabaseSecretProperties> secureBackendAccessorFactory() {
return new DatabaseSecureBackendAccessorFactory();
public SecretBackendMetadataFactory<DatabaseSecretProperties> secretBackendMetadataFactory() {
return new DatabaseSecretBackendMetadataFactory();
}
static class DatabaseSecureBackendAccessorFactory
implements SecureBackendAccessorFactory<DatabaseSecretProperties> {
static class DatabaseSecretBackendMetadataFactory
implements SecretBackendMetadataFactory<DatabaseSecretProperties> {
@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<String, String> variables() {
public Map<String, String> getVariables() {
Map<String, String> variables = new HashMap<>();
variables.put("backend", properties.getBackend());
@@ -86,14 +98,8 @@ public class VaultConfigDatabaseBootstrapConfiguration {
}
@Override
public Map<String, String> transformProperties(
Map<String, String> input) {
Map<String, String> result = new HashMap<>();
result.put(properties.getUsernameProperty(), input.get("username"));
result.put(properties.getPasswordProperty(), input.get("password"));
return result;
public PropertyTransformer getPropertyTransformer() {
return transformer;
}
};
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<VaultRabbitMqProperties> secureBackendAccessorFactory() {
return new RabbitMqSecureBackendAccessorFactory();
public SecretBackendMetadataFactory<VaultRabbitMqProperties> secureBackendAccessorFactory() {
return new RabbitMqSecretBackendMetadataFactory();
}
@Bean
@@ -43,42 +47,51 @@ public class VaultConfigRabbitMqBootstrapConfiguration {
return new VaultRabbitMqProperties();
}
static class RabbitMqSecureBackendAccessorFactory
implements SecureBackendAccessorFactory<VaultRabbitMqProperties> {
static class RabbitMqSecretBackendMetadataFactory
implements SecretBackendMetadataFactory<VaultRabbitMqProperties> {
@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<String, String> variables() {
public Map<String, String> getVariables() {
Map<String, String> 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<String, String> transformProperties(
Map<String, String> input) {
Map<String, String> result = new HashMap();
result.put(properties.getUsernameProperty(), input.get("username"));
result.put(properties.getPasswordProperty(), input.get("password"));
return result;
public PropertyTransformer getPropertyTransformer() {
return transformer;
}
};
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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<String, String> getVariables() {
Map<String, String> variables = new HashMap<>();
variables.put("backend", secretBackendPath);
variables.put("key", key);
return variables;
}
}

View File

@@ -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");

View File

@@ -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<SecureBackendAccessor> backendAccessors,
Collection<SecretBackendMetadata> 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);

View File

@@ -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.
* <p>
* 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<String, String> 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<String, String> transformProperties(Map<String, String> input) {
if (input == null) {
return null;
}
Map<String, String> 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;
}
}

View File

@@ -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.
* <p>
* 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<String, String> variables();
/**
*
* Transform properties by creating a new map using the transformed property set.
*
* @param input must not be {@literal null}.
* @return transformed properties.
*/
Map<String, String> transformProperties(Map<String, String> input);
/**
*
* @return the name for this accessor.
*/
String getName();
}

View File

@@ -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<SecretBackendMetadata> createSecretBackendMetadata(
Collection<VaultSecretBackendDescriptor> vaultSecretBackendDescriptors,
Collection<SecretBackendMetadataFactory<? super VaultSecretBackendDescriptor>> factories) {
List<SecretBackendMetadata> 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<SecretBackendMetadataFactory<? super VaultSecretBackendDescriptor>> factories,
VaultSecretBackendDescriptor vaultSecretBackendDescriptor) {
SecretBackendMetadata accessor = null;
for (SecretBackendMetadataFactory<? super VaultSecretBackendDescriptor> factory : factories) {
if (factory.supports(vaultSecretBackendDescriptor)) {
accessor = factory.createMetadata(vaultSecretBackendDescriptor);
break;
}
}
return accessor;
}
}

View File

@@ -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<T extends VaultSecretBackend> {
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<String, String> getVariables();
}

View File

@@ -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.
*
* <p>
* 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)}.
*
* <p>
* 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<T extends VaultSecretBackendDescriptor> {
/**
* 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);
}

View File

@@ -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<String, String> variables() {
Map<String, String> 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<String, String> transformProperties(Map<String, String> input) {
return input;
}
};
}
}

View File

@@ -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<SecureBackendAccessor> createBackendAcessors(
Collection<VaultSecretBackend> vaultSecretBackends,
Collection<SecureBackendAccessorFactory<? super VaultSecretBackend>> factories) {
List<SecureBackendAccessor> 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<SecureBackendAccessorFactory<? super VaultSecretBackend>> factories,
VaultSecretBackend vaultSecretBackend) {
SecureBackendAccessor accessor = null;
for (SecureBackendAccessorFactory<? super VaultSecretBackend> factory : factories) {
if (factory.supports(vaultSecretBackend)) {
accessor = factory.createSecureBackendAccessor(vaultSecretBackend);
break;
}
}
return accessor;
}
}

View File

@@ -81,20 +81,21 @@ public class VaultBootstrapConfiguration {
private final VaultProperties vaultProperties;
private final Collection<VaultSecretBackend> vaultSecretBackends;
private final Collection<VaultSecretBackendDescriptor> vaultSecretBackendDescriptors;
private final Collection<SecureBackendAccessorFactory<? super VaultSecretBackend>> factories;
private final Collection<SecretBackendMetadataFactory<? super VaultSecretBackendDescriptor>> 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<TaskSchedulerWrapper<? extends TaskScheduler>> taskSchedulerProvider) {
Collection<SecureBackendAccessor> backendAccessors = SecureBackendFactories
.createBackendAcessors(vaultSecretBackends, factories);
Collection<SecretBackendMetadata> 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 <T>

View File

@@ -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();

View File

@@ -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<Secrets> response = vaultOperations.doWithVault(
new VaultOperations.SessionCallback<VaultResponseEntity<Secrets>>() {
@@ -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;
}

View File

@@ -31,31 +31,33 @@ import lombok.extern.slf4j.Slf4j;
* @author Mark Paluch
*/
@Slf4j
class VaultPropertySource extends EnumerablePropertySource<VaultConfigTemplate> {
class VaultPropertySource extends EnumerablePropertySource<VaultConfigOperations> {
private final boolean failFast;
private final SecretBackendMetadata secretBackendMetadata;
private final VaultProperties vaultProperties;
private final SecureBackendAccessor secureBackendAccessor;
private final Map<String, String> 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<VaultConfigTemplate>
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);

View File

@@ -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<SecureBackendAccessor> backendAccessors;
private final Collection<SecretBackendMetadata> 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<SecureBackendAccessor> backendAccessors) {
Collection<SecretBackendMetadata> 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<String> contexts, String baseContext,

View File

@@ -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.
*
* <p>
* 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();

View File

@@ -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<String, String> 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();
}

View File

@@ -60,7 +60,7 @@ public class LeasingVaultPropertySourceLocatorUnitTests {
propertySourceLocator = new LeasingVaultPropertySourceLocator(operations,
new VaultProperties(), new VaultGenericBackendProperties(),
Collections.<SecureBackendAccessor> emptyList(), taskScheduler);
Collections.<SecretBackendMetadata> emptyList(), taskScheduler);
}
@Test
@@ -71,7 +71,7 @@ public class LeasingVaultPropertySourceLocatorUnitTests {
propertySourceLocator = new LeasingVaultPropertySourceLocator(operations,
vaultProperties, new VaultGenericBackendProperties(),
Collections.<SecureBackendAccessor> emptyList(), taskScheduler);
Collections.<SecretBackendMetadata> emptyList(), taskScheduler);
assertThat(propertySourceLocator.getOrder()).isEqualTo(10);
}

View File

@@ -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));

View File

@@ -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<String, String> 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();
}
}

View File

@@ -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();