Refactor code to use Spring Vault.
Extract VaultClient and configuration parts into Spring Vault and reimport using the spring-vault-core dependency. Fixes gh-37
This commit is contained in:
9
pom.xml
9
pom.xml
@@ -21,7 +21,6 @@
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud-vault-dependencies</module>
|
||||
<module>spring-cloud-vault-core</module>
|
||||
<module>spring-cloud-vault-config</module>
|
||||
<module>spring-cloud-vault-config-databases</module>
|
||||
<module>spring-cloud-vault-config-consul</module>
|
||||
@@ -53,6 +52,14 @@
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -20,18 +20,13 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -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.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessor;
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessorFactory;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -59,16 +59,15 @@ public class VaultConfigAwsBootstrapConfiguration {
|
||||
|
||||
/**
|
||||
* Creates a {@link SecureBackendAccessor} for a secure backend using
|
||||
* {@link VaultAwsProperties}. This accessor transforms Vault's
|
||||
* username/password property names to names provided with
|
||||
* {@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}
|
||||
*/
|
||||
public static SecureBackendAccessor forAws(
|
||||
final VaultAwsProperties properties) {
|
||||
public static SecureBackendAccessor forAws(final VaultAwsProperties properties) {
|
||||
Assert.notNull(properties, "VaultAwsProperties must not be null");
|
||||
|
||||
return new SecureBackendAccessor() {
|
||||
@@ -93,8 +92,10 @@ public class VaultConfigAwsBootstrapConfiguration {
|
||||
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"));
|
||||
result.put(properties.getAccessKeyProperty(),
|
||||
input.get("access_key"));
|
||||
result.put(properties.getSecretKeyProperty(),
|
||||
input.get("secret_key"));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -15,34 +15,32 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config.aws;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.AbstractIntegrationTests;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultTemplate;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.cloud.vault.config.aws.VaultConfigAwsBootstrapConfiguration.AwsSecureBackendAccessorFactory.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultConfigTemplate;
|
||||
import org.springframework.cloud.vault.config.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the aws secret backend. This test
|
||||
* requires AWS credentials and a region, see {@link #AWS_ACCESS_KEY} and
|
||||
* Integration tests for {@link VaultConfigTemplate} using the aws secret backend. This
|
||||
* test requires AWS credentials and a region, see {@link #AWS_ACCESS_KEY} and
|
||||
* {@link #AWS_SECRET_KEY} to be provided externally.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class AwsSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public class AwsSecretIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
private final static String AWS_REGION = "eu-west-1";
|
||||
private final static String AWS_ACCESS_KEY = System.getProperty("aws.access.key");
|
||||
@@ -68,22 +66,25 @@ public class AwsSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
aws.setEnabled(true);
|
||||
aws.setRole("readonly");
|
||||
|
||||
if (!prepare().hasSecret(aws.getBackend())) {
|
||||
if (!prepare().hasSecretBackend(aws.getBackend())) {
|
||||
prepare().mountSecret(aws.getBackend());
|
||||
}
|
||||
|
||||
VaultOperations vaultOperations = prepare().getVaultOperations();
|
||||
|
||||
Map<String, String> connection = new HashMap<>();
|
||||
connection.put("region", AWS_REGION);
|
||||
connection.put("access_key", AWS_ACCESS_KEY);
|
||||
connection.put("secret_key", AWS_SECRET_KEY);
|
||||
|
||||
prepare().write(String.format("%s/config/root", aws.getBackend()), connection);
|
||||
vaultOperations.write(String.format("%s/config/root", aws.getBackend()),
|
||||
connection);
|
||||
|
||||
prepare().write(String.format("%s/roles/%s", aws.getBackend(), aws.getRole()),
|
||||
vaultOperations.write(
|
||||
String.format("%s/roles/%s", aws.getBackend(), aws.getRole()),
|
||||
Collections.singletonMap("arn", ARN));
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties, prepare().newVaultClient(),
|
||||
ClientAuthentication.token(vaultProperties)).opsForConfig();
|
||||
configOperations = new VaultConfigTemplate(vaultOperations, vaultProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests using the aws secret backend. In case this test should fail because
|
||||
@@ -73,18 +74,20 @@ public class VaultConfigAwsTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
if (!vaultRule.prepare().hasSecret("aws")) {
|
||||
if (!vaultRule.prepare().hasSecretBackend("aws")) {
|
||||
vaultRule.prepare().mountSecret("aws");
|
||||
}
|
||||
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
Map<String, String> connection = new HashMap<>();
|
||||
connection.put("region", AWS_REGION);
|
||||
connection.put("access_key", AWS_ACCESS_KEY);
|
||||
connection.put("secret_key", AWS_SECRET_KEY);
|
||||
|
||||
vaultRule.prepare().write("aws/config/root", connection);
|
||||
vaultOperations.write("aws/config/root", connection);
|
||||
|
||||
vaultRule.prepare().write("aws/roles/readonly",
|
||||
vaultOperations.write("aws/roles/readonly",
|
||||
Collections.singletonMap("arn", ARN));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,18 +18,13 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -20,8 +20,8 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessor;
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessorFactory;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -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.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@@ -15,19 +15,23 @@
|
||||
*/
|
||||
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 java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.cloud.vault.AbstractIntegrationTests;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultTemplate;
|
||||
import org.springframework.cloud.vault.config.VaultConfigTemplate;
|
||||
import org.springframework.cloud.vault.config.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -35,21 +39,15 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.cloud.vault.config.consul.VaultConfigConsulBootstrapConfiguration.ConsulSecureBackendAccessorFactory.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the consul secret backend. This test
|
||||
* requires a running Consul instance, see {@link #CONNECTION_URL}.
|
||||
* Integration tests for {@link VaultConfigTemplate} using the consul secret backend. This
|
||||
* test requires a running Consul instance, see {@link #CONNECTION_URL}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class ConsulSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public class ConsulSecretIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
private final static String CONSUL_HOST = "localhost";
|
||||
private final static int CONSUL_PORT = 8500;
|
||||
@@ -82,10 +80,12 @@ public class ConsulSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
consul.setEnabled(true);
|
||||
consul.setRole("readonly");
|
||||
|
||||
if (!prepare().hasSecret(consul.getBackend())) {
|
||||
if (!prepare().hasSecretBackend(consul.getBackend())) {
|
||||
prepare().mountSecret(consul.getBackend());
|
||||
}
|
||||
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("X-Consul-Token", CONSUL_ACL_MASTER_TOKEN);
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(
|
||||
@@ -98,16 +98,15 @@ public class ConsulSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
consulAccess.put("address", CONNECTION_URL);
|
||||
consulAccess.put("token", tokenResponse.getBody().get("ID"));
|
||||
|
||||
prepare().write(String.format("%s/config/access", consul.getBackend()),
|
||||
vaultOperations.write(String.format("%s/config/access", consul.getBackend()),
|
||||
consulAccess);
|
||||
|
||||
prepare().write(
|
||||
vaultOperations.write(
|
||||
String.format("%s/roles/%s", consul.getBackend(), consul.getRole()),
|
||||
Collections.singletonMap("policy",
|
||||
Base64Utils.encodeToString(POLICY.getBytes())));
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties, prepare().newVaultClient(),
|
||||
ClientAuthentication.token(vaultProperties)).opsForConfig();
|
||||
configOperations = new VaultConfigTemplate(vaultOperations, vaultProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -44,6 +44,8 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests using the consul secret backend. In case this test should fail
|
||||
@@ -72,7 +74,7 @@ public class VaultConfigConsulTests {
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the rabbitmq secret backend.
|
||||
* Initialize the consul secret backend.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -84,27 +86,28 @@ public class VaultConfigConsulTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
if (!vaultRule.prepare().hasSecret("consul")) {
|
||||
if (!vaultRule.prepare().hasSecretBackend("consul")) {
|
||||
vaultRule.prepare().mountSecret("consul");
|
||||
}
|
||||
|
||||
TestRestTemplate restTemplate = new TestRestTemplate();
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("X-Consul-Token", CONSUL_ACL_MASTER_TOKEN);
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(
|
||||
"{\"Name\": \"sample\", \"Type\": \"management\"}", headers);
|
||||
ResponseEntity<Map<String, String>> tokenResponse = restTemplate.exchange(
|
||||
"http://{address}/v1/acl/create", HttpMethod.PUT, requestEntity,
|
||||
STRING_MAP, CONNECTION_URL);
|
||||
"http://{host}:{port}/v1/acl/create", HttpMethod.PUT, requestEntity,
|
||||
STRING_MAP, CONSUL_HOST, CONSUL_PORT);
|
||||
|
||||
Map<String, String> consulAccess = new HashMap<>();
|
||||
consulAccess.put("address", CONNECTION_URL);
|
||||
consulAccess.put("token", tokenResponse.getBody().get("ID"));
|
||||
|
||||
vaultRule.prepare().write("consul/config/access", consulAccess);
|
||||
vaultOperations.write("consul/config/access", consulAccess);
|
||||
|
||||
vaultRule.prepare().write("consul/roles/readonly", Collections
|
||||
vaultOperations.write("consul/roles/readonly", Collections
|
||||
.singletonMap("policy", Base64.encode(POLICY.getBytes())));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,18 +18,13 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config.databases;
|
||||
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
|
||||
/**
|
||||
* Configuration properties interface for database secrets.
|
||||
|
||||
@@ -20,8 +20,8 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessor;
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessorFactory;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -2,9 +2,9 @@ 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.VaultSecretBackend;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
|
||||
/**
|
||||
* Configuration properties for Vault using the MySQL integration.
|
||||
|
||||
@@ -15,35 +15,33 @@
|
||||
*/
|
||||
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 java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.AbstractIntegrationTests;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultTemplate;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultConfigTemplate;
|
||||
import org.springframework.cloud.vault.config.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the cassandra secret backend. This test
|
||||
* requires a running Cassandra instance, see {@link #CASSANDRA_HOST} and other
|
||||
* Integration tests for {@link VaultConfigTemplate} using the cassandra secret backend.
|
||||
* This test requires a running Cassandra instance, see {@link #CASSANDRA_HOST} and other
|
||||
* {@code CASSANDRA_*} properties.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class CassandraSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public class CassandraSecretIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
private final static String CASSANDRA_HOST = "localhost";
|
||||
private final static int CASSANDRA_PORT = 9042;
|
||||
@@ -71,26 +69,26 @@ public class CassandraSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
cassandra.setEnabled(true);
|
||||
cassandra.setRole("readonly");
|
||||
|
||||
if (!prepare().hasSecret(cassandra.getBackend())) {
|
||||
if (!prepare().hasSecretBackend(cassandra.getBackend())) {
|
||||
prepare().mountSecret(cassandra.getBackend());
|
||||
}
|
||||
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
Map<String, String> connection = new HashMap<>();
|
||||
connection.put("hosts", CASSANDRA_HOST);
|
||||
connection.put("username", CASSANDRA_USERNAME);
|
||||
connection.put("password", CASSANDRA_PASSWORD);
|
||||
|
||||
prepare().write(String.format("%s/config/connection", cassandra.getBackend()),
|
||||
vaultOperations.write(
|
||||
String.format("%s/config/connection", cassandra.getBackend()),
|
||||
connection);
|
||||
|
||||
prepare()
|
||||
.write(String.format("%s/roles/%s", cassandra.getBackend(),
|
||||
cassandra.getRole()),
|
||||
Collections.singletonMap("creation_cql",
|
||||
CREATE_USER_AND_GRANT_CQL));
|
||||
vaultOperations.write(
|
||||
String.format("%s/roles/%s", cassandra.getBackend(), cassandra.getRole()),
|
||||
Collections.singletonMap("creation_cql", CREATE_USER_AND_GRANT_CQL));
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties, prepare().newVaultClient(),
|
||||
ClientAuthentication.token(vaultProperties)).opsForConfig();
|
||||
configOperations = new VaultConfigTemplate(vaultOperations, vaultProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,38 +15,36 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config.databases;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.AbstractIntegrationTests;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultTemplate;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultConfigTemplate;
|
||||
import org.springframework.cloud.vault.config.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the mysql secret backend. This test
|
||||
* requires a running MySQL instance, see {@link #ROOT_CREDENTIALS}.
|
||||
* Integration tests for {@link VaultConfigTemplate} using the mysql secret backend. This
|
||||
* test requires a running MySQL instance, see {@link #ROOT_CREDENTIALS}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MySqlSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public class MySqlSecretIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
private final static int MYSQL_PORT = 3306;
|
||||
private final static String MYSQL_HOST = "localhost";
|
||||
private final static String ROOT_CREDENTIALS = String.format(
|
||||
"spring:vault@tcp(%s:%d)/", MYSQL_HOST, MYSQL_PORT);
|
||||
private final static String ROOT_CREDENTIALS = String
|
||||
.format("spring:vault@tcp(%s:%d)/", MYSQL_HOST, MYSQL_PORT);
|
||||
private final static String CREATE_USER_AND_GRANT_SQL = "CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';"
|
||||
+ "GRANT SELECT ON *.* TO '{{name}}'@'%';";
|
||||
|
||||
@@ -67,19 +65,20 @@ public class MySqlSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
mySql.setEnabled(true);
|
||||
mySql.setRole("readonly");
|
||||
|
||||
if (!prepare().hasSecret(mySql.getBackend())) {
|
||||
if (!prepare().hasSecretBackend(mySql.getBackend())) {
|
||||
prepare().mountSecret(mySql.getBackend());
|
||||
}
|
||||
|
||||
prepare().write(String.format("%s/config/connection", mySql.getBackend()),
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
vaultOperations.write(String.format("%s/config/connection", mySql.getBackend()),
|
||||
Collections.singletonMap("connection_url", ROOT_CREDENTIALS));
|
||||
|
||||
prepare().write(
|
||||
vaultOperations.write(
|
||||
String.format("%s/roles/%s", mySql.getBackend(), mySql.getRole()),
|
||||
Collections.singletonMap("sql", CREATE_USER_AND_GRANT_SQL));
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties, prepare().newVaultClient(),
|
||||
ClientAuthentication.token(vaultProperties)).opsForConfig();
|
||||
configOperations = new VaultConfigTemplate(vaultOperations, vaultProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,33 +15,33 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config.databases;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.AbstractIntegrationTests;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultTemplate;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultConfigTemplate;
|
||||
import org.springframework.cloud.vault.config.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the postgresql secret backend. This
|
||||
* test requires a running PostgreSQL instance, see {@link #CONNECTION_URL}.
|
||||
* Integration tests for
|
||||
* {@link org.springframework.cloud.vault.config.VaultConfigTemplate} using the postgresql
|
||||
* secret backend. This test requires a running PostgreSQL instance, see
|
||||
* {@link #CONNECTION_URL}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PostgreSqlSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public class PostgreSqlSecretIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
private final static String POSTGRES_HOST = "localhost";
|
||||
private final static int POSTGRES_PORT = 5432;
|
||||
@@ -71,20 +71,23 @@ public class PostgreSqlSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
postgreSql.setEnabled(true);
|
||||
postgreSql.setRole("readonly");
|
||||
|
||||
if (!prepare().hasSecret(postgreSql.getBackend())) {
|
||||
if (!prepare().hasSecretBackend(postgreSql.getBackend())) {
|
||||
prepare().mountSecret(postgreSql.getBackend());
|
||||
}
|
||||
|
||||
prepare().write(String.format("%s/config/connection", postgreSql.getBackend()),
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
vaultOperations.write(
|
||||
String.format("%s/config/connection", postgreSql.getBackend()),
|
||||
Collections.singletonMap("connection_url", CONNECTION_URL));
|
||||
|
||||
prepare().write(
|
||||
vaultOperations.write(
|
||||
String.format("%s/roles/%s", postgreSql.getBackend(),
|
||||
postgreSql.getRole()),
|
||||
Collections.singletonMap("sql", CREATE_USER_AND_GRANT_SQL));
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties, prepare().newVaultClient(),
|
||||
ClientAuthentication.token(vaultProperties)).opsForConfig();
|
||||
configOperations = new VaultConfigTemplate(vaultOperations, vaultProperties);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.PlainTextAuthProvider;
|
||||
import com.datastax.driver.core.Session;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests using the cassandra secret backend. In case this test should fail because of SSL make sure you run
|
||||
@@ -76,19 +77,21 @@ public class VaultConfigCassandraTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
if (!vaultRule.prepare().hasSecret("cassandra")) {
|
||||
if (!vaultRule.prepare().hasSecretBackend("cassandra")) {
|
||||
vaultRule.prepare().mountSecret("cassandra");
|
||||
}
|
||||
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
Map<String, String> connection = new HashMap<>();
|
||||
connection.put("hosts", CASSANDRA_HOST);
|
||||
connection.put("username", CASSANDRA_USERNAME);
|
||||
connection.put("password", CASSANDRA_PASSWORD);
|
||||
|
||||
vaultRule.prepare().write(String.format("%s/config/connection", "cassandra"),
|
||||
vaultOperations.write(String.format("%s/config/connection", "cassandra"),
|
||||
connection);
|
||||
|
||||
vaultRule.prepare().write("cassandra/roles/readonly",
|
||||
vaultOperations.write("cassandra/roles/readonly",
|
||||
Collections.singletonMap("creation_cql", CREATE_USER_AND_GRANT_CQL));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests using the mysql secret backend. In case this test should fail because of SSL make sure you run the
|
||||
@@ -71,14 +72,16 @@ public class VaultConfigMySqlTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
if (!vaultRule.prepare().hasSecret("mysql")) {
|
||||
if (!vaultRule.prepare().hasSecretBackend("mysql")) {
|
||||
vaultRule.prepare().mountSecret("mysql");
|
||||
}
|
||||
|
||||
vaultRule.prepare().write("mysql/config/connection",
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
vaultOperations.write("mysql/config/connection",
|
||||
Collections.singletonMap("connection_url", ROOT_CREDENTIALS));
|
||||
|
||||
vaultRule.prepare().write("mysql/roles/readonly",
|
||||
vaultOperations.write("mysql/roles/readonly",
|
||||
Collections.singletonMap("sql", CREATE_USER_AND_GRANT_SQL));
|
||||
}
|
||||
|
||||
|
||||
@@ -39,18 +39,21 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests using the postgresql secret backend. In case this test should fail because of SSL make sure you run
|
||||
* the test within the spring-cloud-vault-config/spring-cloud-vault-config directory as the keystore is referenced with
|
||||
* {@code ../work/keystore.jks}.
|
||||
* Integration tests using the postgresql secret backend. In case this test should fail
|
||||
* because of SSL make sure you run the test within the
|
||||
* spring-cloud-vault-config/spring-cloud-vault-config directory as the keystore is
|
||||
* referenced with {@code ../work/keystore.jks}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = VaultConfigPostgreSqlTests.TestApplication.class)
|
||||
@IntegrationTest({ "spring.cloud.vault.postgresql.enabled=true",
|
||||
"spring.cloud.vault.postgresql.role=readonly", "spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?ssl=false" })
|
||||
"spring.cloud.vault.postgresql.role=readonly",
|
||||
"spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?ssl=false" })
|
||||
public class VaultConfigPostgreSqlTests {
|
||||
|
||||
private final static String POSTGRES_HOST = "localhost";
|
||||
@@ -77,14 +80,16 @@ public class VaultConfigPostgreSqlTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
if (!vaultRule.prepare().hasSecret("postgresql")) {
|
||||
if (!vaultRule.prepare().hasSecretBackend("postgresql")) {
|
||||
vaultRule.prepare().mountSecret("postgresql");
|
||||
}
|
||||
|
||||
vaultRule.prepare().write("postgresql/config/connection",
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
vaultOperations.write("postgresql/config/connection",
|
||||
Collections.singletonMap("connection_url", CONNECTION_URL));
|
||||
|
||||
vaultRule.prepare().write("postgresql/roles/readonly",
|
||||
vaultOperations.write("postgresql/roles/readonly",
|
||||
Collections.singletonMap("sql", CREATE_USER_AND_GRANT_SQL));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,18 +20,13 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -20,8 +20,8 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessor;
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.SecureBackendAccessorFactory;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -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.VaultSecretBackend;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackend;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@@ -15,34 +15,31 @@
|
||||
*/
|
||||
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 java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.AbstractIntegrationTests;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.config.VaultConfigOperations;
|
||||
import org.springframework.cloud.vault.config.VaultTemplate;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.cloud.vault.config.rabbitmq.VaultConfigRabbitMqBootstrapConfiguration.RabbitMqSecureBackendAccessorFactory.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.config.VaultConfigTemplate;
|
||||
import org.springframework.cloud.vault.config.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the rabbitmq secret backend. This test
|
||||
* requires a running RabbitMQ instance, see {@link #RABBITMQ_URI}.
|
||||
* Integration tests for {@link VaultConfigTemplate} using the rabbitmq secret backend.
|
||||
* This test requires a running RabbitMQ instance, see {@link #RABBITMQ_URI}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class RabbitMqSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public class RabbitMqSecretIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
private final static int RABBITMQ_HTTP_MANAGEMENT_PORT = 15672;
|
||||
private final static String RABBITMQ_HOST = "localhost";
|
||||
@@ -56,7 +53,7 @@ public class RabbitMqSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
private final static String VHOSTS_ROLE = "{\"/\":{\"write\": \".*\", \"read\": \".*\"}}";
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private VaultConfigOperations configOperations;
|
||||
private VaultConfigTemplate configOperations;
|
||||
private VaultRabbitMqProperties rabbitmq = new VaultRabbitMqProperties();
|
||||
|
||||
/**
|
||||
@@ -67,13 +64,13 @@ public class RabbitMqSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
assumeTrue(CanConnect.to(new InetSocketAddress(RABBITMQ_HOST,
|
||||
RABBITMQ_HTTP_MANAGEMENT_PORT)));
|
||||
assumeTrue(CanConnect
|
||||
.to(new InetSocketAddress(RABBITMQ_HOST, RABBITMQ_HTTP_MANAGEMENT_PORT)));
|
||||
|
||||
rabbitmq.setEnabled(true);
|
||||
rabbitmq.setRole("readonly");
|
||||
|
||||
if (!prepare().hasSecret(rabbitmq.getBackend())) {
|
||||
if (!prepare().hasSecretBackend(rabbitmq.getBackend())) {
|
||||
prepare().mountSecret(rabbitmq.getBackend());
|
||||
}
|
||||
|
||||
@@ -82,15 +79,16 @@ public class RabbitMqSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
connection.put("username", RABBITMQ_USERNAME);
|
||||
connection.put("password", RABBITMQ_PASSWORD);
|
||||
|
||||
prepare().write(String.format("%s/config/connection", rabbitmq.getBackend()),
|
||||
connection);
|
||||
VaultOperations vaultOperations = prepare().getVaultOperations();
|
||||
|
||||
prepare().write(
|
||||
vaultOperations.write(
|
||||
String.format("%s/config/connection", rabbitmq.getBackend()), connection);
|
||||
|
||||
vaultOperations.write(
|
||||
String.format("%s/roles/%s", rabbitmq.getBackend(), rabbitmq.getRole()),
|
||||
Collections.singletonMap("vhosts", VHOSTS_ROLE));
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties, prepare().newVaultClient(),
|
||||
ClientAuthentication.token(vaultProperties)).opsForConfig();
|
||||
configOperations = new VaultConfigTemplate(vaultOperations, vaultProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.ConnectionFactory;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration tests using the rabbitmq secret backend. In case this test should fail
|
||||
@@ -81,19 +82,21 @@ public class VaultConfigRabbitMqTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
if (!vaultRule.prepare().hasSecret("rabbitmq")) {
|
||||
if (!vaultRule.prepare().hasSecretBackend("rabbitmq")) {
|
||||
vaultRule.prepare().mountSecret("rabbitmq");
|
||||
}
|
||||
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
Map<String, String> connection = new HashMap<>();
|
||||
connection.put("connection_uri", RABBITMQ_URI);
|
||||
connection.put("username", RABBITMQ_USERNAME);
|
||||
connection.put("password", RABBITMQ_PASSWORD);
|
||||
|
||||
vaultRule.prepare().write(String.format("rabbitmq/config/connection"),
|
||||
vaultOperations.write(String.format("rabbitmq/config/connection"),
|
||||
connection);
|
||||
|
||||
vaultRule.prepare().write(String.format("rabbitmq/roles/readonly"),
|
||||
vaultOperations.write(String.format("rabbitmq/roles/readonly"),
|
||||
Collections.singletonMap("vhosts", VHOSTS_ROLE));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<groupId>org.springframework.vault</groupId>
|
||||
<artifactId>spring-vault-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -29,15 +29,59 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test-jar</id>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
|
||||
/**
|
||||
* Factory to convert {@link VaultSecretBackend} instance to a
|
||||
* {@link SecureBackendAccessor}.
|
||||
|
||||
@@ -19,13 +19,12 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CommonsLog
|
||||
@Slf4j
|
||||
public class SecureBackendFactories {
|
||||
|
||||
public static Collection<SecureBackendAccessor> createBackendAcessors(
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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.net.URI;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.authentication.AppIdAuthentication;
|
||||
import org.springframework.vault.authentication.AppIdAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.AppIdUserIdMechanism;
|
||||
import org.springframework.vault.authentication.AwsEc2Authentication;
|
||||
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.ClientCertificateAuthentication;
|
||||
import org.springframework.vault.authentication.DefaultSessionManager;
|
||||
import org.springframework.vault.authentication.IpAddressUserId;
|
||||
import org.springframework.vault.authentication.MacAddressUserId;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.StaticUserId;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.client.VaultClient;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
|
||||
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.core.DefaultVaultClientFactory;
|
||||
import org.springframework.vault.core.VaultClientFactory;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Vault support.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties({ VaultProperties.class,
|
||||
VaultGenericBackendProperties.class })
|
||||
public class VaultBootstrapConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final VaultProperties vaultProperties;
|
||||
private final Collection<VaultSecretBackend> vaultSecretBackends;
|
||||
private final Collection<SecureBackendAccessorFactory<? super VaultSecretBackend>> factories;
|
||||
|
||||
public VaultBootstrapConfiguration(ApplicationContext applicationContext, VaultProperties vaultProperties) {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.vaultProperties = vaultProperties;
|
||||
|
||||
this.vaultSecretBackends = applicationContext
|
||||
.getBeansOfType(VaultSecretBackend.class).values();
|
||||
this.factories = (Collection) applicationContext
|
||||
.getBeansOfType(SecureBackendAccessorFactory.class).values();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VaultPropertySourceLocator vaultPropertySourceLocator(
|
||||
VaultOperations operations, VaultProperties vaultProperties,
|
||||
VaultGenericBackendProperties vaultGenericBackendProperties) {
|
||||
|
||||
Collection<SecureBackendAccessor> backendAccessors = SecureBackendFactories
|
||||
.createBackendAcessors(vaultSecretBackends, factories);
|
||||
VaultConfigTemplate vaultConfigTemplate = new VaultConfigTemplate(operations,
|
||||
vaultProperties);
|
||||
|
||||
return new VaultPropertySourceLocator(vaultConfigTemplate, vaultProperties,
|
||||
vaultGenericBackendProperties, backendAccessors);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClientAuthentication clientAuthentication() {
|
||||
|
||||
VaultClient vaultClient = vaultClient();
|
||||
|
||||
switch (vaultProperties.getAuthentication()) {
|
||||
|
||||
case TOKEN:
|
||||
Assert.hasText("Token (spring.cloud.vault.token) must not be empty",
|
||||
vaultProperties.getToken());
|
||||
return new TokenAuthentication(vaultProperties.getToken());
|
||||
|
||||
case APPID:
|
||||
return appIdAuthentication(vaultProperties, vaultClient);
|
||||
|
||||
case CERT:
|
||||
return new ClientCertificateAuthentication(vaultClient);
|
||||
|
||||
case AWS_EC2:
|
||||
return awsEc2Authentication(vaultProperties, 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ClientFactoryWrapper} containing a
|
||||
* {@link ClientHttpRequestFactory}. {@link ClientHttpRequestFactory} is not exposed
|
||||
* as root bean because {@link ClientHttpRequestFactory} is configured with
|
||||
* {@link ClientOptions} and {@link SslConfiguration} which are not necessarily
|
||||
* applicable for the whole application.
|
||||
*
|
||||
* @return the {@link ClientFactoryWrapper} to wrap a {@link ClientHttpRequestFactory}
|
||||
* instance.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClientFactoryWrapper clientHttpRequestFactoryWrapper() {
|
||||
|
||||
ClientOptions clientOptions = new ClientOptions(
|
||||
vaultProperties.getConnectionTimeout(), vaultProperties.getReadTimeout());
|
||||
|
||||
VaultProperties.Ssl ssl = vaultProperties.getSsl();
|
||||
SslConfiguration sslConfiguration;
|
||||
if (ssl != null) {
|
||||
sslConfiguration = new SslConfiguration(ssl.getKeyStore(),
|
||||
ssl.getKeyStorePassword(), ssl.getTrustStore(),
|
||||
ssl.getTrustStorePassword());
|
||||
}
|
||||
else {
|
||||
sslConfiguration = SslConfiguration.NONE;
|
||||
}
|
||||
|
||||
return new ClientFactoryWrapper(
|
||||
ClientHttpRequestFactoryFactory.create(clientOptions, sslConfiguration));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link VaultClient}
|
||||
* @see #clientHttpRequestFactoryWrapper()
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VaultClient vaultClient() {
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(
|
||||
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory());
|
||||
|
||||
VaultEndpoint vaultEndpoint = new VaultEndpoint();
|
||||
vaultEndpoint.setHost(vaultProperties.getHost());
|
||||
vaultEndpoint.setPort(vaultProperties.getPort());
|
||||
vaultEndpoint.setScheme(vaultProperties.getScheme());
|
||||
|
||||
return new VaultClient(restTemplate, vaultEndpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link VaultClientFactory} to be used with {@link VaultTemplate}. Uses
|
||||
* by default {@link DefaultVaultClientFactory} with the configured
|
||||
* {@link #vaultClient()} instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VaultClientFactory vaultClientFactory() {
|
||||
return new DefaultVaultClientFactory(vaultClient());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link VaultTemplate}.
|
||||
*
|
||||
* @return
|
||||
* @see #vaultClientFactory()
|
||||
* @see #sessionManager(ClientAuthentication)
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VaultTemplate vaultTemplate(ClientAuthentication clientAuthentication) {
|
||||
return new VaultTemplate(vaultClientFactory(),
|
||||
sessionManager(clientAuthentication));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the {@link SessionManager} for Vault session management.
|
||||
* @see SessionManager
|
||||
* @see DefaultSessionManager
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SessionManager sessionManager(ClientAuthentication clientAuthentication) {
|
||||
return new DefaultSessionManager(clientAuthentication);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.vault.VaultBootstrapConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -33,13 +32,12 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @author Stuart Ingram
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnBean(VaultBootstrapConfiguration.class)
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
@ConditionalOnExpression("${health.vault.enabled:true}")
|
||||
@AutoConfigureBefore({ EndpointAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ HealthIndicatorAutoConfiguration.class })
|
||||
public class VaultConfigBootstrapHealthIndicator {
|
||||
@AutoConfigureAfter({ VaultBootstrapConfiguration.class, HealthIndicatorAutoConfiguration.class })
|
||||
public class VaultBootstrapHealthIndicatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "vaultHealthIndicator")
|
||||
@@ -1,123 +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.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.vault.AppIdUserIdMechanism;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultBootstrapConfiguration;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultSecretBackend;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(VaultBootstrapConfiguration.class)
|
||||
public class VaultConfigBootstrapConfiguration implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private Collection<VaultSecretBackend> vaultSecretBackends;
|
||||
private Collection<SecureBackendAccessorFactory<? super VaultSecretBackend>> factories;
|
||||
|
||||
@Bean
|
||||
public VaultGenericBackendProperties vaultGenericBackendProperties() {
|
||||
return new VaultGenericBackendProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VaultOperations vaultOperations(VaultProperties properties, VaultClient client) {
|
||||
|
||||
ClientAuthentication clientAuthentication = clientAuthentication(
|
||||
applicationContext, properties, client);
|
||||
|
||||
return new VaultTemplate(properties, client, clientAuthentication);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VaultPropertySourceLocator vaultPropertySourceLocator(
|
||||
VaultOperations operations, VaultProperties vaultProperties,
|
||||
VaultGenericBackendProperties vaultGenericBackendProperties) {
|
||||
|
||||
Collection<SecureBackendAccessor> backendAccessors = SecureBackendFactories
|
||||
.createBackendAcessors(vaultSecretBackends, factories);
|
||||
|
||||
return new VaultPropertySourceLocator(operations.opsForConfig(), vaultProperties,
|
||||
vaultGenericBackendProperties, backendAccessors);
|
||||
}
|
||||
|
||||
private ClientAuthentication clientAuthentication(
|
||||
ApplicationContext applicationContext, VaultProperties vaultProperties,
|
||||
VaultClient client) {
|
||||
|
||||
ClientAuthentication clientAuthentication;
|
||||
|
||||
if (vaultProperties.getAuthentication() == VaultProperties.AuthenticationMethod.TOKEN) {
|
||||
clientAuthentication = ClientAuthentication.token(vaultProperties);
|
||||
}
|
||||
else if (vaultProperties.getAuthentication() == VaultProperties.AuthenticationMethod.APPID) {
|
||||
|
||||
Map<String, AppIdUserIdMechanism> appIdUserIdMechanisms = applicationContext
|
||||
.getBeansOfType(AppIdUserIdMechanism.class);
|
||||
if (!appIdUserIdMechanisms.isEmpty()) {
|
||||
clientAuthentication = ClientAuthentication.appId(vaultProperties,
|
||||
client, appIdUserIdMechanisms.values().iterator().next());
|
||||
}
|
||||
else {
|
||||
clientAuthentication = ClientAuthentication
|
||||
.appId(vaultProperties, client);
|
||||
}
|
||||
}
|
||||
else {
|
||||
clientAuthentication = ClientAuthentication.create(vaultProperties, client);
|
||||
}
|
||||
|
||||
return clientAuthentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void postConstruct() {
|
||||
|
||||
this.vaultSecretBackends = applicationContext.getBeansOfType(
|
||||
VaultSecretBackend.class).values();
|
||||
this.factories = (Collection) applicationContext.getBeansOfType(
|
||||
SecureBackendAccessorFactory.class).values();
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,8 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.VaultClientResponse;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
|
||||
/**
|
||||
* Interface that specified a basic set of Vault operations, implemented by
|
||||
* {@link VaultConfigTemplate}.
|
||||
@@ -40,5 +35,4 @@ public interface VaultConfigOperations {
|
||||
* @throws IllegalStateException if {@link VaultProperties#isFailFast()} is enabled.
|
||||
*/
|
||||
Map<String, String> read(SecureBackendAccessor secureBackendAccessor);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,36 +15,30 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultClientResponse;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.config.VaultOperations.SessionCallback;
|
||||
import org.springframework.cloud.vault.config.VaultOperations.VaultSession;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.client.VaultResponseEntity;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Central class to retrieve configuration from Vault.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see VaultClient
|
||||
* @see ClientAuthentication
|
||||
* @see VaultOperations
|
||||
*/
|
||||
@CommonsLog
|
||||
@Slf4j
|
||||
public class VaultConfigTemplate implements VaultConfigOperations {
|
||||
|
||||
private final VaultOperations vaultOperations;
|
||||
private final VaultProperties properties;
|
||||
private final VaultConfigSessionCallback callback;
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultConfigTemplate}.
|
||||
@@ -52,38 +46,48 @@ public class VaultConfigTemplate implements VaultConfigOperations {
|
||||
* @param vaultOperations must not be {@literal null}.
|
||||
* @param properties must not be {@literal null}.
|
||||
*/
|
||||
public VaultConfigTemplate(VaultOperations vaultOperations, VaultProperties properties) {
|
||||
public VaultConfigTemplate(VaultOperations vaultOperations,
|
||||
VaultProperties properties) {
|
||||
|
||||
Assert.notNull(vaultOperations, "VaultOperations must not be null!");
|
||||
Assert.notNull(properties, "VaultProperties must not be null!");
|
||||
|
||||
this.vaultOperations = vaultOperations;
|
||||
this.properties = properties;
|
||||
this.callback = new VaultConfigSessionCallback(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> read(SecureBackendAccessor secureBackendAccessor) {
|
||||
public Map<String, String> read(final SecureBackendAccessor secureBackendAccessor) {
|
||||
|
||||
Assert.notNull(secureBackendAccessor, "SecureBackendAccessor must not be null!");
|
||||
|
||||
VaultClientResponse response = vaultOperations.doWithVault("{backend}/{key}",
|
||||
secureBackendAccessor.variables(), callback);
|
||||
VaultResponseEntity<VaultResponse> response = vaultOperations.doWithVault(
|
||||
new VaultOperations.SessionCallback<VaultResponseEntity<VaultResponse>>() {
|
||||
@Override
|
||||
public VaultResponseEntity<VaultResponse> doWithVault(
|
||||
VaultOperations.VaultSession session) {
|
||||
|
||||
return session.exchange("{backend}/{key}", HttpMethod.GET, null,
|
||||
VaultResponse.class, secureBackendAccessor.variables());
|
||||
}
|
||||
});
|
||||
|
||||
log.info(String.format("Fetching config from Vault at: %s", response.getUri()));
|
||||
|
||||
if (response.getStatusCode() == HttpStatus.OK) {
|
||||
return secureBackendAccessor
|
||||
.transformProperties(response.getBody().getData());
|
||||
|
||||
Map<String, String> stringMap = toStringMap(response.getBody().getData());
|
||||
|
||||
return secureBackendAccessor.transformProperties(stringMap);
|
||||
}
|
||||
|
||||
if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
|
||||
log.info(String
|
||||
.format("Could not locate PropertySource: %s", "key not found"));
|
||||
log.info(String.format("Could not locate PropertySource: %s",
|
||||
"key not found"));
|
||||
}
|
||||
else if (properties.isFailFast()) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Could not locate PropertySource and the fail fast property is set, failing Status %d %s",
|
||||
response.getStatusCode().value(), response.getMessage()));
|
||||
throw new IllegalStateException(String.format(
|
||||
"Could not locate PropertySource and the fail fast property is set, failing Status %d %s",
|
||||
response.getStatusCode().value(), response.getMessage()));
|
||||
}
|
||||
else {
|
||||
log.warn(String.format("Could not locate PropertySource: Status %d %s",
|
||||
@@ -93,19 +97,17 @@ public class VaultConfigTemplate implements VaultConfigOperations {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
static class VaultConfigSessionCallback implements SessionCallback {
|
||||
private Map<String, String> toStringMap(Map<String, Object> data) {
|
||||
|
||||
private final Log log;
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (String key : data.keySet()) {
|
||||
Object value = data.get(key);
|
||||
|
||||
public VaultConfigSessionCallback(Log log) {
|
||||
this.log = log;
|
||||
if (value != null) {
|
||||
result.put(key, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultClientResponse doWithVault(URI uri, VaultSession session) {
|
||||
log.info(String.format("Fetching config from Vault at: %s", uri));
|
||||
return session.read(uri);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ package org.springframework.cloud.vault.config;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.cloud.vault.VaultHealthResponse;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.support.VaultHealth;
|
||||
|
||||
/**
|
||||
* @author Stuart Ingram
|
||||
@@ -26,14 +27,14 @@ import org.springframework.cloud.vault.VaultHealthResponse;
|
||||
public class VaultHealthIndicator implements HealthIndicator {
|
||||
|
||||
@Autowired
|
||||
private VaultTemplate vaultTemplate;
|
||||
private VaultOperations vaultOperations;
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
|
||||
try {
|
||||
|
||||
VaultHealthResponse vaultHealthResponse = vaultTemplate.health();
|
||||
VaultHealth vaultHealthResponse = vaultOperations.opsForSys().health();
|
||||
|
||||
if (!vaultHealthResponse.isInitialized()) {
|
||||
return Health.down().withDetail("state", "Vault uninitialized").build();
|
||||
@@ -44,14 +45,14 @@ public class VaultHealthIndicator implements HealthIndicator {
|
||||
}
|
||||
|
||||
if (vaultHealthResponse.isStandby()) {
|
||||
return Health.outOfService().withDetail("state", "Vault in standby").build();
|
||||
return Health.outOfService().withDetail("state", "Vault in standby")
|
||||
.build();
|
||||
}
|
||||
|
||||
return Health.up().build();
|
||||
}
|
||||
catch (Exception e) {
|
||||
return Health.down().build();
|
||||
return Health.down(e).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,109 +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.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.VaultClientResponse;
|
||||
import org.springframework.cloud.vault.VaultHealthResponse;
|
||||
|
||||
/**
|
||||
* Interface that specified a basic set of Vault operations, implemented by
|
||||
* {@link VaultTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface VaultOperations {
|
||||
|
||||
/**
|
||||
* @return the operations interface to interact with Vault configuration data.
|
||||
*/
|
||||
VaultConfigOperations opsForConfig();
|
||||
|
||||
/**
|
||||
* Executes a Vault {@link SessionCallback}. Allows to interact with Vault in an
|
||||
* authenticated session.
|
||||
*
|
||||
* @param path the path of the resource, e.g. {@code transit/encrypt/foo}, must not be
|
||||
* empty or {@literal null}.
|
||||
* @param sessionCallback the request.
|
||||
* @return
|
||||
*/
|
||||
<T> T doWithVault(String path, SessionCallback sessionCallback);
|
||||
|
||||
/**
|
||||
* Executes a Vault {@link SessionCallback}. Allows to interact with Vault in an
|
||||
* authenticated session.
|
||||
*
|
||||
* @param pathTemplate the path of the resource, e.g. {@code transit/ key}/foo}, must
|
||||
* not be empty or {@literal null}. * @param variables the variables for expansion of
|
||||
* the {@code pathTemplate}, must not be {@literal null}.
|
||||
* @param sessionCallback the request.
|
||||
* @return
|
||||
*/
|
||||
<T> T doWithVault(String pathTemplate, Map<String, ?> variables,
|
||||
SessionCallback sessionCallback);
|
||||
|
||||
/**
|
||||
* Query the current Vault service for it's health status.
|
||||
*
|
||||
* @return A {@link VaultHealthResponse} containing the current service status.
|
||||
*/
|
||||
VaultHealthResponse health();
|
||||
|
||||
/**
|
||||
* Callback to execute actions within an authenticated {@link VaultSession}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface SessionCallback {
|
||||
|
||||
/**
|
||||
* Callback method.
|
||||
*
|
||||
* @param uri the URI that is used for the request, must not be {@literal null}.
|
||||
* @param session session to use, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
<T> T doWithVault(URI uri, VaultSession session);
|
||||
}
|
||||
|
||||
/**
|
||||
* An authenticated Vault session.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface VaultSession {
|
||||
|
||||
/**
|
||||
* Read data from the given Vault {@code uri}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @return the {@link VaultClientResponse}.
|
||||
*/
|
||||
public VaultClientResponse read(URI uri);
|
||||
|
||||
/**
|
||||
* Write data to the given Vault {@code uri}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return the {@link VaultClientResponse}.
|
||||
*/
|
||||
public VaultClientResponse write(URI uri, Object entity);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
@@ -94,14 +95,14 @@ public class VaultProperties {
|
||||
/**
|
||||
* Property value for UserId generation using a Mac-Address.
|
||||
*
|
||||
* @see MacAddressUserId
|
||||
* @see org.springframework.vault.authentication.MacAddressUserId
|
||||
*/
|
||||
public final static String MAC_ADDRESS = "MAC_ADDRESS";
|
||||
|
||||
/**
|
||||
* Property value for UserId generation using an IP-Address.
|
||||
*
|
||||
* @see IpAddressUserId
|
||||
* @see org.springframework.vault.authentication.IpAddressUserId
|
||||
*/
|
||||
public final static String IP_ADDRESS = "IP_ADDRESS";
|
||||
|
||||
@@ -19,20 +19,19 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* A {@link EnumerablePropertySource} backed by {@link VaultConfigOperations}.
|
||||
* A {@link EnumerablePropertySource} backed by {@link VaultConfigTemplate}.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CommonsLog
|
||||
class VaultPropertySource extends EnumerablePropertySource<VaultConfigOperations> {
|
||||
@Slf4j
|
||||
class VaultPropertySource extends EnumerablePropertySource<VaultConfigTemplate> {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
private final SecureBackendAccessor secureBackendAccessor;
|
||||
@@ -45,12 +44,12 @@ class VaultPropertySource extends EnumerablePropertySource<VaultConfigOperations
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param secureBackendAccessor must not be {@literal null}.
|
||||
*/
|
||||
public VaultPropertySource(VaultConfigOperations operations,
|
||||
VaultProperties properties, SecureBackendAccessor secureBackendAccessor) {
|
||||
public VaultPropertySource(VaultConfigTemplate operations, VaultProperties properties,
|
||||
SecureBackendAccessor secureBackendAccessor) {
|
||||
|
||||
super(secureBackendAccessor.getName(), operations);
|
||||
|
||||
Assert.notNull(operations, "VaultConfigOperations must not be null!");
|
||||
Assert.notNull(operations, "VaultConfigTemplate must not be null!");
|
||||
Assert.notNull(properties, "VaultProperties must not be null!");
|
||||
Assert.notNull(secureBackendAccessor, "SecureBackendAccessor must not be null!");
|
||||
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import static org.springframework.cloud.vault.config.SecureBackendAccessors.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -23,8 +24,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -33,10 +32,8 @@ import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.vault.config.SecureBackendAccessors.*;
|
||||
|
||||
/**
|
||||
* {@link PropertySourceLocator} using {@link VaultClient}.
|
||||
* {@link PropertySourceLocator} using {@link VaultConfigTemplate}.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
@@ -44,7 +41,7 @@ import static org.springframework.cloud.vault.config.SecureBackendAccessors.*;
|
||||
*/
|
||||
class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrdered {
|
||||
|
||||
private final VaultConfigOperations operations;
|
||||
private final VaultConfigTemplate operations;
|
||||
private final VaultProperties properties;
|
||||
private final VaultGenericBackendProperties genericBackendProperties;
|
||||
private final Collection<SecureBackendAccessor> backendAccessors;
|
||||
@@ -57,7 +54,7 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
* @param genericBackendProperties must not be {@literal null}.
|
||||
* @param backendAccessors must not be {@literal null}.
|
||||
*/
|
||||
public VaultPropertySourceLocator(VaultConfigOperations operations,
|
||||
public VaultPropertySourceLocator(VaultConfigTemplate operations,
|
||||
VaultProperties properties,
|
||||
VaultGenericBackendProperties genericBackendProperties,
|
||||
Collection<SecureBackendAccessor> backendAccessors) {
|
||||
@@ -79,7 +76,8 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
|
||||
if (environment instanceof ConfigurableEnvironment) {
|
||||
|
||||
CompositePropertySource propertySource = createCompositePropertySource((ConfigurableEnvironment) environment);
|
||||
CompositePropertySource propertySource = createCompositePropertySource(
|
||||
(ConfigurableEnvironment) environment);
|
||||
initialize(propertySource);
|
||||
|
||||
return propertySource;
|
||||
@@ -130,8 +128,9 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
|
||||
if (StringUtils.hasText(propertySourceContext)) {
|
||||
|
||||
VaultPropertySource vaultPropertySource = createVaultPropertySource(generic(
|
||||
genericBackendProperties.getBackend(), propertySourceContext));
|
||||
VaultPropertySource vaultPropertySource = createVaultPropertySource(
|
||||
generic(genericBackendProperties.getBackend(),
|
||||
propertySourceContext));
|
||||
|
||||
propertySource.addPropertySource(vaultPropertySource);
|
||||
}
|
||||
@@ -140,7 +139,8 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
|
||||
for (SecureBackendAccessor backendAccessor : backendAccessors) {
|
||||
|
||||
VaultPropertySource vaultPropertySource = createVaultPropertySource(backendAccessor);
|
||||
VaultPropertySource vaultPropertySource = createVaultPropertySource(
|
||||
backendAccessor);
|
||||
propertySource.addPropertySource(vaultPropertySource);
|
||||
}
|
||||
return propertySource;
|
||||
@@ -153,7 +153,8 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
}
|
||||
}
|
||||
|
||||
private VaultPropertySource createVaultPropertySource(SecureBackendAccessor accessor) {
|
||||
private VaultPropertySource createVaultPropertySource(
|
||||
SecureBackendAccessor accessor) {
|
||||
return new VaultPropertySource(this.operations, this.properties, accessor);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
/**
|
||||
* A secret backend that can return secrets from Vault.
|
||||
@@ -1,30 +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 lombok.Data;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
|
||||
/**
|
||||
* State of the Vault client.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
class VaultState {
|
||||
private VaultToken token;
|
||||
}
|
||||
@@ -1,125 +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.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultClientResponse;
|
||||
import org.springframework.cloud.vault.VaultHealthResponse;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This class encapsulates main Vault interaction. {@link VaultTemplate} will log into
|
||||
* Vault on initialization and use the token throughout the whole lifetime.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class VaultTemplate implements InitializingBean, VaultOperations {
|
||||
|
||||
private final static String HEALTH_URL_TEMPLATE = "sys/health";
|
||||
|
||||
private final VaultProperties properties;
|
||||
private final VaultClient client;
|
||||
private final ClientAuthentication clientAuthentication;
|
||||
private final transient VaultState vaultState = new VaultState();
|
||||
private final VaultSession vaultSession;
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultConfigTemplate} for the given {@link VaultProperties},
|
||||
* {@link VaultClient} and {@link ClientAuthentication}.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param client must not be {@literal null}.
|
||||
* @param clientAuthentication must not be {@literal null}.
|
||||
*/
|
||||
public VaultTemplate(VaultProperties properties, VaultClient client,
|
||||
ClientAuthentication clientAuthentication) {
|
||||
|
||||
Assert.notNull(properties, "VaultProperties must not be null!");
|
||||
Assert.notNull(client, "VaultClient must not be null!");
|
||||
Assert.notNull(clientAuthentication, "ClientAuthentication must not be null!");
|
||||
|
||||
this.properties = properties;
|
||||
this.client = client;
|
||||
this.clientAuthentication = clientAuthentication;
|
||||
this.vaultSession = new VaultSession() {
|
||||
@Override
|
||||
public VaultClientResponse read(URI uri) {
|
||||
return VaultTemplate.this.client.read(uri, getToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultClientResponse write(URI uri, Object entity) {
|
||||
return VaultTemplate.this.client.write(uri, entity, getToken());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
login();
|
||||
}
|
||||
|
||||
private void login() {
|
||||
vaultState.setToken(clientAuthentication.login());
|
||||
}
|
||||
|
||||
private VaultToken getToken() {
|
||||
|
||||
if (vaultState.getToken() == null) {
|
||||
login();
|
||||
}
|
||||
|
||||
return vaultState.getToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultConfigOperations opsForConfig() {
|
||||
return new VaultConfigTemplate(this, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T doWithVault(String path, SessionCallback sessionCallback) {
|
||||
|
||||
Assert.notNull(sessionCallback, "SessionCallback must not be null!");
|
||||
|
||||
URI uri = VaultClient.buildUri(properties, path);
|
||||
return sessionCallback.doWithVault(uri, vaultSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T doWithVault(String pathTemplate, Map<String, ?> variables,
|
||||
SessionCallback sessionCallback) {
|
||||
|
||||
Assert.notNull(sessionCallback, "SessionCallback must not be null!");
|
||||
|
||||
URI uri = client.buildUri(properties, pathTemplate, variables);
|
||||
return sessionCallback.doWithVault(uri, vaultSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultHealthResponse health() {
|
||||
|
||||
URI uri = VaultClient.buildUri(properties, HEALTH_URL_TEMPLATE);
|
||||
return client.health(uri);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Bootstrap Configuration
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.vault.config.VaultConfigBootstrapConfiguration,\
|
||||
org.springframework.cloud.vault.config.VaultConfigBootstrapHealthIndicator
|
||||
org.springframework.cloud.vault.config.VaultBootstrapConfiguration,\
|
||||
org.springframework.cloud.vault.config.VaultBootstrapHealthIndicatorConfiguration
|
||||
@@ -1,72 +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 org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.IpAddressUserId;
|
||||
import org.springframework.cloud.vault.TestRestTemplateFactory;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties.AppIdProperties;
|
||||
import org.springframework.cloud.vault.VaultProperties.AuthenticationMethod;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using {@link AuthenticationMethod#APPID}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class AppIdAuthenticationIntegrationTests extends GenericSecretIntegrationTests {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
this.vaultProperties = Settings.createVaultProperties();
|
||||
|
||||
super.setUp();
|
||||
|
||||
AppIdProperties appId = configureAppIdProperties();
|
||||
vaultProperties.setApplicationName("myapp");
|
||||
vaultProperties.setAuthentication(AuthenticationMethod.APPID);
|
||||
vaultProperties.setAppId(appId);
|
||||
|
||||
if (!prepare().hasAuth(appId.getAppIdPath())) {
|
||||
prepare().mountAuth(appId.getAppIdPath());
|
||||
}
|
||||
|
||||
IpAddressUserId userIdMechanism = new IpAddressUserId();
|
||||
String userId = userIdMechanism.createUserId();
|
||||
prepare().mapAppId(vaultProperties.getApplicationName());
|
||||
prepare().mapUserId(vaultProperties.getApplicationName(), userId);
|
||||
|
||||
VaultClient vaultClient = prepare().newVaultClient();
|
||||
|
||||
ClientAuthentication clientAuthentication = ClientAuthentication.appId(
|
||||
vaultProperties, vaultClient, userIdMechanism);
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties,
|
||||
vaultClient,
|
||||
clientAuthentication).opsForConfig();
|
||||
}
|
||||
|
||||
private AppIdProperties configureAppIdProperties() {
|
||||
|
||||
AppIdProperties appId = new AppIdProperties();
|
||||
appId.setUserId(AppIdProperties.IP_ADDRESS);
|
||||
return appId;
|
||||
}
|
||||
}
|
||||
@@ -15,46 +15,42 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.vault.AbstractIntegrationTests;
|
||||
import org.springframework.cloud.vault.ClientAuthentication;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.cloud.vault.config.SecureBackendAccessors.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the generic secret backend.
|
||||
* Integration tests for {@link VaultConfigTemplate} using the generic secret backend.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class GenericSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public class GenericSecretIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
protected VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
protected VaultConfigOperations configOperations;
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private VaultConfigOperations configOperations;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
vaultProperties.setFailFast(false);
|
||||
prepare().writeSecret("app-name", (Map) createData());
|
||||
prepare().getVaultOperations().write("secret/app-name", (Map) createData());
|
||||
|
||||
configOperations = new VaultTemplate(vaultProperties, prepare().newVaultClient(),
|
||||
ClientAuthentication.token(vaultProperties)).opsForConfig();
|
||||
configOperations = new VaultConfigTemplate(prepare().getVaultOperations(),
|
||||
vaultProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnSecretsCorrectly() throws Exception {
|
||||
|
||||
Map<String, String> secretProperties = configOperations.read(generic("secret",
|
||||
"app-name"));
|
||||
Map<String, String> secretProperties = configOperations
|
||||
.read(generic("secret", "app-name"));
|
||||
|
||||
assertThat(secretProperties).containsAllEntriesOf(createExpectedMap());
|
||||
}
|
||||
@@ -62,8 +58,8 @@ public class GenericSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
@Test
|
||||
public void shouldReturnNullIfNotFound() throws Exception {
|
||||
|
||||
Map<String, String> secretProperties = configOperations.read(generic("secret",
|
||||
"missing"));
|
||||
Map<String, String> secretProperties = configOperations
|
||||
.read(generic("secret", "missing"));
|
||||
|
||||
assertThat(secretProperties).isEmpty();
|
||||
}
|
||||
|
||||
@@ -1,47 +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 org.junit.Test;
|
||||
import org.springframework.cloud.vault.TestRestTemplateFactory;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
import org.springframework.cloud.vault.util.PrepareVault;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link PrepareVault}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PrepareVaultTests {
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private PrepareVault prepareVault = new PrepareVault(TestRestTemplateFactory.create(vaultProperties));
|
||||
|
||||
@Test
|
||||
public void initializeShouldCreateANewVault() throws Exception {
|
||||
|
||||
prepareVault.setRootToken(Settings.token());
|
||||
prepareVault.setVaultProperties(vaultProperties);
|
||||
|
||||
if (!prepareVault.isAvailable()) {
|
||||
VaultToken rootToken = prepareVault.initializeVault();
|
||||
prepareVault.setRootToken(rootToken);
|
||||
prepareVault.createToken(vaultProperties.getToken(), "root");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ package org.springframework.cloud.vault.config;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@@ -29,14 +31,18 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.AppIdUserIdMechanism;
|
||||
import org.springframework.cloud.vault.config.VaultConfigAppIdCustomMechanismTests.BootstrapConfiguration;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.authentication.AppIdAuthentication;
|
||||
import org.springframework.vault.authentication.AppIdAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.AppIdUserIdMechanism;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.client.VaultClient;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
@@ -54,10 +60,6 @@ public class VaultConfigAppIdCustomMechanismTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
vaultRule.prepare().writeSecret(
|
||||
VaultConfigAppIdCustomMechanismTests.class.getSimpleName(),
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
vaultProperties.setAuthentication(VaultProperties.AuthenticationMethod.APPID);
|
||||
|
||||
@@ -65,12 +67,29 @@ public class VaultConfigAppIdCustomMechanismTests {
|
||||
vaultRule.prepare().mountAuth(vaultProperties.getAppId().getAppIdPath());
|
||||
}
|
||||
|
||||
vaultRule.prepare()
|
||||
.mapAppId(VaultConfigAppIdCustomMechanismTests.class.getSimpleName());
|
||||
vaultRule.prepare().mapUserId(
|
||||
VaultConfigAppIdCustomMechanismTests.class.getSimpleName(),
|
||||
new StaticUserIdMechanism().createUserId());
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
String appId = VaultConfigAppIdCustomMechanismTests.class.getSimpleName();
|
||||
|
||||
vaultOperations.write(
|
||||
"secret/" + VaultConfigAppIdCustomMechanismTests.class.getSimpleName(),
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
|
||||
Map<String, String> appIdData = new HashMap<String, String>();
|
||||
appIdData.put("value", "root"); // policy
|
||||
appIdData.put("display_name", "this is my test application");
|
||||
|
||||
vaultOperations.write(String.format("auth/app-id/map/app-id/%s", appId),
|
||||
appIdData);
|
||||
|
||||
Map<String, String> userIdData = new HashMap<String, String>();
|
||||
userIdData.put("value", appId); // name of the app-id
|
||||
userIdData.put("cidr_block", "0.0.0.0/0");
|
||||
|
||||
String userId = new StaticUserIdMechanism().createUserId();
|
||||
|
||||
vaultOperations.write(String.format("auth/app-id/map/user-id/%s", userId),
|
||||
userIdData);
|
||||
}
|
||||
|
||||
@Value("${vault.value}")
|
||||
@@ -78,7 +97,6 @@ public class VaultConfigAppIdCustomMechanismTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
|
||||
assertThat(configValue).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@@ -93,10 +111,16 @@ public class VaultConfigAppIdCustomMechanismTests {
|
||||
@Configuration
|
||||
public static class BootstrapConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("use.custom.config")
|
||||
AppIdUserIdMechanism appIdUserIdMechanism() {
|
||||
return new StaticUserIdMechanism();
|
||||
@Bean
|
||||
ClientAuthentication clientAuthentication(VaultClient vaultClient) {
|
||||
return new AppIdAuthentication(
|
||||
AppIdAuthenticationOptions
|
||||
.builder()
|
||||
.appId("VaultConfigAppIdCustomMechanismTests")
|
||||
.userIdMechanism(new StaticUserIdMechanism()).build(),
|
||||
vaultClient);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cloud.vault.config;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@@ -27,16 +29,17 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.IpAddressUserId;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.authentication.IpAddressUserId;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration test using config infrastructure with AppId authentication. In case this test should fail because of SSL
|
||||
* make sure you run the test within the spring-cloud-vault-config/spring-cloud-vault-config directory as the keystore
|
||||
* is referenced with {@code ../work/keystore.jks}.
|
||||
* Integration test using config infrastructure with AppId authentication. In case this
|
||||
* test should fail because of SSL make sure you run the test within the
|
||||
* spring-cloud-vault-config/spring-cloud-vault-config directory as the keystore is
|
||||
* referenced with {@code ../work/keystore.jks}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@@ -53,9 +56,6 @@ public class VaultConfigAppIdTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
vaultRule.prepare().writeSecret(VaultConfigAppIdTests.class.getSimpleName(),
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
vaultProperties.setAuthentication(VaultProperties.AuthenticationMethod.APPID);
|
||||
vaultProperties.getAppId().setUserId(VaultProperties.AppIdProperties.IP_ADDRESS);
|
||||
@@ -64,9 +64,29 @@ public class VaultConfigAppIdTests {
|
||||
vaultRule.prepare().mountAuth(vaultProperties.getAppId().getAppIdPath());
|
||||
}
|
||||
|
||||
vaultRule.prepare().mapAppId(VaultConfigAppIdTests.class.getSimpleName());
|
||||
vaultRule.prepare().mapUserId(VaultConfigAppIdTests.class.getSimpleName(),
|
||||
new IpAddressUserId().createUserId());
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
String appId = VaultConfigAppIdTests.class.getSimpleName();
|
||||
|
||||
vaultOperations.write(
|
||||
"secret/" + VaultConfigAppIdTests.class.getSimpleName(),
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
|
||||
Map<String, String> appIdData = new HashMap<String, String>();
|
||||
appIdData.put("value", "root"); // policy
|
||||
appIdData.put("display_name", "this is my test application");
|
||||
|
||||
vaultOperations.write(String.format("auth/app-id/map/app-id/%s", appId),
|
||||
appIdData);
|
||||
|
||||
Map<String, String> userIdData = new HashMap<String, String>();
|
||||
userIdData.put("value", appId); // name of the app-id
|
||||
userIdData.put("cidr_block", "0.0.0.0/0");
|
||||
|
||||
String userId = new IpAddressUserId().createUserId();
|
||||
|
||||
vaultOperations.write(String.format("auth/app-id/map/user-id/%s", userId),
|
||||
userIdData);
|
||||
}
|
||||
|
||||
@Value("${vault.value}")
|
||||
|
||||
@@ -27,11 +27,11 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.client.VaultClient;
|
||||
|
||||
/**
|
||||
* Integration test using config infrastructure with token authentication. In case this
|
||||
@@ -52,7 +52,7 @@ public class VaultConfigDisabledTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
vaultRule.prepare().writeSecret("testVaultApp",
|
||||
vaultRule.prepare().getVaultOperations().write("secret/testVaultApp",
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public class VaultConfigGenericBackendDisabledTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
vaultRule.prepare().writeSecret("testVaultApp",
|
||||
vaultRule.prepare().getVaultOperations().write("secret/testVaultApp",
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,12 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.client.VaultClient;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
@@ -53,7 +53,7 @@ public class VaultConfigTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
vaultRule.prepare().writeSecret("testVaultApp",
|
||||
vaultRule.prepare().getVaultOperations().write("secret/testVaultApp",
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,29 +15,28 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.cloud.vault.util.Settings.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.util.Files;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.cloud.vault.util.Settings.*;
|
||||
|
||||
import org.assertj.core.util.Files;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration test using config infrastructure with TLS certificate authentication. In
|
||||
@@ -61,16 +60,18 @@ public class VaultConfigTlsCertAuthenticationTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
vaultRule.prepare().writeSecret(
|
||||
VaultConfigTlsCertAuthenticationTests.class.getSimpleName(),
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
|
||||
if (!vaultRule.prepare().hasAuth(vaultProperties.getSsl().getCertAuthPath())) {
|
||||
vaultRule.prepare().mountAuth(vaultProperties.getSsl().getCertAuthPath());
|
||||
}
|
||||
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
vaultOperations.write(
|
||||
"secret/" + VaultConfigTlsCertAuthenticationTests.class.getSimpleName(),
|
||||
Collections.singletonMap("vault.value", "foo"));
|
||||
|
||||
File workDir = findWorkDir();
|
||||
|
||||
String certificate = Files.contentOf(
|
||||
@@ -80,7 +81,7 @@ public class VaultConfigTlsCertAuthenticationTests {
|
||||
role.put("certificate", certificate);
|
||||
role.put("policies", "root");
|
||||
|
||||
vaultRule.prepare().write("auth/cert/certs/my-role", role);
|
||||
vaultOperations.write("auth/cert/certs/my-role", role);
|
||||
}
|
||||
|
||||
@Value("${vault.value}")
|
||||
|
||||
@@ -29,11 +29,13 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
/**
|
||||
* Integration test using config infrastructure with token authentication. In case this test should fail because of SSL
|
||||
* make sure you run the test within the spring-cloud-vault-config/spring-cloud-vault-config directory as the keystore
|
||||
* is referenced with {@code ../work/keystore.jks}.
|
||||
* Integration test using config infrastructure with token authentication. In case this
|
||||
* test should fail because of SSL make sure you run the test within the
|
||||
* spring-cloud-vault-config/spring-cloud-vault-config directory as the keystore is
|
||||
* referenced with {@code ../work/keystore.jks}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@@ -48,11 +50,13 @@ public class VaultConfigWithContextTests {
|
||||
VaultRule vaultRule = new VaultRule();
|
||||
vaultRule.before();
|
||||
|
||||
vaultRule.prepare().writeSecret("testVaultApp/my-profile",
|
||||
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
|
||||
|
||||
vaultOperations.write("secret/testVaultApp/my-profile",
|
||||
Collections.singletonMap("vault.value", "hello"));
|
||||
|
||||
vaultRule.prepare().writeSecret("testVaultApp",
|
||||
Collections.singletonMap("vault.value", "worls"));
|
||||
vaultOperations.write("secret/testVaultApp",
|
||||
Collections.singletonMap("vault.value", "world"));
|
||||
}
|
||||
|
||||
@Value("${vault.value}")
|
||||
@@ -60,7 +64,6 @@ public class VaultConfigWithContextTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
|
||||
assertThat(configValue).isEqualTo("hello");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
* 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 static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -26,7 +26,9 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.cloud.vault.VaultHealthResponse;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.core.VaultSysOperations;
|
||||
import org.springframework.vault.support.VaultHealth;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
@@ -38,15 +40,26 @@ public class VaultHealthIndicatorUnitTests {
|
||||
VaultHealthIndicator healthIndicator = new VaultHealthIndicator();
|
||||
|
||||
@Mock
|
||||
VaultTemplate vaultTemplate;
|
||||
VaultOperations vaultOperations;
|
||||
|
||||
@Mock
|
||||
VaultSysOperations vaultSysOperations;
|
||||
|
||||
@Mock
|
||||
VaultHealth healthResponse;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
|
||||
when(vaultOperations.opsForSys()).thenReturn(vaultSysOperations);
|
||||
when(vaultSysOperations.health()).thenReturn(healthResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReportHealthyService() throws Exception {
|
||||
|
||||
VaultHealthResponse healthResponse = new VaultHealthResponse();
|
||||
healthResponse.setInitialized(true);
|
||||
|
||||
when(vaultTemplate.health()).thenReturn(healthResponse);
|
||||
when(healthResponse.isInitialized()).thenReturn(true);
|
||||
when(vaultOperations.opsForSys()).thenReturn(vaultSysOperations);
|
||||
|
||||
Health health = healthIndicator.health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
@@ -56,11 +69,8 @@ public class VaultHealthIndicatorUnitTests {
|
||||
@Test
|
||||
public void shouldReportSealedService() throws Exception {
|
||||
|
||||
VaultHealthResponse healthResponse = new VaultHealthResponse();
|
||||
healthResponse.setInitialized(true);
|
||||
healthResponse.setSealed(true);
|
||||
|
||||
when(vaultTemplate.health()).thenReturn(healthResponse);
|
||||
when(healthResponse.isInitialized()).thenReturn(true);
|
||||
when(healthResponse.isSealed()).thenReturn(true);
|
||||
|
||||
Health health = healthIndicator.health();
|
||||
|
||||
@@ -71,10 +81,6 @@ public class VaultHealthIndicatorUnitTests {
|
||||
@Test
|
||||
public void shouldReportUninitializedService() throws Exception {
|
||||
|
||||
VaultHealthResponse healthResponse = new VaultHealthResponse();
|
||||
|
||||
when(vaultTemplate.health()).thenReturn(healthResponse);
|
||||
|
||||
Health health = healthIndicator.health();
|
||||
|
||||
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
|
||||
@@ -84,11 +90,8 @@ public class VaultHealthIndicatorUnitTests {
|
||||
@Test
|
||||
public void shouldReportStandbyService() throws Exception {
|
||||
|
||||
VaultHealthResponse healthResponse = new VaultHealthResponse();
|
||||
healthResponse.setInitialized(true);
|
||||
healthResponse.setStandby(true);
|
||||
|
||||
when(vaultTemplate.health()).thenReturn(healthResponse);
|
||||
when(healthResponse.isInitialized()).thenReturn(true);
|
||||
when(healthResponse.isStandby()).thenReturn(true);
|
||||
|
||||
Health health = healthIndicator.health();
|
||||
|
||||
@@ -99,10 +102,11 @@ public class VaultHealthIndicatorUnitTests {
|
||||
@Test
|
||||
public void exceptionsShouldReportDownStatus() throws Exception {
|
||||
|
||||
when(vaultTemplate.health()).thenThrow(new IllegalStateException());
|
||||
reset(vaultSysOperations);
|
||||
when(vaultSysOperations.health()).thenThrow(new IllegalStateException());
|
||||
|
||||
Health health = healthIndicator.health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
|
||||
assertThat(health.getDetails()).isEmpty();
|
||||
assertThat(health.getDetails()).containsKey("error");
|
||||
}
|
||||
}
|
||||
@@ -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.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultPropertySource}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class VaultPropertySourceIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
prepare().getVaultOperations().write("secret/myapp",
|
||||
Collections.singletonMap("key", "value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadValue() throws Exception {
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
|
||||
VaultPropertySource propertySource = new VaultPropertySource(
|
||||
new VaultConfigTemplate(prepare().getVaultOperations(), vaultProperties),
|
||||
vaultProperties, SecureBackendAccessors.generic("secret", "myapp"));
|
||||
|
||||
propertySource.init();
|
||||
|
||||
assertThat(propertySource.getPropertyNames()).contains("key");
|
||||
assertThat(propertySource.getProperty("key")).isEqualTo("value");
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
package org.springframework.cloud.vault.util;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.springframework.cloud.vault.util.PrepareVault;
|
||||
@@ -25,7 +25,7 @@ import org.springframework.cloud.vault.util.VaultRule;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public abstract class AbstractIntegrationTests {
|
||||
public abstract class IntegrationTestSupport {
|
||||
|
||||
@Rule
|
||||
public final VaultRule vaultRule = new VaultRule();
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.core.VaultSysOperations;
|
||||
import org.springframework.vault.support.VaultInitializationRequest;
|
||||
import org.springframework.vault.support.VaultInitializationResponse;
|
||||
import org.springframework.vault.support.VaultMount;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.vault.support.VaultTokenRequest;
|
||||
import org.springframework.vault.support.VaultTokenResponse;
|
||||
import org.springframework.vault.support.VaultUnsealStatus;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PrepareVault {
|
||||
|
||||
private final VaultOperations vaultOperations;
|
||||
private final VaultSysOperations adminOperations;
|
||||
|
||||
public PrepareVault(VaultOperations vaultOperations) {
|
||||
|
||||
this.vaultOperations = vaultOperations;
|
||||
this.adminOperations = vaultOperations.opsForSys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Vault and unseal the vault.
|
||||
*
|
||||
* @return the root token.
|
||||
*/
|
||||
public VaultToken initializeVault() {
|
||||
|
||||
int createKeys = 2;
|
||||
int requiredKeys = 2;
|
||||
|
||||
VaultInitializationResponse initialized = vaultOperations.opsForSys()
|
||||
.initialize(new VaultInitializationRequest(createKeys, requiredKeys));
|
||||
|
||||
for (int i = 0; i < requiredKeys; i++) {
|
||||
|
||||
VaultUnsealStatus unsealStatus = vaultOperations.opsForSys()
|
||||
.unseal(initialized.getKeys().get(i));
|
||||
|
||||
if (!unsealStatus.isSealed()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return VaultToken.of(initialized.getRootToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a token for the given {@code tokenId} and {@code policy}.
|
||||
*
|
||||
* @param tokenId
|
||||
* @param policy
|
||||
* @return
|
||||
*/
|
||||
public VaultToken createToken(String tokenId, String policy) {
|
||||
|
||||
VaultTokenRequest tokenRequest = new VaultTokenRequest();
|
||||
|
||||
tokenRequest.setId(tokenId);
|
||||
if (policy != null) {
|
||||
tokenRequest.setPolicies(Collections.singletonList(policy));
|
||||
}
|
||||
|
||||
VaultTokenResponse vaultTokenResponse = vaultOperations.opsForToken()
|
||||
.create(tokenRequest);
|
||||
return vaultTokenResponse.getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether Vault is available (vault created and unsealed).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
return adminOperations.isInitialized() && !adminOperations.health().isSealed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount an auth backend.
|
||||
*
|
||||
* @param authBackend
|
||||
*/
|
||||
public void mountAuth(String authBackend) {
|
||||
|
||||
Assert.hasText(authBackend, "AuthBackend must not be empty");
|
||||
|
||||
adminOperations.authMount(authBackend, VaultMount.create(authBackend));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a auth-backend is enabled.
|
||||
*
|
||||
* @param authBackend
|
||||
* @return
|
||||
*/
|
||||
public boolean hasAuth(String authBackend) {
|
||||
|
||||
Assert.hasText(authBackend, "AuthBackend must not be empty");
|
||||
|
||||
return adminOperations.getAuthMounts().containsKey(authBackend + "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount an secret backend.
|
||||
*
|
||||
* @param secretBackend
|
||||
*/
|
||||
public void mountSecret(String secretBackend) {
|
||||
|
||||
Assert.hasText(secretBackend, "SecretBackend must not be empty");
|
||||
|
||||
adminOperations.mount(secretBackend, VaultMount.create(secretBackend));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a auth-backend is enabled.
|
||||
*
|
||||
* @param secretBackend
|
||||
* @return
|
||||
*/
|
||||
public boolean hasSecretBackend(String secretBackend) {
|
||||
|
||||
Assert.hasText(secretBackend, "SecretBackend must not be empty");
|
||||
Map<String, VaultMount> mounts = adminOperations.getMounts();
|
||||
return mounts.containsKey(secretBackend)
|
||||
|| mounts.containsKey(secretBackend + "/");
|
||||
}
|
||||
|
||||
public VaultOperations getVaultOperations() {
|
||||
return vaultOperations;
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
import org.springframework.cloud.vault.config.VaultProperties;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
|
||||
/**
|
||||
* Utility to retrieve settings during test.
|
||||
@@ -37,13 +39,24 @@ public class Settings {
|
||||
|
||||
VaultProperties vaultProperties = new VaultProperties();
|
||||
vaultProperties.getSsl().setTrustStorePassword("changeit");
|
||||
vaultProperties.getSsl().setTrustStore(
|
||||
new FileSystemResource(new File(workDir, "keystore.jks")));
|
||||
vaultProperties.getSsl()
|
||||
.setTrustStore(new FileSystemResource(new File(workDir, "keystore.jks")));
|
||||
vaultProperties.setToken(token().getToken());
|
||||
|
||||
return vaultProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the vault properties.
|
||||
*/
|
||||
public static SslConfiguration createSslConfiguration() {
|
||||
|
||||
File workDir = findWorkDir();
|
||||
|
||||
return SslConfiguration.forTrustStore(
|
||||
new FileSystemResource(new File(workDir, "keystore.jks")), "changeit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the {@code work} directory, starting at the {@code user.dir} directory. Search
|
||||
* is performed by walking the parent directories.
|
||||
@@ -55,8 +68,8 @@ public class Settings {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the {@code work} directory, starting at the given {@code directory}. Search
|
||||
* is performed by walking the parent directories.
|
||||
* Find the {@code work} directory, starting at the given {@code directory}. Search is
|
||||
* performed by walking the parent directories.
|
||||
* @return the {@link File} pointing to the {@code work} directory
|
||||
* @throws IllegalStateException If the {@code work} directory cannot be found.
|
||||
*/
|
||||
@@ -13,79 +13,84 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
package org.springframework.cloud.vault.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Factory for {@link TestRestTemplate}. The template caches the
|
||||
* Factory for {@link RestTemplate}. The template caches the
|
||||
* {@link ClientHttpRequestFactory} once it was initialized. Changes to timeouts or the
|
||||
* SSL configuration won't be applied once a {@link ClientHttpRequestFactory} was created
|
||||
* for the first time.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class TestRestTemplateFactory {
|
||||
|
||||
private final static AtomicReference<ClientHttpRequestFactory> factoryCache = new AtomicReference<>();
|
||||
private static final AtomicReference<ClientHttpRequestFactory> factoryCache = new AtomicReference<ClientHttpRequestFactory>();
|
||||
|
||||
/**
|
||||
* Create a new {@link TestRestTemplate} using the {@link VaultProperties}. The
|
||||
* Create a new {@link RestTemplate} using the {@link SslConfiguration}. The
|
||||
* underlying {@link ClientHttpRequestFactory} is cached. See
|
||||
* {@link #create(ClientHttpRequestFactory)} to create {@link TestRestTemplate} for a
|
||||
* {@link #create(ClientHttpRequestFactory)} to create {@link RestTemplate} for a
|
||||
* given {@link ClientHttpRequestFactory}.
|
||||
*
|
||||
* @param vaultProperties must not be {@literal null}.
|
||||
* @param sslConfiguration must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static TestRestTemplate create(VaultProperties vaultProperties) {
|
||||
public static RestTemplate create(SslConfiguration sslConfiguration) {
|
||||
|
||||
Assert.notNull(vaultProperties, "VaultProperties must not be null!");
|
||||
Assert.notNull(sslConfiguration, "SslConfiguration must not be null!");
|
||||
|
||||
initializeClientHttpRequestFactory(vaultProperties);
|
||||
return create(factoryCache.get());
|
||||
try {
|
||||
initializeClientHttpRequestFactory(sslConfiguration);
|
||||
return create(factoryCache.get());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TestRestTemplate} using the {@link ClientHttpRequestFactory}.
|
||||
* The {@link TestRestTemplate} will throw
|
||||
* Create a new {@link RestTemplate} using the {@link ClientHttpRequestFactory}. The
|
||||
* {@link RestTemplate} will throw
|
||||
* {@link org.springframework.web.client.HttpStatusCodeException exceptions} in error
|
||||
* cases and behave in that aspect like the regular
|
||||
* {@link org.springframework.web.client.RestTemplate}.
|
||||
* cases and behave in that aspect like the regular {@link RestTemplate}.
|
||||
*
|
||||
* @param requestFactory must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static TestRestTemplate create(ClientHttpRequestFactory requestFactory) {
|
||||
public static RestTemplate create(ClientHttpRequestFactory requestFactory) {
|
||||
|
||||
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null!");
|
||||
|
||||
TestRestTemplate testRestTemplate = new TestRestTemplate();
|
||||
testRestTemplate.setErrorHandler(new DefaultResponseErrorHandler());
|
||||
testRestTemplate.setRequestFactory(requestFactory);
|
||||
RestTemplate RestTemplate = new RestTemplate();
|
||||
RestTemplate.setErrorHandler(new DefaultResponseErrorHandler());
|
||||
RestTemplate.setRequestFactory(requestFactory);
|
||||
|
||||
return testRestTemplate;
|
||||
return RestTemplate;
|
||||
}
|
||||
|
||||
private static void initializeClientHttpRequestFactory(VaultProperties vaultProperties)
|
||||
throws Exception {
|
||||
private static void initializeClientHttpRequestFactory(
|
||||
SslConfiguration sslConfiguration) throws Exception {
|
||||
|
||||
if (factoryCache.get() != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryFactory
|
||||
.create(vaultProperties);
|
||||
.create(new ClientOptions(), sslConfiguration);
|
||||
|
||||
if (factoryCache.compareAndSet(null, clientHttpRequestFactory)) {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.client.VaultClient;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.core.DefaultVaultClientFactory;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
|
||||
/**
|
||||
* Vault rule to ensure a running and prepared Vault.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class VaultRule extends ExternalResource {
|
||||
|
||||
private final VaultEndpoint vaultEndpoint;
|
||||
private final PrepareVault prepareVault;
|
||||
|
||||
private VaultToken token;
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultRule} with default SSL configuration and endpoint.
|
||||
*
|
||||
* @see Settings#createSslConfiguration()
|
||||
* @see VaultEndpoint
|
||||
*/
|
||||
public VaultRule() {
|
||||
this(Settings.createSslConfiguration(), new VaultEndpoint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultRule} with the given {@link SslConfiguration} and
|
||||
* {@link VaultEndpoint}.
|
||||
*
|
||||
* @param sslConfiguration must not be {@literal null}.
|
||||
* @param vaultEndpoint must not be {@literal null}.
|
||||
*/
|
||||
public VaultRule(SslConfiguration sslConfiguration, VaultEndpoint vaultEndpoint) {
|
||||
|
||||
Assert.notNull(sslConfiguration, "SslConfiguration must not be null");
|
||||
Assert.notNull(vaultEndpoint, "VaultEndpoint must not be null");
|
||||
|
||||
VaultClient vaultClient = new VaultClient(
|
||||
TestRestTemplateFactory.create(sslConfiguration), vaultEndpoint);
|
||||
DefaultVaultClientFactory clientFactory = new DefaultVaultClientFactory(
|
||||
vaultClient);
|
||||
|
||||
VaultTemplate vaultTemplate = new VaultTemplate(clientFactory,
|
||||
new PreparingSessionManager());
|
||||
|
||||
this.token = Settings.token();
|
||||
this.prepareVault = new PrepareVault(vaultTemplate);
|
||||
this.vaultEndpoint = vaultEndpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before() {
|
||||
|
||||
Socket socket = null;
|
||||
try {
|
||||
|
||||
socket = new Socket();
|
||||
|
||||
socket.connect(new InetSocketAddress(InetAddress.getByName("localhost"),
|
||||
vaultEndpoint.getPort()));
|
||||
socket.close();
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Vault is not running on localhost:%d which is required to run a test using @Rule %s",
|
||||
vaultEndpoint.getPort(), getClass().getSimpleName()));
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.prepareVault.isAvailable()) {
|
||||
this.token = prepareVault.initializeVault();
|
||||
this.prepareVault.createToken(Settings.token().getToken(), "root");
|
||||
this.token = Settings.token();
|
||||
}
|
||||
}
|
||||
|
||||
public PrepareVault prepare() {
|
||||
return prepareVault;
|
||||
}
|
||||
|
||||
private class PreparingSessionManager implements SessionManager {
|
||||
|
||||
@Override
|
||||
public VaultToken getSessionToken() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-parent</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<name>Spring Cloud Vault Core</name>
|
||||
<description>Spring Cloud Vault Core</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test-jar</id>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,32 +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;
|
||||
|
||||
/**
|
||||
* Interface to obtain a UserId for AppId authentication.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface AppIdUserIdMechanism {
|
||||
|
||||
/**
|
||||
* Creates a UserId for AppId authentication.
|
||||
*
|
||||
* @return the UserId.
|
||||
*/
|
||||
String createUserId();
|
||||
}
|
||||
@@ -1,78 +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;
|
||||
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public abstract class ClientAuthentication {
|
||||
|
||||
/**
|
||||
* Perform a login to Vault and return a {@link VaultToken}.
|
||||
*
|
||||
* @return a {@link VaultToken}.
|
||||
*/
|
||||
public abstract VaultToken login();
|
||||
|
||||
/**
|
||||
* Creates a Token-based authentication adapter.
|
||||
*
|
||||
* @param vaultProperties must not be {@literal null}.
|
||||
* @return the {@link ClientAuthentication} adapter.
|
||||
*/
|
||||
public static ClientAuthentication token(VaultProperties vaultProperties) {
|
||||
return new TokenClientAuthentication(vaultProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a generic authentication adapter.
|
||||
*
|
||||
* @param vaultProperties must not be {@literal null}.
|
||||
* @param vaultClient must not be {@literal null}.
|
||||
* @return the {@link ClientAuthentication} adapter.
|
||||
*/
|
||||
public static ClientAuthentication create(VaultProperties vaultProperties,
|
||||
VaultClient vaultClient) {
|
||||
return new DefaultClientAuthentication(vaultProperties, vaultClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AppId-based authentication adapter.
|
||||
*
|
||||
* @param vaultProperties must not be {@literal null}.
|
||||
* @return the {@link ClientAuthentication} adapter.
|
||||
*/
|
||||
public static ClientAuthentication appId(VaultProperties vaultProperties,
|
||||
VaultClient vaultClient) {
|
||||
return new DefaultClientAuthentication(vaultProperties, vaultClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AppId-based authentication adapter.
|
||||
*
|
||||
* @param vaultProperties must not be {@literal null}.
|
||||
* @param vaultClient must not be {@literal null}.
|
||||
* @param userIdMechanism must not be {@literal null}.
|
||||
* @return the {@link ClientAuthentication} adapter.
|
||||
*/
|
||||
public static ClientAuthentication appId(VaultProperties vaultProperties,
|
||||
VaultClient vaultClient, AppIdUserIdMechanism userIdMechanism) {
|
||||
return new DefaultClientAuthentication(vaultProperties, vaultClient,
|
||||
userIdMechanism);
|
||||
}
|
||||
}
|
||||
@@ -1,284 +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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.springframework.cloud.vault.VaultProperties.Ssl;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.OkHttpClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
|
||||
/**
|
||||
* Factory for {@link ClientHttpRequestFactory} that supports Apache HTTP Components,
|
||||
* OkHttp, Netty and the JDK HTTP client (in that order). This factory configures a
|
||||
* {@link ClientHttpRequestFactory} depending on the available dependencies.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CommonsLog
|
||||
class ClientHttpRequestFactoryFactory {
|
||||
|
||||
private final static boolean HTTP_COMPONENTS_PRESENT = ClassUtils.isPresent(
|
||||
"org.apache.http.client.HttpClient",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
private final static boolean OKHTTP_PRESENT = ClassUtils.isPresent(
|
||||
"com.squareup.okhttp.OkHttpClient",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
private final static boolean NETTY_PRESENT = ClassUtils.isPresent(
|
||||
"io.netty.channel.nio.NioEventLoopGroup",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Creates a {@link ClientHttpRequestFactory} for the given {@link VaultProperties}.
|
||||
*
|
||||
* @param vaultProperties must not be {@literal null}
|
||||
* @return a new {@link ClientHttpRequestFactory}. Lifecycle beans must be initialized
|
||||
* after obtaining.
|
||||
*/
|
||||
public static ClientHttpRequestFactory create(VaultProperties vaultProperties) {
|
||||
|
||||
try {
|
||||
|
||||
if (HTTP_COMPONENTS_PRESENT) {
|
||||
return HttpComponents.usingHttpComponents(vaultProperties);
|
||||
}
|
||||
|
||||
if (OKHTTP_PRESENT) {
|
||||
return OkHttp.usingOkHttp(vaultProperties);
|
||||
}
|
||||
|
||||
if (NETTY_PRESENT) {
|
||||
return Netty.usingNetty(vaultProperties);
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException | GeneralSecurityException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
log.warn("VaultProperties has SSL configured but the SSL configuration "
|
||||
+ "must be applied outside the Vault Client to use the JDK HTTP client");
|
||||
}
|
||||
|
||||
return new SimpleClientHttpRequestFactory();
|
||||
}
|
||||
|
||||
private static SSLContext getSSLContext(VaultProperties.Ssl ssl)
|
||||
throws GeneralSecurityException, IOException {
|
||||
|
||||
KeyManager[] keyManagers = ssl.getKeyStore() != null ? createKeyManagerFactory(
|
||||
ssl.getKeyStore(), ssl.getKeyStorePassword()).getKeyManagers() : null;
|
||||
|
||||
TrustManager[] trustManagers = ssl.getTrustStore() != null ? createTrustManagerFactory(
|
||||
ssl.getTrustStore(), ssl.getTrustStorePassword()).getTrustManagers()
|
||||
: null;
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(keyManagers, trustManagers, null);
|
||||
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
private static KeyManagerFactory createKeyManagerFactory(Resource keystoreFile,
|
||||
String storePassword) throws GeneralSecurityException, IOException {
|
||||
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
|
||||
try (InputStream inputStream = keystoreFile.getInputStream()) {
|
||||
keyStore.load(inputStream,
|
||||
StringUtils.hasText(storePassword) ? storePassword.toCharArray()
|
||||
: null);
|
||||
}
|
||||
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(keyStore,
|
||||
StringUtils.hasText(storePassword) ? storePassword.toCharArray()
|
||||
: new char[0]);
|
||||
|
||||
return keyManagerFactory;
|
||||
}
|
||||
|
||||
private static TrustManagerFactory createTrustManagerFactory(Resource trustFile,
|
||||
String storePassword) throws GeneralSecurityException, IOException {
|
||||
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
|
||||
try (InputStream inputStream = trustFile.getInputStream()) {
|
||||
trustStore.load(inputStream,
|
||||
StringUtils.hasText(storePassword) ? storePassword.toCharArray()
|
||||
: null);
|
||||
}
|
||||
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(trustStore);
|
||||
|
||||
return trustManagerFactory;
|
||||
}
|
||||
|
||||
private static boolean hasSslConfiguration(VaultProperties vaultProperties) {
|
||||
|
||||
Ssl ssl = vaultProperties.getSsl();
|
||||
|
||||
if (ssl == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ssl.getTrustStore() != null || ssl.getKeyStore() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequestFactory} for Apache Http Components.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class HttpComponents {
|
||||
|
||||
static ClientHttpRequestFactory usingHttpComponents(
|
||||
VaultProperties vaultProperties) throws GeneralSecurityException,
|
||||
IOException {
|
||||
|
||||
HttpClientBuilder httpClientBuilder = HttpClients.custom();
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
|
||||
SSLContext sslContext = getSSLContext(vaultProperties.getSsl());
|
||||
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
|
||||
sslContext);
|
||||
httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
|
||||
httpClientBuilder.setSSLContext(sslContext);
|
||||
}
|
||||
|
||||
RequestConfig requestConfig = RequestConfig.custom() //
|
||||
.setConnectTimeout(vaultProperties.getConnectionTimeout()) //
|
||||
.setSocketTimeout(vaultProperties.getReadTimeout()) //
|
||||
.setAuthenticationEnabled(true) //
|
||||
.build();
|
||||
|
||||
httpClientBuilder.setDefaultRequestConfig(requestConfig);
|
||||
|
||||
return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequestFactory} for the {@link OkHttpClient}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class OkHttp {
|
||||
|
||||
static ClientHttpRequestFactory usingOkHttp(VaultProperties vaultProperties)
|
||||
throws GeneralSecurityException, IOException {
|
||||
|
||||
final OkHttpClient okHttpClient = new OkHttpClient();
|
||||
|
||||
OkHttpClientHttpRequestFactory requestFactory = new OkHttpClientHttpRequestFactory(
|
||||
okHttpClient) {
|
||||
|
||||
@Override
|
||||
public void destroy() throws IOException {
|
||||
|
||||
if (okHttpClient.getCache() != null) {
|
||||
okHttpClient.getCache().close();
|
||||
}
|
||||
|
||||
okHttpClient.getDispatcher().getExecutorService().shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
okHttpClient.setSslSocketFactory(getSSLContext(vaultProperties.getSsl())
|
||||
.getSocketFactory());
|
||||
}
|
||||
|
||||
requestFactory.setConnectTimeout(vaultProperties.getConnectionTimeout());
|
||||
requestFactory.setReadTimeout(vaultProperties.getReadTimeout());
|
||||
|
||||
return requestFactory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequestFactory} for Netty.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class Netty {
|
||||
|
||||
static ClientHttpRequestFactory usingNetty(VaultProperties vaultProperties)
|
||||
throws GeneralSecurityException, IOException {
|
||||
|
||||
VaultProperties.Ssl ssl = vaultProperties.getSsl();
|
||||
|
||||
final Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
|
||||
SslContextBuilder sslContextBuilder = SslContextBuilder //
|
||||
.forClient();
|
||||
|
||||
if (ssl.getTrustStore() != null) {
|
||||
sslContextBuilder.trustManager(createTrustManagerFactory(
|
||||
ssl.getTrustStore(), ssl.getTrustStorePassword()));
|
||||
}
|
||||
|
||||
if (ssl.getKeyStore() != null) {
|
||||
sslContextBuilder.keyManager(createKeyManagerFactory(
|
||||
ssl.getKeyStore(), ssl.getKeyStorePassword()));
|
||||
}
|
||||
|
||||
requestFactory.setSslContext(sslContextBuilder.sslProvider(
|
||||
SslProvider.JDK).build());
|
||||
}
|
||||
|
||||
requestFactory.setConnectTimeout(vaultProperties.getConnectionTimeout());
|
||||
requestFactory.setReadTimeout(vaultProperties.getReadTimeout());
|
||||
|
||||
return requestFactory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,234 +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;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.cloud.vault.VaultProperties.AuthenticationMethod;
|
||||
import org.springframework.cloud.vault.VaultProperties.Ssl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ClientAuthentication}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CommonsLog
|
||||
class DefaultClientAuthentication extends ClientAuthentication {
|
||||
|
||||
private final VaultProperties properties;
|
||||
private final VaultClient vaultClient;
|
||||
private final AppIdUserIdMechanism appIdUserIdMechanism;
|
||||
private char[] nonce;
|
||||
|
||||
/**
|
||||
* Creates a {@link DefaultClientAuthentication} using {@link VaultProperties} and
|
||||
* {@link RestTemplate}.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param vaultClient must not be {@literal null}.
|
||||
*/
|
||||
DefaultClientAuthentication(VaultProperties properties, VaultClient vaultClient) {
|
||||
|
||||
Assert.notNull(properties, "VaultProperties must not be null");
|
||||
Assert.notNull(vaultClient, "RestTemplate must not be null");
|
||||
|
||||
this.properties = properties;
|
||||
this.vaultClient = vaultClient;
|
||||
this.appIdUserIdMechanism = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DefaultClientAuthentication} using {@link VaultProperties} and
|
||||
* {@link RestTemplate} for AppId authentication.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param vaultClient must not be {@literal null}.
|
||||
* @param appIdUserIdMechanism must not be {@literal null}.
|
||||
*/
|
||||
DefaultClientAuthentication(VaultProperties properties, VaultClient vaultClient,
|
||||
AppIdUserIdMechanism appIdUserIdMechanism) {
|
||||
|
||||
Assert.notNull(properties, "VaultProperties must not be null");
|
||||
Assert.notNull(vaultClient, "VaultClient must not be null");
|
||||
Assert.notNull(appIdUserIdMechanism, "AppIdUserIdMechanism must not be null");
|
||||
|
||||
this.properties = properties;
|
||||
this.vaultClient = vaultClient;
|
||||
this.appIdUserIdMechanism = appIdUserIdMechanism;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultToken login() {
|
||||
|
||||
if (properties.getAuthentication() == VaultProperties.AuthenticationMethod.APPID
|
||||
&& appIdUserIdMechanism != null) {
|
||||
log.info("Using AppId authentication to log into Vault");
|
||||
|
||||
VaultProperties.AppIdProperties appId = properties.getAppId();
|
||||
return createTokenUsingAppId(new AppIdTuple(properties.getApplicationName(),
|
||||
appIdUserIdMechanism.createUserId()), appId);
|
||||
}
|
||||
|
||||
if (properties.getAuthentication() == AuthenticationMethod.CERT
|
||||
&& properties.getSsl() != null) {
|
||||
log.info("Using TLS Certificate authentication to log into Vault");
|
||||
|
||||
return createTokenUsingTlsCertAuthentication(properties.getSsl());
|
||||
}
|
||||
|
||||
if (properties.getAuthentication() == VaultProperties.AuthenticationMethod.AWS_EC2) {
|
||||
log.info("Using AWS-EC2 authentication to log into Vault");
|
||||
|
||||
return createTokenUsingAwsEc2();
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"Cannot create a token for auth method %s",
|
||||
properties.getAuthentication()));
|
||||
}
|
||||
|
||||
private VaultToken createTokenUsingAppId(AppIdTuple appIdTuple,
|
||||
VaultProperties.AppIdProperties appId) {
|
||||
|
||||
URI uri = VaultClient.buildUri(properties,
|
||||
String.format("auth/%s/login", appId.getAppIdPath()));
|
||||
|
||||
Map<String, String> login = getAppIdLogin(appIdTuple);
|
||||
|
||||
VaultClientResponse response = vaultClient.write(uri, login);
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Cannot login using app-id: %s", response.getMessage()));
|
||||
}
|
||||
|
||||
VaultResponse body = response.getBody();
|
||||
String token = (String) body.getAuth().get("client_token");
|
||||
|
||||
log.debug("Login successful using AppId authentication");
|
||||
|
||||
return VaultToken.of(token, body.getLeaseDuration());
|
||||
}
|
||||
|
||||
private VaultToken createTokenUsingTlsCertAuthentication(Ssl ssl) {
|
||||
|
||||
URI uri = VaultClient.buildUri(properties,
|
||||
String.format("auth/%s/login", ssl.getCertAuthPath()));
|
||||
|
||||
VaultClientResponse response = vaultClient.write(uri, Collections.emptyMap());
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Cannot login using TLS certificates: %s", response.getMessage()));
|
||||
}
|
||||
|
||||
VaultResponse body = response.getBody();
|
||||
String token = (String) body.getAuth().get("client_token");
|
||||
|
||||
log.debug("Login successful using TLS certificates");
|
||||
|
||||
return VaultToken.of(token, body.getLeaseDuration());
|
||||
}
|
||||
|
||||
private Map<String, String> getAppIdLogin(AppIdTuple appIdTuple) {
|
||||
|
||||
Map<String, String> login = new HashMap<>();
|
||||
login.put("app_id", appIdTuple.getAppId());
|
||||
login.put("user_id", appIdTuple.getUserId());
|
||||
return login;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private VaultToken createTokenUsingAwsEc2() {
|
||||
|
||||
VaultProperties.AwsEc2Properties awsEc2 = this.properties.getAwsEc2();
|
||||
|
||||
URI uri = VaultClient.buildUri(this.properties,
|
||||
String.format("auth/%s/login", awsEc2.getAwsEc2Path()));
|
||||
|
||||
Map<String, String> login = getEc2Login(awsEc2);
|
||||
|
||||
VaultClientResponse response = vaultClient.write(uri, login);
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Cannot login using AWS-EC2: %s", response.getMessage()));
|
||||
}
|
||||
|
||||
VaultResponse body = response.getBody();
|
||||
String token = (String) body.getAuth().get("client_token");
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
if (body.getAuth().get("metadata") instanceof Map) {
|
||||
Map<Object, Object> metadata = (Map<Object, Object>) body.getAuth().get(
|
||||
"metadata");
|
||||
log.debug(String
|
||||
.format("Login successful using AWS-EC2 authentication for instance %s, AMI %s",
|
||||
metadata.get("instance_id"), metadata.get("instance_id")));
|
||||
}
|
||||
else {
|
||||
log.debug("Login successful using AWS-EC2 authentication");
|
||||
}
|
||||
}
|
||||
|
||||
return VaultToken.of(token, body.getLeaseDuration());
|
||||
}
|
||||
|
||||
private Map<String, String> getEc2Login(VaultProperties.AwsEc2Properties properties) {
|
||||
|
||||
Map<String, String> login = new HashMap<>();
|
||||
|
||||
if (StringUtils.hasText(properties.getRole())) {
|
||||
login.put("role", properties.getRole());
|
||||
}
|
||||
|
||||
if (properties.isUseNonce()) {
|
||||
if (this.nonce == null) {
|
||||
this.nonce = createNonce();
|
||||
}
|
||||
|
||||
login.put("nonce", new String(this.nonce));
|
||||
}
|
||||
|
||||
String pkcs7 = vaultClient.getRestTemplate().getForObject(
|
||||
properties.getIdentityDocument(), String.class);
|
||||
if (StringUtils.hasText(pkcs7)) {
|
||||
login.put("pkcs7", pkcs7.replaceAll("\\r", "").replace("\\n", ""));
|
||||
}
|
||||
return login;
|
||||
}
|
||||
|
||||
private char[] createNonce() {
|
||||
return UUID.randomUUID().toString().toCharArray();
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class AppIdTuple {
|
||||
private String appId;
|
||||
private String userId;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* Mechanism to generate a SHA-256 hashed and hex-encoded representation of the IP address. Can be calculated with
|
||||
* {@code echo -n 192.168.99.1 | sha256sum}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class IpAddressUserId implements AppIdUserIdMechanism {
|
||||
|
||||
@Override
|
||||
public String createUserId() {
|
||||
try {
|
||||
return Sha256.toSha256(InetAddress.getLocalHost().getHostAddress());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* Mechanism to generate a UserId based on the Mac address. {@link MacAddressUserId} creates a hex-encoded
|
||||
* representation of the Mac address without any separators (0123456789AB). A
|
||||
* {@link VaultProperties.AppIdProperties#networkInterface} can be
|
||||
* specified optionally to select a network interface (index/name).
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
@CommonsLog
|
||||
public class MacAddressUserId implements AppIdUserIdMechanism {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
@Override
|
||||
public String createUserId() {
|
||||
try {
|
||||
|
||||
NetworkInterface networkInterface = null;
|
||||
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||
|
||||
VaultProperties.AppIdProperties appId = vaultProperties.getAppId();
|
||||
if (StringUtils.hasText(appId.getNetworkInterface())) {
|
||||
try {
|
||||
networkInterface = getNetworkInterface(Integer.parseInt(appId.getNetworkInterface()), interfaces);
|
||||
} catch (NumberFormatException e) {
|
||||
networkInterface = getNetworkInterface((appId.getNetworkInterface()), interfaces);
|
||||
}
|
||||
}
|
||||
|
||||
if (networkInterface == null) {
|
||||
if (StringUtils.hasText(appId.getNetworkInterface())) {
|
||||
log.warn(
|
||||
String.format("Did not find a NetworkInterface applying hint %s", appId.getNetworkInterface()));
|
||||
}
|
||||
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
networkInterface = NetworkInterface.getByInetAddress(localHost);
|
||||
|
||||
if (networkInterface == null) {
|
||||
throw new IllegalStateException(String.format("Cannot determine NetworkInterface for %s", localHost));
|
||||
}
|
||||
}
|
||||
|
||||
byte[] mac = networkInterface.getHardwareAddress();
|
||||
if (mac == null) {
|
||||
throw new IllegalStateException(String.format("Network interface %s has no hardware address", networkInterface.getName()));
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
sb.append(String.format("%02X", mac[i]));
|
||||
}
|
||||
return Sha256.toSha256(sb.toString());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private NetworkInterface getNetworkInterface(Number hint, List<NetworkInterface> interfaces) {
|
||||
|
||||
if (interfaces.size() > hint.intValue() && hint.intValue() >= 0) {
|
||||
return interfaces.get(hint.intValue());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private NetworkInterface getNetworkInterface(String hint, List<NetworkInterface> interfaces) {
|
||||
|
||||
for (NetworkInterface anInterface : interfaces) {
|
||||
if (hint.equals(anInterface.getDisplayName()) || hint.equals(anInterface.getName())) {
|
||||
return anInterface;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +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;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility to generate a SHA 256 checksum.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class Sha256 {
|
||||
|
||||
/**
|
||||
* Generates a hex-encoded SHA256 checksum from the supplied {@code content}.
|
||||
*
|
||||
* @param content must not be {@literal null} and not empty.
|
||||
* @return hex-encoded SHA256 checksum
|
||||
*/
|
||||
public static String toSha256(String content) {
|
||||
|
||||
Assert.hasText(content, "Content must not be empty");
|
||||
|
||||
MessageDigest messageDigest = getMessageDigest("SHA-256");
|
||||
byte[] digest = messageDigest.digest(content.getBytes(StandardCharsets.US_ASCII));
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MessageDigest instance for the given algorithm. Throws an
|
||||
* IllegalArgumentException if <i>algorithm</i> is unknown
|
||||
*
|
||||
* @return MessageDigest instance
|
||||
* @throws IllegalArgumentException if NoSuchAlgorithmException is thrown
|
||||
*/
|
||||
private static MessageDigest getMessageDigest(String algorithm)
|
||||
throws IllegalArgumentException {
|
||||
try {
|
||||
return MessageDigest.getInstance(algorithm);
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException("No such algorithm [" + algorithm + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +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;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* A static UserId.
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
@CommonsLog
|
||||
public class StaticUserId implements AppIdUserIdMechanism {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
@Override
|
||||
public String createUserId() {
|
||||
return vaultProperties.getAppId().getUserId();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +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;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Static Token-based client authentication method.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class TokenClientAuthentication extends ClientAuthentication {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
TokenClientAuthentication(VaultProperties vaultProperties) {
|
||||
|
||||
Assert.notNull(vaultProperties);
|
||||
Assert.isTrue(
|
||||
vaultProperties
|
||||
.getAuthentication() == VaultProperties.AuthenticationMethod.TOKEN,
|
||||
String.format("Authentication must be Token but is %s",
|
||||
vaultProperties.getAuthentication()));
|
||||
Assert.hasText(vaultProperties.getToken(), "Token must not be empty");
|
||||
|
||||
this.vaultProperties = vaultProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultToken login() {
|
||||
return VaultToken.of(vaultProperties.getToken());
|
||||
}
|
||||
}
|
||||
@@ -1,121 +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;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
public class VaultBootstrapConfiguration {
|
||||
|
||||
@Bean
|
||||
public ClientFactoryWrapper clientHttpRequestFactoryWrapper() {
|
||||
return new ClientFactoryWrapper(
|
||||
ClientHttpRequestFactoryFactory.create(vaultProperties()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VaultClient vaultClient(ApplicationContext applicationContext) {
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(
|
||||
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory());
|
||||
|
||||
VaultClient vaultClient = new VaultClient();
|
||||
vaultClient.setRestTemplate(restTemplate);
|
||||
|
||||
return vaultClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VaultProperties vaultProperties() {
|
||||
return new VaultProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = "spring.cloud.vault", name = "authentication", havingValue = "APPID")
|
||||
public AppIdUserIdMechanism appIdUserIdMechanism(VaultProperties vaultProperties) {
|
||||
|
||||
String userId = vaultProperties.getAppId().getUserId();
|
||||
Assert.hasText(userId,
|
||||
"UserId (spring.cloud.vault.app-id.user-id) must not be empty.");
|
||||
|
||||
try {
|
||||
Class<?> userIdClass = ClassUtils.forName(userId, null);
|
||||
return (AppIdUserIdMechanism) BeanUtils.instantiateClass(userIdClass);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
|
||||
switch (userId.toUpperCase()) {
|
||||
case VaultProperties.AppIdProperties.IP_ADDRESS:
|
||||
return new IpAddressUserId();
|
||||
case VaultProperties.AppIdProperties.MAC_ADDRESS:
|
||||
return new MacAddressUserId(vaultProperties);
|
||||
default:
|
||||
return new StaticUserId(vaultProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link ClientHttpRequestFactory} to not expose the bean globally.
|
||||
*/
|
||||
public static class ClientFactoryWrapper implements InitializingBean, DisposableBean {
|
||||
|
||||
private final ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
|
||||
public ClientFactoryWrapper(ClientHttpRequestFactory clientHttpRequestFactory) {
|
||||
this.clientHttpRequestFactory = clientHttpRequestFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
if (clientHttpRequestFactory instanceof DisposableBean) {
|
||||
((DisposableBean) clientHttpRequestFactory).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
if (clientHttpRequestFactory instanceof InitializingBean) {
|
||||
((InitializingBean) clientHttpRequestFactory).afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
|
||||
public ClientHttpRequestFactory getClientHttpRequestFactory() {
|
||||
return clientHttpRequestFactory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,208 +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;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Vault client. This client reads data from Vault.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class VaultClient {
|
||||
|
||||
public static final String API_VERSION = "v1";
|
||||
public static final String VAULT_TOKEN = "X-Vault-Token";
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
public VaultClient() {
|
||||
this(new RestTemplate());
|
||||
}
|
||||
|
||||
public VaultClient(RestTemplate restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from the given Vault {@code uri} using the {@link VaultToken}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @param vaultToken must not be {@literal null}.
|
||||
* @return A {@link Map} containing properties.
|
||||
*/
|
||||
public VaultClientResponse read(URI uri, VaultToken vaultToken) {
|
||||
|
||||
Assert.notNull(uri, "URI must not be empty!");
|
||||
Assert.notNull(vaultToken, "Vault Token must not be null!");
|
||||
|
||||
return exchange(uri, HttpMethod.GET, new HttpEntity<>(createHeaders(vaultToken)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the given Vault {@code uri} using the {@link VaultToken}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return A {@link Map} containing properties.
|
||||
*/
|
||||
public VaultClientResponse write(URI uri, Object entity) {
|
||||
|
||||
Assert.notNull(uri, "URI must not be empty!");
|
||||
Assert.notNull(entity, "Entity must not be null!");
|
||||
|
||||
return exchange(uri, HttpMethod.POST, new HttpEntity<>(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the given Vault {@code uri} using the {@link VaultToken}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param vaultToken must not be {@literal null}.
|
||||
* @return A {@link Map} containing properties.
|
||||
*/
|
||||
public VaultClientResponse write(URI uri, Object entity, VaultToken vaultToken) {
|
||||
|
||||
Assert.notNull(uri, "URI must not be empty!");
|
||||
Assert.notNull(entity, "Vault Token must not be null!");
|
||||
Assert.notNull(vaultToken, "Vault Token must not be null!");
|
||||
|
||||
return exchange(uri, HttpMethod.POST, new HttpEntity<>(entity,
|
||||
createHeaders(vaultToken)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the current Vault service for it's health status
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @return A {@link VaultHealthResponse} containing the current service status.
|
||||
*/
|
||||
public VaultHealthResponse health(URI uri) {
|
||||
try {
|
||||
ResponseEntity<VaultHealthResponse> healthResponse = this.restTemplate.exchange(
|
||||
uri, HttpMethod.GET, null,
|
||||
VaultHealthResponse.class);
|
||||
return healthResponse.getBody();
|
||||
} catch (HttpStatusCodeException responseError) {
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthResponse.class);
|
||||
}
|
||||
catch (Exception jsonError) {
|
||||
throw responseError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VaultClientResponse exchange(URI uri, HttpMethod httpMethod,
|
||||
HttpEntity<?> httpEntity) {
|
||||
|
||||
Assert.notNull(uri, "URI must not be empty!");
|
||||
|
||||
try {
|
||||
ResponseEntity<VaultResponse> response = this.restTemplate.exchange(uri,
|
||||
httpMethod, httpEntity, VaultResponse.class);
|
||||
|
||||
return VaultClientResponse.of(response.getBody(), response.getStatusCode(),
|
||||
uri, response.getStatusCode().getReasonPhrase());
|
||||
}
|
||||
catch (HttpServerErrorException | HttpClientErrorException e) {
|
||||
|
||||
String message = e.getResponseBodyAsString();
|
||||
|
||||
if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders()
|
||||
.getContentType())) {
|
||||
message = VaultErrorMessage.getError(message);
|
||||
}
|
||||
|
||||
return VaultClientResponse.of(null, e.getStatusCode(), uri, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Vault {@link URI} based on the given {@link VaultProperties} and
|
||||
* {@code path}.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param path must not be empty or {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static URI buildUri(VaultProperties properties, String path) {
|
||||
return URI.create(createBaseUrlWithPath(properties, path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Vault {@link URI} based on the given {@link VaultProperties} and
|
||||
* {@code pathTemplate}. URI template variables will be expanded using
|
||||
* {@code uriVariables}.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param pathTemplate must not be empty or {@literal null}.
|
||||
* @param uriVariables must not be {@literal null}.
|
||||
* @see org.springframework.web.util.UriComponentsBuilder
|
||||
* @return
|
||||
*/
|
||||
public URI buildUri(VaultProperties properties, String pathTemplate,
|
||||
Map<String, ?> uriVariables) {
|
||||
|
||||
Assert.notNull(properties, "VaultProperties must not be null!");
|
||||
Assert.hasText(pathTemplate, "Path must not be empty!");
|
||||
Assert.notNull(properties, "Vault Token must not be null!");
|
||||
|
||||
return restTemplate.getUriTemplateHandler().expand(
|
||||
createBaseUrlWithPath(properties, pathTemplate), uriVariables);
|
||||
}
|
||||
|
||||
private HttpHeaders createHeaders(VaultToken vaultToken) {
|
||||
|
||||
Assert.notNull(vaultToken, "Vault Token must not be null!");
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add(VAULT_TOKEN, vaultToken.getToken());
|
||||
return headers;
|
||||
}
|
||||
|
||||
private static String createBaseUrlWithPath(VaultProperties properties, String path) {
|
||||
|
||||
Assert.notNull(properties, "VaultProperties must not be null!");
|
||||
Assert.hasText(path, "Path must not be empty!");
|
||||
|
||||
return String.format("%s://%s:%s/%s/%s", properties.getScheme(),
|
||||
properties.getHost(), properties.getPort(), API_VERSION, path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +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;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Encapsulates the client response used in {@link VaultClient}. Consists of the body,
|
||||
* status code the location and a message. The {@code body} is empty for all
|
||||
* non-successful results.
|
||||
*
|
||||
* This class is immutable.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
public class VaultClientResponse {
|
||||
|
||||
private VaultResponse body;
|
||||
private HttpStatus statusCode;
|
||||
private URI uri;
|
||||
private String message;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {@literal true} if the request was completed successfully.
|
||||
*/
|
||||
public boolean isSuccessful() {
|
||||
return body != null && statusCode.is2xxSuccessful();
|
||||
}
|
||||
}
|
||||
@@ -1,63 +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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Utility to obtain a Vault error message.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class VaultErrorMessage {
|
||||
|
||||
private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Obtain the error message from a JSON response.
|
||||
*
|
||||
* @param json
|
||||
* @return
|
||||
*/
|
||||
static String getError(String json) {
|
||||
|
||||
if (json.contains("\"errors\":")) {
|
||||
|
||||
try {
|
||||
Map<String, Object> map = OBJECT_MAPPER.readValue(json.getBytes(),
|
||||
Map.class);
|
||||
if (map.containsKey("errors")) {
|
||||
|
||||
Collection<String> errors = (Collection<String>) map.get("errors");
|
||||
if (errors.size() == 1) {
|
||||
return errors.iterator().next();
|
||||
}
|
||||
return errors.toString();
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException o_O) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +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;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Value object to bind HTTP API responses for sys/health.
|
||||
*
|
||||
* @author Stuart Ingram
|
||||
* @author Bill Koch
|
||||
*/
|
||||
@Data
|
||||
public class VaultHealthResponse {
|
||||
|
||||
private boolean initialized;
|
||||
private boolean sealed;
|
||||
private boolean standby;
|
||||
|
||||
@JsonProperty("server_time_utc")
|
||||
private int serverTimeUtc;
|
||||
}
|
||||
@@ -1,44 +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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Value object to bind HTTP API responses.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
public class VaultResponse {
|
||||
|
||||
private Map<String, Object> auth;
|
||||
private Map<String, String> data;
|
||||
private Map<String, String> metadata;
|
||||
|
||||
@JsonProperty("lease_duration")
|
||||
private long leaseDuration;
|
||||
|
||||
@JsonProperty("lease_id")
|
||||
private String leaseId;
|
||||
private boolean renewable;
|
||||
}
|
||||
@@ -1,56 +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;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Value object for a Vault token.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class VaultToken {
|
||||
|
||||
private String token;
|
||||
private long leaseDuration;
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultToken}.
|
||||
* @param token must not be {@literal null}.
|
||||
* @return the created {@link VaultToken}
|
||||
*/
|
||||
public static VaultToken of(String token) {
|
||||
return of(token, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultToken} with a {@code leaseDuration}.
|
||||
* @param token must not be {@literal null}.
|
||||
* @return the created {@link VaultToken}
|
||||
*/
|
||||
public static VaultToken of(String token, long leaseDuration) {
|
||||
|
||||
Assert.hasText(token, "Token must not be empty");
|
||||
return new VaultToken(token, leaseDuration);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Bootstrap Configuration
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.vault.VaultBootstrapConfiguration
|
||||
@@ -1,151 +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;
|
||||
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.springframework.cloud.vault.VaultProperties.AppIdProperties;
|
||||
import org.springframework.cloud.vault.VaultProperties.AuthenticationMethod;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using various UserIds.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class AppIdAuthenticationMethodsIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
if (!prepare().hasAuth("app-id")) {
|
||||
prepare().mountAuth("app-id");
|
||||
}
|
||||
|
||||
prepare().mapAppId("myapp");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginUsingIpAddressShouldCreateAToken() throws Exception {
|
||||
|
||||
VaultProperties vaultProperties = prepareAppIdAuthenticationMethod(
|
||||
AppIdProperties.IP_ADDRESS, "myapp");
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, prepare().newVaultClient(), new IpAddressUserId());
|
||||
|
||||
assertThat(clientAuthentication.login()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginUsingStaticUserIdShouldCreateAToken() throws Exception {
|
||||
|
||||
VaultProperties vaultProperties = prepareAppIdAuthenticationMethod("my-user-id",
|
||||
"myapp");
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, prepare().newVaultClient(), new StaticUserId(
|
||||
vaultProperties));
|
||||
|
||||
assertThat(clientAuthentication.login()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginUsingMacAddressShouldCreateAToken() throws Exception {
|
||||
|
||||
VaultProperties vaultProperties = prepareAppIdAuthenticationMethod(
|
||||
AppIdProperties.MAC_ADDRESS, "myapp");
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, prepare().newVaultClient(), new MacAddressUserId(
|
||||
vaultProperties));
|
||||
|
||||
assertThat(clientAuthentication.login()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidLogin() throws Exception {
|
||||
|
||||
expectedException.expect(IllegalStateException.class);
|
||||
expectedException.expectMessage("Cannot login using app-id");
|
||||
|
||||
VaultProperties vaultProperties = prepareAppIdAuthenticationMethod(
|
||||
AppIdProperties.IP_ADDRESS, "myapp");
|
||||
vaultProperties.setApplicationName("foobar");
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, prepare().newVaultClient(), new MacAddressUserId(
|
||||
vaultProperties));
|
||||
|
||||
clientAuthentication.login();
|
||||
|
||||
fail("Missing IllegalStateException");
|
||||
}
|
||||
|
||||
private VaultProperties prepareAppIdAuthenticationMethod(String userId, String appId)
|
||||
throws SocketException {
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
|
||||
AppIdProperties appIdProperties = new AppIdProperties();
|
||||
vaultProperties.setApplicationName(appId);
|
||||
appIdProperties.setUserId(userId);
|
||||
|
||||
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface
|
||||
.getNetworkInterfaces();
|
||||
NetworkInterface networkInterface = null;
|
||||
while (networkInterfaces.hasMoreElements()) {
|
||||
networkInterface = networkInterfaces.nextElement();
|
||||
if (networkInterface.getHardwareAddress() != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have always a network interface even if the localhost reverse
|
||||
// lookup maps to an IP address that is not handled by this host.
|
||||
appIdProperties.setNetworkInterface(networkInterface.getName());
|
||||
|
||||
vaultProperties.setAuthentication(AuthenticationMethod.APPID);
|
||||
vaultProperties.setAppId(appIdProperties);
|
||||
|
||||
String userIdValue;
|
||||
if (userId.equals(AppIdProperties.IP_ADDRESS)) {
|
||||
userIdValue = new IpAddressUserId().createUserId();
|
||||
}
|
||||
else if (userId.equals(AppIdProperties.MAC_ADDRESS)) {
|
||||
userIdValue = new MacAddressUserId(vaultProperties).createUserId();
|
||||
}
|
||||
else {
|
||||
userIdValue = userId;
|
||||
}
|
||||
|
||||
prepare().mapUserId(vaultProperties.getApplicationName(), userIdValue);
|
||||
|
||||
return vaultProperties;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.cloud.vault.VaultProperties.AuthenticationMethod;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using AWS-EC2 login. This test requires AWS
|
||||
* credentials, a region and an AMI, see {@link #AWS_ACCESS_KEY}, {@link #AWS_SECRET_KEY}
|
||||
* and the {@link IntegrationTest} properties to be provided externally. It needs to be
|
||||
* run on a AWS-EC2 instance to be able to obtain instance metadata.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class AwsEc2AuthenticationMethodsIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
private final static String AWS_REGION = "eu-west-1";
|
||||
private final static String AWS_AMI = "ami-f95ef58a";
|
||||
private final static String AWS_ACCESS_KEY = System.getProperty("aws.access.key");
|
||||
private final static String AWS_SECRET_KEY = System.getProperty("aws.secret.key");
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
assumeTrue(StringUtils.hasText(AWS_ACCESS_KEY)
|
||||
&& StringUtils.hasText(AWS_SECRET_KEY));
|
||||
|
||||
if (!prepare().hasAuth("aws-ec2")) {
|
||||
prepare().mountAuth("aws-ec2");
|
||||
}
|
||||
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("access_key", AWS_ACCESS_KEY);
|
||||
config.put("secret_key", AWS_SECRET_KEY);
|
||||
config.put("endpoint", String.format("https://ec2.%s.amazonaws.com", AWS_REGION));
|
||||
|
||||
prepare().write("auth/aws-ec2/config/client", config);
|
||||
|
||||
prepare().write(String.format("auth/aws-ec2/role/%s", AWS_AMI),
|
||||
Collections.singletonMap("bound_ami_id", AWS_AMI));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldCreateAToken() throws Exception {
|
||||
|
||||
VaultProperties vaultProperties = prepareAwsEc2Authentication();
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, prepare().newVaultClient());
|
||||
|
||||
assertThat(clientAuthentication.login()).isNotNull();
|
||||
}
|
||||
|
||||
private VaultProperties prepareAwsEc2Authentication() {
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
vaultProperties.setAuthentication(AuthenticationMethod.AWS_EC2);
|
||||
|
||||
return vaultProperties;
|
||||
}
|
||||
}
|
||||
@@ -1,127 +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;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.cloud.vault.ClientHttpRequestFactoryFactory.Netty;
|
||||
import org.springframework.cloud.vault.ClientHttpRequestFactoryFactory.OkHttp;
|
||||
import org.springframework.cloud.vault.VaultProperties.AuthenticationMethod;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.cloud.vault.util.Settings.*;
|
||||
|
||||
import org.assertj.core.util.Files;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using TLS certificate authentication using
|
||||
* various HTTP clients.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class CertAuthenticationMethodsIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
VaultProperties vaultProperties = prepareCertAuthenticationMethod();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
if (!prepare().hasAuth("cert")) {
|
||||
prepare().mountAuth("cert");
|
||||
}
|
||||
|
||||
File workDir = findWorkDir();
|
||||
|
||||
String certificate = Files.contentOf(
|
||||
new File(workDir, "ca/certs/client.cert.pem"), StandardCharsets.US_ASCII);
|
||||
|
||||
prepare().write("auth/cert/certs/my-role",
|
||||
Collections.singletonMap("certificate", certificate));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAuthenticateUsingCertificateAuthenticationUsingHttpComponents()
|
||||
throws Exception {
|
||||
|
||||
VaultClient client = new VaultClient(
|
||||
TestRestTemplateFactory
|
||||
.create(ClientHttpRequestFactoryFactory.HttpComponents
|
||||
.usingHttpComponents(vaultProperties)));
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, client);
|
||||
|
||||
assertThat(clientAuthentication.login()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAuthenticateUsingCertificateAuthenticationUsingOkHttp()
|
||||
throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = OkHttp.usingOkHttp(vaultProperties);
|
||||
|
||||
VaultClient client = new VaultClient(TestRestTemplateFactory.create(factory));
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, client);
|
||||
|
||||
assertThat(clientAuthentication.login()).isNotNull();
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAuthenticateUsingCertificateAuthenticationUsingNetty()
|
||||
throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = Netty.usingNetty(vaultProperties);
|
||||
|
||||
VaultClient client = new VaultClient(TestRestTemplateFactory.create(factory));
|
||||
|
||||
ClientAuthentication clientAuthentication = new DefaultClientAuthentication(
|
||||
vaultProperties, client);
|
||||
|
||||
assertThat(clientAuthentication.login()).isNotNull();
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
|
||||
private VaultProperties prepareCertAuthenticationMethod() {
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
|
||||
vaultProperties.setAuthentication(AuthenticationMethod.CERT);
|
||||
|
||||
vaultProperties.getSsl().setKeyStorePassword("changeit");
|
||||
vaultProperties.getSsl().setKeyStore(
|
||||
new FileSystemResource(new File(findWorkDir(), "client-cert.jks")));
|
||||
|
||||
return vaultProperties;
|
||||
}
|
||||
}
|
||||
@@ -1,98 +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;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.*;
|
||||
import static org.springframework.cloud.vault.ClientHttpRequestFactoryFactory.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.OkHttpClientHttpRequestFactory;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ClientHttpRequestFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class ClientHttpRequestFactoryFactoryIntegrationTests {
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private String url = String.format("%s://%s:%d/v1/sys/health", vaultProperties.getScheme(), vaultProperties.getHost(),
|
||||
vaultProperties.getPort());
|
||||
|
||||
@Test
|
||||
public void httpComponentsClientShouldWork() throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = HttpComponents.usingHttpComponents(vaultProperties);
|
||||
RestTemplate template = new RestTemplate(factory);
|
||||
|
||||
String response = request(template);
|
||||
|
||||
assertThat(factory).isInstanceOf(HttpComponentsClientHttpRequestFactory.class);
|
||||
assertThat(response).isNotNull().contains("initialized");
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
|
||||
private String request(RestTemplate template) {
|
||||
|
||||
// Uninitialized and sealed can cause status 500
|
||||
try {
|
||||
ResponseEntity<String> responseEntity = template.exchange(url, HttpMethod.GET, null, String.class);
|
||||
return responseEntity.getBody();
|
||||
} catch (HttpStatusCodeException e) {
|
||||
return e.getResponseBodyAsString();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nettyClientShouldWork() throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = Netty.usingNetty(vaultProperties);
|
||||
((InitializingBean) factory).afterPropertiesSet();
|
||||
RestTemplate template = new RestTemplate(factory);
|
||||
|
||||
String response = request(template);
|
||||
|
||||
assertThat(factory).isInstanceOf(Netty4ClientHttpRequestFactory.class);
|
||||
assertThat(response).isNotNull().contains("initialized");
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void okHttpClientShouldWork() throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = OkHttp.usingOkHttp(vaultProperties);
|
||||
RestTemplate template = new RestTemplate(factory);
|
||||
|
||||
String response = request(template);
|
||||
|
||||
assertThat(factory).isInstanceOf(OkHttpClientHttpRequestFactory.class);
|
||||
assertThat(response).isNotNull().contains("initialized");
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +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;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.util.PrepareVault;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link PrepareVault}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PrepareVaultTests {
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private PrepareVault prepareVault = new PrepareVault(TestRestTemplateFactory.create(vaultProperties));
|
||||
|
||||
@Test
|
||||
public void initializeShouldCreateANewVault() throws Exception {
|
||||
|
||||
prepareVault.setRootToken(Settings.token());
|
||||
prepareVault.setVaultProperties(vaultProperties);
|
||||
|
||||
if (!prepareVault.isAvailable()) {
|
||||
VaultToken rootToken = prepareVault.initializeVault();
|
||||
prepareVault.setRootToken(rootToken);
|
||||
prepareVault.createToken(vaultProperties.getToken(), "root");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,514 +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.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.cloud.vault.VaultClient;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Test helper to prepare various settings within Vault.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PrepareVault {
|
||||
|
||||
public final static String INITIALIZE_URL_TEMPLATE = "{baseuri}/sys/init";
|
||||
public final static String MOUNT_AUTH_URL_TEMPLATE = "{baseuri}/sys/auth/{authBackend}";
|
||||
public final static String SYS_AUTH_URL_TEMPLATE = "{baseuri}/sys/auth";
|
||||
public final static String MOUNT_SECRET_URL_TEMPLATE = "{baseuri}/sys/mounts/{type}";
|
||||
public final static String SYS_MOUNTS_URL_TEMPLATE = "{baseuri}/sys/mounts";
|
||||
public final static String SEAL_STATUS_URL_TEMPLATE = "{baseuri}/sys/seal-status";
|
||||
public final static String UNSEAL_URL_TEMPLATE = "{baseuri}/sys/unseal";
|
||||
public final static String CREATE_TOKEN_URL_TEMPLATE = "{baseuri}/auth/token/create-orphan";
|
||||
public final static String WRITE_URL_TEMPLATE = "{baseuri}/{path}";
|
||||
public static final ParameterizedTypeReference<Map<String, Object>> MAP_OF_MAPS_TYPE = new ParameterizedTypeReference<Map<String, Object>>() {
|
||||
|
||||
};
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@Setter
|
||||
@NonNull
|
||||
private VaultProperties vaultProperties;
|
||||
|
||||
@Setter
|
||||
@NonNull
|
||||
private VaultToken rootToken;
|
||||
|
||||
public PrepareVault(RestTemplate restTemplate) {
|
||||
|
||||
Assert.notNull(restTemplate, "RestTemplate must not be null");
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultClient}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public VaultClient newVaultClient() {
|
||||
return new VaultClient(restTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Vault and unseal the vault.
|
||||
*
|
||||
* @return the root token.
|
||||
*/
|
||||
public VaultToken initializeVault() {
|
||||
|
||||
Assert.notNull(vaultProperties, "VaultProperties must not be null");
|
||||
|
||||
Map<String, String> parameters = parameters(vaultProperties);
|
||||
|
||||
int createKeys = 2;
|
||||
int requiredKeys = 2;
|
||||
|
||||
InitializeVault initializeVault = InitializeVault.of(createKeys, requiredKeys);
|
||||
|
||||
ResponseEntity<VaultInitialized> initResponse = restTemplate.exchange(
|
||||
INITIALIZE_URL_TEMPLATE, HttpMethod.PUT,
|
||||
new HttpEntity<>(initializeVault), VaultInitialized.class, parameters);
|
||||
|
||||
if (!initResponse.getStatusCode().is2xxSuccessful()) {
|
||||
throw new IllegalStateException("Cannot initialize vault: "
|
||||
+ initResponse.toString());
|
||||
}
|
||||
VaultInitialized initialized = initResponse.getBody();
|
||||
|
||||
for (int i = 0; i < requiredKeys; i++) {
|
||||
|
||||
UnsealKey unsealKey = UnsealKey.of(initialized.getKeys().get(i));
|
||||
ResponseEntity<UnsealProgress> unsealResponse = restTemplate.exchange(
|
||||
UNSEAL_URL_TEMPLATE, HttpMethod.PUT, new HttpEntity<>(unsealKey),
|
||||
UnsealProgress.class, parameters);
|
||||
|
||||
UnsealProgress unsealProgress = unsealResponse.getBody();
|
||||
if (!unsealProgress.isSealed()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return VaultToken.of(initialized.getRootToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a token for the given {@code tokenId} and {@code policy}.
|
||||
*
|
||||
* @param tokenId
|
||||
* @param policy
|
||||
* @return
|
||||
*/
|
||||
public VaultToken createToken(String tokenId, String policy) {
|
||||
|
||||
Map<String, String> parameters = parameters(vaultProperties);
|
||||
|
||||
CreateToken createToken = new CreateToken();
|
||||
createToken.setId(tokenId);
|
||||
if (policy != null) {
|
||||
createToken.setPolicies(Collections.singletonList(policy));
|
||||
}
|
||||
|
||||
HttpHeaders headers = authenticatedHeaders();
|
||||
|
||||
HttpEntity<CreateToken> entity = new HttpEntity<>(createToken, headers);
|
||||
|
||||
ResponseEntity<TokenCreated> createTokenResponse = restTemplate.exchange(
|
||||
CREATE_TOKEN_URL_TEMPLATE, HttpMethod.POST, entity, TokenCreated.class,
|
||||
parameters);
|
||||
|
||||
if (!createTokenResponse.getStatusCode().is2xxSuccessful()) {
|
||||
throw new IllegalStateException("Cannot create token: "
|
||||
+ createTokenResponse.toString());
|
||||
}
|
||||
|
||||
AuthToken authToken = createTokenResponse.getBody().getAuth();
|
||||
|
||||
return VaultToken.of(authToken.getClientToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether Vault is available (vault created and unsealed).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
|
||||
Map<String, String> parameters = parameters(vaultProperties);
|
||||
|
||||
ResponseEntity<String> exchange = null;
|
||||
try {
|
||||
exchange = restTemplate.getForEntity(SEAL_STATUS_URL_TEMPLATE, String.class,
|
||||
parameters);
|
||||
|
||||
if (exchange.getStatusCode().is2xxSuccessful()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (HttpStatusCodeException e) {
|
||||
if (e.getStatusCode().is4xxClientError()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (exchange.getStatusCode().is4xxClientError()) {
|
||||
return false;
|
||||
}
|
||||
throw new IllegalStateException("Vault error: " + exchange.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount an auth backend.
|
||||
*
|
||||
* @param authBackend
|
||||
*/
|
||||
public void mountAuth(String authBackend) {
|
||||
|
||||
Assert.hasText(authBackend, "AuthBackend must not be empty");
|
||||
|
||||
Map<String, String> parameters = parameters(vaultProperties);
|
||||
parameters.put("authBackend", authBackend);
|
||||
|
||||
Map<String, String> requestEntity = Collections.singletonMap("type", authBackend);
|
||||
|
||||
HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestEntity,
|
||||
authenticatedHeaders());
|
||||
|
||||
ResponseEntity<String> responseEntity = restTemplate.exchange(
|
||||
MOUNT_AUTH_URL_TEMPLATE, HttpMethod.POST, entity, String.class,
|
||||
parameters);
|
||||
|
||||
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
throw new IllegalStateException("Cannot create mount auth backend: "
|
||||
+ responseEntity.toString());
|
||||
}
|
||||
|
||||
responseEntity.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a auth-backend is enabled.
|
||||
*
|
||||
* @param authBackend
|
||||
* @return
|
||||
*/
|
||||
public boolean hasAuth(String authBackend) {
|
||||
|
||||
Assert.hasText(authBackend, "AuthBackend must not be empty");
|
||||
return hasMount(SYS_AUTH_URL_TEMPLATE, authBackend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount an secret backend.
|
||||
*
|
||||
* @param secretBackend
|
||||
*/
|
||||
public void mountSecret(String secretBackend) {
|
||||
|
||||
Assert.hasText(secretBackend, "SecretBackend must not be empty");
|
||||
|
||||
Map<String, String> parameters = parameters(vaultProperties);
|
||||
parameters.put("type", secretBackend);
|
||||
|
||||
Map<String, String> requestEntity = Collections.singletonMap("type",
|
||||
secretBackend);
|
||||
|
||||
HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestEntity,
|
||||
authenticatedHeaders());
|
||||
|
||||
ResponseEntity<String> responseEntity = restTemplate.exchange(
|
||||
MOUNT_SECRET_URL_TEMPLATE, HttpMethod.POST, entity, String.class,
|
||||
parameters);
|
||||
|
||||
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
throw new IllegalStateException("Cannot create mount secret backend: "
|
||||
+ responseEntity.toString());
|
||||
}
|
||||
|
||||
responseEntity.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a auth-backend is enabled.
|
||||
*
|
||||
* @param secretBackend
|
||||
* @return
|
||||
*/
|
||||
public boolean hasSecret(String secretBackend) {
|
||||
|
||||
Assert.hasText(secretBackend, "SecretBackend must not be empty");
|
||||
return hasMount(SYS_MOUNTS_URL_TEMPLATE, secretBackend);
|
||||
}
|
||||
|
||||
private boolean hasMount(String urlTemplate, String type) {
|
||||
Map<String, String> parameters = parameters(vaultProperties);
|
||||
|
||||
HttpEntity<Map<String, String>> entity = new HttpEntity<>(authenticatedHeaders());
|
||||
|
||||
ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange(
|
||||
urlTemplate, HttpMethod.GET, entity, MAP_OF_MAPS_TYPE, parameters);
|
||||
|
||||
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
throw new IllegalStateException("Cannot enumerate mounts: "
|
||||
+ responseEntity.toString());
|
||||
}
|
||||
|
||||
Map<String, Object> body = responseEntity.getBody();
|
||||
for (Entry<String, Object> entry : body.entrySet()) {
|
||||
|
||||
if (entry.getValue() instanceof Map) {
|
||||
Map<String, Object> nested = (Map<String, Object>) entry.getValue();
|
||||
|
||||
if (entry.getKey().contains(type) && type.equals(nested.get("type"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write key-value data to the Vault secret backend.
|
||||
*
|
||||
* @param path
|
||||
* @param data
|
||||
*/
|
||||
public void writeSecret(String path, Map<String, ?> data) {
|
||||
|
||||
Assert.hasText(path, "Path must not be empty");
|
||||
write("secret/" + path, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write key-value data to a path in Vault.
|
||||
*
|
||||
* @param path
|
||||
* @param data
|
||||
*/
|
||||
public void write(String path, Map<String, ?> data) {
|
||||
write(HttpMethod.POST, path, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write key-value data to a path in Vault.
|
||||
*
|
||||
* @param httpMethod
|
||||
* @param path
|
||||
* @param data
|
||||
*/
|
||||
public void write(HttpMethod httpMethod, String path, Map<String, ?> data) {
|
||||
|
||||
Assert.notNull(httpMethod, "HttpMethod must not be null");
|
||||
Assert.hasText(path, "Path must not be empty");
|
||||
Assert.notNull(data, "Data must not be null");
|
||||
|
||||
HttpHeaders headers = authenticatedHeaders();
|
||||
|
||||
Map<String, String> parameters = parameters(vaultProperties);
|
||||
parameters.put("path", path);
|
||||
|
||||
ResponseEntity<String> exchange = restTemplate.exchange(WRITE_URL_TEMPLATE,
|
||||
HttpMethod.PUT, new HttpEntity<Object>(data, headers), String.class,
|
||||
parameters);
|
||||
|
||||
if (!exchange.getStatusCode().is2xxSuccessful()) {
|
||||
throw new IllegalStateException(String.format("Cannot write to %s: %s", path,
|
||||
exchange.getBody()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an userId to appId mapping.
|
||||
*
|
||||
* @param appId
|
||||
* @param userId
|
||||
*/
|
||||
public void mapUserId(String appId, String userId) {
|
||||
|
||||
Map<String, String> userIdData = new HashMap<>();
|
||||
userIdData.put("value", appId); // name of the app-id
|
||||
userIdData.put("cidr_block", "0.0.0.0/0");
|
||||
|
||||
String appIdPath = vaultProperties.getAppId().getAppIdPath();
|
||||
if (!hasAuth(appIdPath)) {
|
||||
mountAuth(appIdPath);
|
||||
}
|
||||
|
||||
write(String.format("auth/%s/map/user-id/%s", appIdPath, userId), userIdData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an appId mapping.
|
||||
*
|
||||
* @param appId
|
||||
*/
|
||||
public void mapAppId(String appId) {
|
||||
|
||||
Map<String, String> appIdData = new HashMap<>();
|
||||
appIdData.put("value", "root"); // policy
|
||||
appIdData.put("display_name", "this is my test application");
|
||||
|
||||
write(String.format("auth/%s/map/app-id/%s", vaultProperties.getAppId()
|
||||
.getAppIdPath(), appId), appIdData);
|
||||
}
|
||||
|
||||
private HttpHeaders authenticatedHeaders() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add(VaultClient.VAULT_TOKEN, rootToken.getToken());
|
||||
return headers;
|
||||
}
|
||||
|
||||
private Map<String, String> parameters(VaultProperties vaultProperties) {
|
||||
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
String baseUri = String.format("%s://%s:%s/%s", vaultProperties.getScheme(),
|
||||
vaultProperties.getHost(), vaultProperties.getPort(),
|
||||
VaultClient.API_VERSION);
|
||||
parameters.put("baseuri", baseUri);
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
static class TokenCreated {
|
||||
|
||||
@JsonProperty("lease_duration")
|
||||
private long leaseDuration;
|
||||
@JsonProperty("renewable")
|
||||
private boolean renewable;
|
||||
@JsonProperty("auth")
|
||||
private AuthToken auth;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
static class InitializeVault {
|
||||
|
||||
@JsonProperty("secret_shares")
|
||||
private int secretShares;
|
||||
|
||||
@JsonProperty("secret_threshold")
|
||||
private int secretThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
static class CreateToken {
|
||||
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("policies")
|
||||
private List<String> policies;
|
||||
|
||||
@JsonProperty("ttl")
|
||||
private String ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
static class UnsealKey {
|
||||
@JsonProperty
|
||||
@NonNull
|
||||
private String key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
static class UnsealProgress {
|
||||
|
||||
@JsonProperty("sealed")
|
||||
private boolean sealed;
|
||||
@JsonProperty("t")
|
||||
private int t;
|
||||
@JsonProperty("n")
|
||||
private int n;
|
||||
@JsonProperty("progress")
|
||||
private int progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
static class VaultInitialized {
|
||||
|
||||
@JsonProperty("keys")
|
||||
private List<String> keys;
|
||||
@JsonProperty("root_token")
|
||||
private String rootToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
public static class AuthToken {
|
||||
|
||||
@JsonProperty("client_token")
|
||||
private String clientToken;
|
||||
|
||||
@JsonProperty("policies")
|
||||
private List<String> policies;
|
||||
|
||||
@JsonProperty("metadata")
|
||||
private Map<String, Object> metadata;
|
||||
|
||||
@JsonProperty("lease_duration")
|
||||
private long leaseDuration;
|
||||
|
||||
@JsonProperty("renewable")
|
||||
private boolean renewable;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +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.util;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.springframework.cloud.vault.TestRestTemplateFactory;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
|
||||
/**
|
||||
* Vault rule to ensure a running and prepared Vault.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class VaultRule extends ExternalResource {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
private final PrepareVault prepareVault;
|
||||
|
||||
public VaultRule() {
|
||||
this(Settings.createVaultProperties());
|
||||
}
|
||||
|
||||
public VaultRule(VaultProperties vaultProperties) {
|
||||
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.prepareVault = new PrepareVault(TestRestTemplateFactory.create(vaultProperties));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before() {
|
||||
|
||||
try (Socket socket = new Socket()) {
|
||||
|
||||
socket.connect(new InetSocketAddress(InetAddress.getByName("localhost"),
|
||||
vaultProperties.getPort()));
|
||||
socket.close();
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Vault is not running on localhost:%d which is required to run a test using @Rule %s",
|
||||
vaultProperties.getPort(), getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
prepareVault.setVaultProperties(vaultProperties);
|
||||
|
||||
if (!prepareVault.isAvailable()) {
|
||||
VaultToken rootToken = prepareVault.initializeVault();
|
||||
prepareVault.setRootToken(rootToken);
|
||||
prepareVault.createToken(vaultProperties.getToken(), "root");
|
||||
}
|
||||
else {
|
||||
prepareVault.setRootToken(Settings.token());
|
||||
}
|
||||
}
|
||||
|
||||
public PrepareVault prepare() {
|
||||
return prepareVault;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,15 +22,24 @@
|
||||
<properties>
|
||||
<spring-cloud-context.version>1.1.2.BUILD-SNAPSHOT</spring-cloud-context.version>
|
||||
<spring-boot.version>1.4.0.RELEASE</spring-boot.version>
|
||||
<spring-vault.version>1.0.0.BUILD-SNAPSHOT</spring-vault.version>
|
||||
<httpclient.version>4.5.2</httpclient.version>
|
||||
<httpcore.version>4.4.4</httpcore.version>
|
||||
<netty.version>4.1.0.Final</netty.version>
|
||||
<okhttp.version>2.7.5</okhttp.version>
|
||||
<slf4j.version>1.7.21</slf4j.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring Vault -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.vault</groupId>
|
||||
<artifactId>spring-vault-core</artifactId>
|
||||
<version>${spring-vault.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud Vault -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -38,14 +47,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-config</artifactId>
|
||||
@@ -119,6 +120,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-vault-core</artifactId>
|
||||
<groupId>org.springframework.vault</groupId>
|
||||
<artifactId>spring-vault-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -12,6 +12,6 @@ mkdir -p ${BASEDIR}/consul/data
|
||||
./consul/consul agent -server \
|
||||
-bootstrap-expect 1 \
|
||||
-data-dir ${BASEDIR}/consul/data \
|
||||
-config-file=${BASEDIR}/spring-cloud-vault-core/src/test/resources/consul.json
|
||||
-config-file=${BASEDIR}/src/test/bash/consul.json
|
||||
|
||||
exit $?
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
|
||||
BASEDIR=`dirname $0`/../../..
|
||||
|
||||
./vault/vault server -config=${BASEDIR}/spring-cloud-vault-core/src/test/resources/vault.conf
|
||||
./vault/vault server -config=${BASEDIR}/src/test/bash/vault.conf
|
||||
|
||||
exit $?
|
||||
|
||||
Reference in New Issue
Block a user