Uses label when searching secrets in [Spring]VaultEnvironmentRepository (#2460)

* Uses label when searching secrets in [Spring]VaultEnvironmentRepository

Signed-off-by: kvmw <mshamsi@broadcom.com>

* Adds a feature flag to enable label in vault secret paths

Signed-off-by: kvmw <mshamsi@broadcom.com>

* Makes default-label in [Spring]VaultEnvironmentRepository configurable

Signed-off-by: kvmw <mshamsi@broadcom.com>

* When label flag is enabled, profile should always by included in vault key

Signed-off-by: kvmw <mshamsi@broadcom.com>

* Updates Vault docs

Signed-off-by: kvmw <mshamsi@broadcom.com>

* Switches to main as default label for vault

Signed-off-by: kvmw <mshamsi@broadcom.com>

---------

Signed-off-by: kvmw <mshamsi@broadcom.com>
This commit is contained in:
Kaveh Shamsi
2024-10-09 16:55:29 +02:00
committed by GitHub
parent e464808fa3
commit 6914dc89b1
6 changed files with 501 additions and 87 deletions

View File

@@ -59,6 +59,12 @@ The following table describes configurable Vault properties:
|defaultKey
|application
|defaultLabel
|main (Only used when `enableLabel` is set to `true`)
|enableLabel
|false
|profileSeparator
|,
@@ -158,6 +164,24 @@ Properties written to `secret/application` are available to <<_vault_server,all
An application with the name, `myApp`, would have any properties written to `secret/myApp` and `secret/application` available to it.
When `myApp` has the `dev` profile enabled, properties written to all of the above paths would be available to it, with properties in the first path in the list taking priority over the others.
[[enabling-serach-by-label]]
== Enabling Search by Label
By default, Vault backend does not use the label when searching for secrets. You can change this by
setting the `enableLabel` feature flag to `true` and, optionally, setting the `defaultLabel`.
When `defaultLabel` is not provided `main` will be used.
When `enableLabel` feature flag is on, the secrets in Vault should always have all three segments(application name, profile and label) in their paths.
So the example in previous section, with enabled feature flag, would be like :
[source,sh]
----
secret/myApp,dev,myLabel
secret/myApp,default,myLabel # default profile
secret/application,dev,myLabel # default application name
secret/application,default,myLabel # default application name and default profile.
----
[[decrypting-vault-secrets]]
== Decrypting Vault Secrets in Property Sources

View File

@@ -18,11 +18,11 @@ package sample;
import java.io.IOException;
import org.json.JSONException;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.Container.ExecResult;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
@@ -36,9 +36,10 @@ import org.springframework.test.util.TestSocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test for https://github.com/spring-cloud/spring-cloud-config/issues/1997
* The error only occurs if a profile specific config imports is used, otherwise
* reordering does not take place. A profile specific config import is defined in
* Integration test for issue
* <a href="https://github.com/spring-cloud/spring-cloud-config/issues/1997">#1997</a> The
* error only occurs if a profile specific config imports is used, otherwise reordering
* does not take place. A profile specific config import is defined in
* vaultordering/client-dev.yml
*/
@Testcontainers
@@ -46,27 +47,26 @@ public class ConfigDataOrderingVaultIntegrationTests {
private static final int configServerPort = TestSocketUtils.findAvailableTcpPort();
private static final int configClientPort = TestSocketUtils.findAvailableTcpPort();
private static ConfigurableApplicationContext client;
private static ConfigurableApplicationContext server;
@Container
public static VaultContainer vaultContainer = new VaultContainer<>(DockerImageName.parse("vault:1.13.3"))
public static VaultContainer<?> vaultContainer = new VaultContainer<>(DockerImageName.parse("vault:1.13.3"))
.withVaultToken("my-root-token")
.withClasspathResourceMapping("vaultordering/vault_test_policy.txt", "/tmp/vault_test_policy.txt",
BindMode.READ_ONLY);
@BeforeAll
public static void startConfigServer() throws IOException, InterruptedException, JSONException {
public static void startConfigServer() throws IOException, InterruptedException {
server = SpringApplication.run(TestConfigServerApplication.class,
"--spring.config.location=classpath:/vaultordering/", "--spring.config.name=server",
"--server.port=" + configServerPort,
"--spring.cloud.config.server.vault.port=" + vaultContainer.getFirstMappedPort());
execInVault("vault", "kv", "put", "secret/client-app,dev", "my.prop=vaultdev");
execInVault("vault", "kv", "put", "secret/client-app", "my.prop=vault");
execInVault("vault", "kv", "put", "secret/client-app,dev", "my.prop=value-in-dev");
execInVault("vault", "kv", "put", "secret/client-app,prod", "my.prop=value-in-prod");
execInVault("vault", "kv", "put", "secret/client-app", "my.prop=default-value");
}
@@ -81,22 +81,35 @@ public class ConfigDataOrderingVaultIntegrationTests {
}
@Test
void propertyFromVaultIsUsed() {
client = SpringApplication.run(TestConfigServerApplication.class, "--server.port=" + configClientPort,
void profileSpecificPropertyFromVaultIsUsed() {
client = SpringApplication.run(TestConfigServerApplication.class,
"--server.port=" + TestSocketUtils.findAvailableTcpPort(),
"--spring.config.location=classpath:/vaultordering/", "--spring.config.name=client",
"--spring.profiles.active=dev", "--spring.application.name=client-app",
"--spring.cloud.config.enabled=true", "--spring.cloud.config.server.enabled=false",
"--config.server.port=" + configServerPort);
assertThat(client.getEnvironment().getProperty("my.prop")).isEqualTo("vaultdev");
assertThat(client.getEnvironment().getProperty("my.prop")).isEqualTo("value-in-dev");
}
private static String execInVault(String... command) throws IOException, InterruptedException {
org.testcontainers.containers.Container.ExecResult execResult = vaultContainer.execInContainer(command);
@Test
void profileSpecificPropertyFromVaultIsUsedInCorrectOrder() {
client = SpringApplication.run(TestConfigServerApplication.class,
"--server.port=" + TestSocketUtils.findAvailableTcpPort(),
"--spring.config.location=classpath:/vaultordering/", "--spring.config.name=client",
"--spring.profiles.active=dev,prod", "--spring.application.name=client-app",
"--spring.cloud.config.enabled=true", "--spring.cloud.config.server.enabled=false",
"--config.server.port=" + configServerPort);
assertThat(client.getEnvironment().getProperty("my.prop")).isEqualTo("value-in-prod");
}
private static void execInVault(String... command) throws IOException, InterruptedException {
ExecResult execResult = vaultContainer.execInContainer(command);
assertThat(execResult.getExitCode()).isZero();
assertThat(execResult.getStderr()).isEmpty();
return execResult.getStdout();
}
}

View File

@@ -16,11 +16,12 @@
package org.springframework.cloud.config.server.environment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotEmpty;
@@ -33,6 +34,7 @@ import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.core.Ordered;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER;
@@ -46,7 +48,9 @@ import static org.springframework.cloud.config.client.ConfigClientProperties.STA
*/
public abstract class AbstractVaultEnvironmentRepository implements EnvironmentRepository, Ordered {
private static Log log = LogFactory.getLog(AbstractVaultEnvironmentRepository.class);
private static final String DEFAULT_PROFILE = "default";
private static final Log log = LogFactory.getLog(AbstractVaultEnvironmentRepository.class);
// TODO: move to watchState:String on findOne?
protected final ObjectProvider<HttpServletRequest> request;
@@ -65,12 +69,18 @@ public abstract class AbstractVaultEnvironmentRepository implements EnvironmentR
@NotEmpty
protected String profileSeparator;
protected final boolean enableLabel;
protected final String defaultLabel;
protected int order;
public AbstractVaultEnvironmentRepository(ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
VaultEnvironmentProperties properties) {
this.defaultKey = properties.getDefaultKey();
this.profileSeparator = properties.getProfileSeparator();
this.enableLabel = properties.isEnableLabel();
this.defaultLabel = properties.getDefaultLabel();
this.order = properties.getOrder();
this.request = request;
this.watch = watch;
@@ -78,24 +88,32 @@ public abstract class AbstractVaultEnvironmentRepository implements EnvironmentR
@Override
public Environment findOne(String application, String profile, String label) {
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
List<String> scrubbedProfiles = scrubProfiles(profiles);
if (ObjectUtils.isEmpty(profile)) {
profile = DEFAULT_PROFILE;
}
if (ObjectUtils.isEmpty(label)) {
label = defaultLabel;
}
List<String> keys = findKeys(application, scrubbedProfiles);
var environment = new Environment(application, split(profile), label, null, getWatchState());
Environment environment = new Environment(application, profiles, label, null, getWatchState());
var profiles = normalize(profile, DEFAULT_PROFILE);
var applications = normalize(application, this.defaultKey);
for (String key : keys) {
// read raw 'data' key from vault
String data = read(key);
if (data != null) {
// data is in json format of which, yaml is a superset, so parse
final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ByteArrayResource(data.getBytes()));
Properties properties = yaml.getObject();
for (String prof : profiles) {
for (String app : applications) {
var key = vaultKey(app, prof, label);
// read raw 'data' key from vault
String data = read(key);
if (data != null) {
// data is in json format of which, yaml is a superset, so parse
var yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ByteArrayResource(data.getBytes()));
var properties = yaml.getObject();
if (!properties.isEmpty()) {
environment.add(new PropertySource("vault:" + key, properties));
if (properties != null && !properties.isEmpty()) {
environment.add(new PropertySource("vault:" + key, properties));
}
}
}
}
@@ -105,6 +123,22 @@ public abstract class AbstractVaultEnvironmentRepository implements EnvironmentR
protected abstract String read(String key);
private String vaultKey(String application, String profile, String label) {
var key = application;
if (this.enableLabel) {
// always append profile to the key, if flag is enabled.
key += this.profileSeparator + profile;
// always append label to the key, if flag is enabled.
key += this.profileSeparator + label;
}
else if (!DEFAULT_PROFILE.equals(profile)) {
// default profile should not be included in the key, if flag is not enabled.
key += this.profileSeparator + profile;
}
return key;
}
private String getWatchState() {
HttpServletRequest servletRequest = this.request.getIfAvailable();
if (servletRequest != null) {
@@ -120,35 +154,22 @@ public abstract class AbstractVaultEnvironmentRepository implements EnvironmentR
return null;
}
private List<String> findKeys(String application, List<String> profiles) {
List<String> keys = new ArrayList<>();
/**
* Splits the comma delimited items and returns the reversed distinct items with given
* default item at the end.
*/
private List<String> normalize(String commaDelimitedItems, String defaultItem) {
var items = Stream.concat(Stream.of(defaultItem), Arrays.stream(split(commaDelimitedItems)))
.distinct()
.filter(Predicate.not(ObjectUtils::isEmpty))
.collect(Collectors.toList());
if (StringUtils.hasText(this.defaultKey) && !this.defaultKey.equals(application)) {
keys.add(this.defaultKey);
addProfiles(keys, this.defaultKey, profiles);
}
// application may have comma-separated list of names
String[] applications = StringUtils.commaDelimitedListToStringArray(application);
for (String app : applications) {
keys.add(app);
addProfiles(keys, app, profiles);
}
Collections.reverse(keys);
return keys;
Collections.reverse(items);
return items;
}
private List<String> scrubProfiles(String[] profiles) {
List<String> scrubbedProfiles = new ArrayList<>(Arrays.asList(profiles));
scrubbedProfiles.remove("default");
return scrubbedProfiles;
}
private void addProfiles(List<String> contexts, String baseContext, List<String> profiles) {
for (String profile : profiles) {
contexts.add(baseContext + this.profileSeparator + profile);
}
private String[] split(String str) {
return StringUtils.commaDelimitedListToStringArray(str);
}
public void setDefaultKey(String defaultKey) {

View File

@@ -101,6 +101,17 @@ public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProp
*/
private String token;
/**
* Flag to indicate that the repository should use 'label' as well as
* 'application-name' and 'profile', for vault secrets. By default, the vault secrets
* are expected to be in 'application-name,profile' path. When this flag enabled, they
* are expected to be in `application-name,profile,label' path. To maintain
* compatibility this flag is not enabled by default.
*/
private boolean enableLabel = false;
private String defaultLabel = "main";
private AppRoleProperties appRole = new AppRoleProperties();
private AwsEc2Properties awsEc2 = new AwsEc2Properties();
@@ -229,6 +240,22 @@ public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProp
this.token = token;
}
public boolean isEnableLabel() {
return enableLabel;
}
public void setEnableLabel(boolean enableLabel) {
this.enableLabel = enableLabel;
}
public String getDefaultLabel() {
return defaultLabel;
}
public void setDefaultLabel(String defaultLabel) {
this.defaultLabel = defaultLabel;
}
public AppRoleProperties getAppRole() {
return this.appRole;
}

View File

@@ -23,7 +23,6 @@ import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@@ -57,12 +56,8 @@ public class VaultEnvironmentRepositoryTests {
@SuppressWarnings("unchecked")
ArgumentCaptor<HttpEntity<?>> requestHeaderCaptor = ArgumentCaptor.forClass(HttpEntity.class);
@BeforeEach
public void init() {
}
@Test
public void testFindOneNoDefaultKey() {
public void findOneWithNoDefaultKey() {
stubRestTemplate("secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/application", toEntityResponse("def-foo", "def-bar"));
@@ -71,14 +66,33 @@ public class VaultEnvironmentRepositoryTests {
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
assertThat(requestHeaderCaptor.getValue().getHeaders()).containsEntry("X-Vault-Token", List.of("token"));
}
@Test
public void testBackendWithSlashes() {
public void findOneWithEmptyDefaultKey() {
stubRestTemplate("secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/application", toEntityResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setDefaultKey("");
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, "my-label");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(1);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
}
@Test
public void findOneWithSlashesInBackend() {
stubRestTemplate("foo/bar/secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("foo/bar/secret/application", toEntityResponse("def-foo", "def-bar"));
@@ -90,12 +104,14 @@ public class VaultEnvironmentRepositoryTests {
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void testFindOneDefaultKeySetAndDifferentToApplication() {
public void findOneWithDefaultKeySet() {
stubRestTemplate("secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/mydefaultkey", toEntityResponse("def-foo", "def-bar"));
@@ -107,12 +123,14 @@ public class VaultEnvironmentRepositoryTests {
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:mydefaultkey");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void testFindOneDefaultKeySetAndDifferentToMultipleApplications() {
public void findOneWithDefaultKeyAndMultipleApplicationNames() {
stubRestTemplate("secret/myapp", toEntityResponse("myapp-foo", "myapp-bar"));
stubRestTemplate("secret/yourapp", toEntityResponse("yourapp-foo", "yourapp-bar"));
stubRestTemplate("secret/mydefaultkey", toEntityResponse("def-foo", "def-bar"));
@@ -126,28 +144,82 @@ public class VaultEnvironmentRepositoryTests {
assertThat(e.getPropertySources().size()).isEqualTo(3);
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("yourapp-foo", "yourapp-bar"));
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:yourapp");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("myapp-foo", "myapp-bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:mydefaultkey");
}
@Test
public void testFindOneDefaultKeySetAndEqualToApplication() {
public void findOneWithDefaultKeySetToApplicationName() {
stubRestTemplate("secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/application", toEntityResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setDefaultKey("myapp");
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, null);
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, "lbl");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(1);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
}
@Test
public void missingConfigToken() {
public void findOneWithProfile() {
stubRestTemplate("secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/myapp,my-profile", toEntityResponse("pro-foo", "pro-bar"));
stubRestTemplate("secret/application", toEntityResponse("def-foo", "def-bar"));
stubRestTemplate("secret/application,my-profile", toEntityResponse("def-pro-foo", "def-pro-bar"));
var e = vaultEnvironmentRepository().findOne("myapp", "my-profile", "lbl");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(4);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,my-profile");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("pro-foo", "pro-bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,my-profile");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-pro-foo", "def-pro-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(3).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(3).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithMultipleProfiles() {
stubRestTemplate("secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/myapp,pr1", toEntityResponse("pr1-foo", "pr1-bar"));
stubRestTemplate("secret/myapp,pr2", toEntityResponse("pr2-foo", "pr2-bar"));
stubRestTemplate("secret/application", toEntityResponse("def-foo", "def-bar"));
stubRestTemplate("secret/application,pr1", toEntityResponse("def-pr1-foo", "def-pr1-bar"));
stubRestTemplate("secret/application,pr2", toEntityResponse("def-pr2-foo", "def-pr2-bar"));
var e = vaultEnvironmentRepository().findOne("myapp", "pr1,pr2", null);
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(6);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,pr2");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("pr2-foo", "pr2-bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,pr2");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-pr2-foo", "def-pr2-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:myapp,pr1");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("pr1-foo", "pr1-bar"));
assertThat(e.getPropertySources().get(3).getName()).isEqualTo("vault:application,pr1");
assertThat(e.getPropertySources().get(3).getSource()).isEqualTo(Map.of("def-pr1-foo", "def-pr1-bar"));
assertThat(e.getPropertySources().get(4).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(4).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(5).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(5).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWhenConfigTokenIsMissing() {
ConfigTokenProvider nullTokenProvider = () -> null;
Assertions.assertThatThrownBy(() -> vaultEnvironmentRepository(nullTokenProvider).findOne("myapp", null, null))
@@ -155,25 +227,28 @@ public class VaultEnvironmentRepositoryTests {
}
@Test
public void testVaultVersioning() {
public void findOneWithVaultVersioning() {
stubRestTemplate("secret/data/myapp", toEntityResponse("data", Map.of("foo", "bar")));
stubRestTemplate("secret/data/application", toEntityResponse("data", Map.of("def-foo", "def-bar")));
var properties = new VaultEnvironmentProperties();
properties.setKvVersion(2);
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, null);
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, "label");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
assertThat(requestHeaderCaptor.getValue().getHeaders()).containsEntry("X-Vault-Token", List.of("token"));
}
@Test
public void testVaultKV2WithPath2Key() {
public void findOneWithVaultKV2WithPath2Key() {
stubRestTemplate("secret/data/myorg/myapp", toEntityResponse("data", Map.of("foo", "bar")));
stubRestTemplate("secret/data/myorg/application", toEntityResponse("data", Map.of("def-foo", "def-bar")));
@@ -181,26 +256,112 @@ public class VaultEnvironmentRepositoryTests {
properties.setKvVersion(2);
properties.setPathToKey("myorg");
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, null);
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, "lbl");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void testNamespaceHeaderSent() {
public void findOneWithNamespaceHeaderSent() {
stubRestTemplate("secret/myapp", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/application", toEntityResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setNamespace("mynamespace");
vaultEnvironmentRepository(properties).findOne("myapp", null, null);
vaultEnvironmentRepository(properties).findOne("myapp", null, "lbl");
assertThat(requestHeaderCaptor.getValue().getHeaders()).containsEntry("X-Vault-Namespace",
List.of("mynamespace"));
assertThat(requestHeaderCaptor.getValue().getHeaders()).containsEntry("X-Vault-Token", List.of("token"));
}
@Test
public void findOneWithDefaultLabelWhenLabelEnabled() {
stubRestTemplate("secret/myapp,default,main", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/application,default,main", toEntityResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, null);
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,default,main");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,default,main");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithCustomDefaultLabelWhenLabelEnabled() {
stubRestTemplate("secret/myapp,default,custom", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/application,default,custom", toEntityResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
properties.setDefaultLabel("custom");
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, null);
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,default,custom");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,default,custom");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithCustomLabelWhenLabelEnabled() {
stubRestTemplate("secret/myapp,default,my-label", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/application,default,my-label", toEntityResponse(Map.of()));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
properties.setDefaultLabel("custom");
var e = vaultEnvironmentRepository(properties).findOne("myapp", null, "my-label");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(1);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,default,my-label");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
}
@Test
public void findOneWithCustomLabelAndProfileWhenLabelEnabled() {
stubRestTemplate("secret/myapp,default,my-label", toEntityResponse("foo", "bar"));
stubRestTemplate("secret/myapp,pr1,my-label", toEntityResponse("pr1-foo", "pr1-bar"));
stubRestTemplate("secret/application,default,my-label", toEntityResponse("def-foo", "def-bar"));
stubRestTemplate("secret/application,pr1,my-label", toEntityResponse("def-pr1-foo", "def-pr1-bar"));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
var e = vaultEnvironmentRepository(properties).findOne("myapp", "pr1", "my-label");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources().size()).isEqualTo(4);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,pr1,my-label");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("pr1-foo", "pr1-bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,pr1,my-label");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-pr1-foo", "def-pr1-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:myapp,default,my-label");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(3).getName()).isEqualTo("vault:application,default,my-label");
assertThat(e.getPropertySources().get(3).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
private VaultEnvironmentRepository vaultEnvironmentRepository() {

View File

@@ -45,17 +45,17 @@ public class SpringVaultEnvironmentRepositoryTests {
private final VaultKeyValueOperations keyValueTemplate = mock(VaultKeyValueOperations.class);
@Test
public void testFindOneNoDefaultKey() {
public void findOneNoDefaultKey() {
defaultKeyTest("", 2);
}
@Test
public void testPathKey() {
public void findOneWithPathKey() {
defaultKeyTest("mypath", 2);
}
@Test
public void testPathKeyNotUsedWithVersionOne() {
public void findOneWithPathKeyNotUsedWithVersionOne() {
defaultKeyTest("mypath", 1);
}
@@ -77,12 +77,14 @@ public class SpringVaultEnvironmentRepositoryTests {
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void testBackendWithSlashes() {
public void findOneWithSlashesInBackend() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("application")).thenReturn(withVaultResponse("def-foo", "def-bar"));
@@ -94,29 +96,50 @@ public class SpringVaultEnvironmentRepositoryTests {
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void testFindOneDefaultKeySetAndDifferentToApplication() {
public void findOneWithDefaultKeySet() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("mydefaultkey")).thenReturn(withVaultResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setDefaultKey("mydefaultkey");
var e = springVaultEnvironmentRepository(properties).findOne("myapp", null, null);
var e = springVaultEnvironmentRepository(properties).findOne("myapp", null, "label");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:mydefaultkey");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void testFindOneDefaultKeySetAndDifferentToMultipleApplications() {
public void findOneWithEmptyDefaultKey() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("application")).thenReturn(withVaultResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setDefaultKey("");
var e = springVaultEnvironmentRepository(properties).findOne("myapp", null, "label");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(1);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
}
@Test
public void findOneWithDefaultKeyAndMultipleApplicationNames() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("myapp-foo", "myapp-bar"));
when(keyValueTemplate.get("yourapp")).thenReturn(withVaultResponse("yourapp-foo", "yourapp-bar"));
when(keyValueTemplate.get("mydefaultkey")).thenReturn(withVaultResponse("def-foo", "def-bar"));
@@ -124,18 +147,21 @@ public class SpringVaultEnvironmentRepositoryTests {
var properties = new VaultEnvironmentProperties();
properties.setDefaultKey("mydefaultkey");
var e = springVaultEnvironmentRepository(properties).findOne("myapp,yourapp", null, null);
var e = springVaultEnvironmentRepository(properties).findOne("myapp,yourapp", null, "lbl");
assertThat(e.getName()).isEqualTo("myapp,yourapp");
assertThat(e.getPropertySources()).hasSize(3);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:yourapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("yourapp-foo", "yourapp-bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("myapp-foo", "myapp-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:mydefaultkey");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void testFindOneDefaultKeySetAndEqualToApplication() {
public void findOneWithDefaultKeySetToApplicationName() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("application")).thenReturn(withVaultResponse("def-foo", "def-bar"));
@@ -147,11 +173,66 @@ public class SpringVaultEnvironmentRepositoryTests {
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(1);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
}
@Test
public void testVaultVersioning() {
public void findOneWithProfile() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("myapp,pr1")).thenReturn(withVaultResponse("foo-pr1", "bar-pr1"));
when(keyValueTemplate.get("application")).thenReturn(withVaultResponse("def-foo", "def-bar"));
when(keyValueTemplate.get("application,pr1")).thenReturn(withVaultResponse("def-pr1-foo", "def-pr1-bar"));
var properties = new VaultEnvironmentProperties();
var e = springVaultEnvironmentRepository(properties).findOne("myapp", "pr1", "lbl");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(4);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,pr1");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo-pr1", "bar-pr1"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,pr1");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-pr1-foo", "def-pr1-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(3).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(3).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithMultipleProfiles() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("myapp,pr1")).thenReturn(withVaultResponse("foo-pr1", "bar-pr1"));
when(keyValueTemplate.get("myapp,pr2")).thenReturn(withVaultResponse("foo-pr2", "bar-pr2"));
when(keyValueTemplate.get("application")).thenReturn(withVaultResponse("def-foo", "def-bar"));
when(keyValueTemplate.get("application,pr1")).thenReturn(withVaultResponse("def-pr1-foo", "def-pr1-bar"));
when(keyValueTemplate.get("application,pr2")).thenReturn(withVaultResponse("def-pr2-foo", "def-pr2-bar"));
var properties = new VaultEnvironmentProperties();
var e = springVaultEnvironmentRepository(properties).findOne("myapp", "pr1,pr2", "lbl");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(6);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,pr2");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo-pr2", "bar-pr2"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,pr2");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-pr2-foo", "def-pr2-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:myapp,pr1");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("foo-pr1", "bar-pr1"));
assertThat(e.getPropertySources().get(3).getName()).isEqualTo("vault:application,pr1");
assertThat(e.getPropertySources().get(3).getSource()).isEqualTo(Map.of("def-pr1-foo", "def-pr1-bar"));
assertThat(e.getPropertySources().get(4).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(4).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(5).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(5).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithVaultVersioning() {
when(keyValueTemplate.get("myapp")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("application")).thenReturn(withVaultResponse("def-foo", "def-bar"));
@@ -163,7 +244,94 @@ public class SpringVaultEnvironmentRepositoryTests {
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithDefaultLabelWhenLabelEnabled() {
when(keyValueTemplate.get("myapp,default,main")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("application,default,main")).thenReturn(withVaultResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
var e = springVaultEnvironmentRepository(properties).findOne("myapp", null, null);
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,default,main");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,default,main");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithProfileAndDefaultLabelWhenLabelEnabled() {
when(keyValueTemplate.get("myapp,default,main")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("myapp,pr1,main")).thenReturn(withVaultResponse("pr1-foo", "pr1-bar"));
when(keyValueTemplate.get("application,default,main")).thenReturn(withVaultResponse("def-foo", "def-bar"));
when(keyValueTemplate.get("application,pr1,main")).thenReturn(withVaultResponse("def-pr1-foo", "def-pr1-bar"));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
var e = springVaultEnvironmentRepository(properties).findOne("myapp", "pr1", null);
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(4);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,pr1,main");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("pr1-foo", "pr1-bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,pr1,main");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-pr1-foo", "def-pr1-bar"));
assertThat(e.getPropertySources().get(2).getName()).isEqualTo("vault:myapp,default,main");
assertThat(e.getPropertySources().get(2).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(3).getName()).isEqualTo("vault:application,default,main");
assertThat(e.getPropertySources().get(3).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithCustomDefaultLabelWhenLabelEnabled() {
when(keyValueTemplate.get("myapp,default,custom")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("application,default,custom")).thenReturn(withVaultResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
properties.setDefaultLabel("custom");
var e = springVaultEnvironmentRepository(properties).findOne("myapp", null, null);
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,default,custom");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,default,custom");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
@Test
public void findOneWithCustomLabelWhenLabelEnabled() {
when(keyValueTemplate.get("myapp,default,my-label")).thenReturn(withVaultResponse("foo", "bar"));
when(keyValueTemplate.get("application,default,my-label")).thenReturn(withVaultResponse("def-foo", "def-bar"));
var properties = new VaultEnvironmentProperties();
properties.setEnableLabel(true);
properties.setDefaultLabel("custom");
var e = springVaultEnvironmentRepository(properties).findOne("myapp", null, "my-label");
assertThat(e.getName()).isEqualTo("myapp");
assertThat(e.getPropertySources()).hasSize(2);
assertThat(e.getPropertySources().get(0).getName()).isEqualTo("vault:myapp,default,my-label");
assertThat(e.getPropertySources().get(0).getSource()).isEqualTo(Map.of("foo", "bar"));
assertThat(e.getPropertySources().get(1).getName()).isEqualTo("vault:application,default,my-label");
assertThat(e.getPropertySources().get(1).getSource()).isEqualTo(Map.of("def-foo", "def-bar"));
}
private SpringVaultEnvironmentRepository springVaultEnvironmentRepository(VaultEnvironmentProperties properties) {