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:
Mark Paluch
2016-09-21 19:19:09 +02:00
parent 853750a1ba
commit 19cf1067bc
92 changed files with 1174 additions and 3631 deletions

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.aws;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.vault.VaultSecretBackend;
import org.springframework.cloud.vault.config.VaultSecretBackend;
import lombok.Data;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.consul;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.vault.VaultSecretBackend;
import org.springframework.cloud.vault.config.VaultSecretBackend;
import lombok.Data;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ package org.springframework.cloud.vault.config.rabbitmq;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.vault.VaultSecretBackend;
import org.springframework.cloud.vault.config.VaultSecretBackend;
import lombok.Data;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.vault.config;
import static org.assertj.core.api.Assertions.*;
import java.util.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");
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.vault.VaultBootstrapConfiguration

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 $?

View File

@@ -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 $?