From 30378c8761b96d449a8db5350d186e1d84d197ef Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 2 Dec 2019 09:54:53 +0100 Subject: [PATCH] Fix versioned key-value access with disabled lifecycle support Versioned key-value secrets are now loaded properly when lifecycle support is disabled. We're using our own mechanism to read secrets instead of using Spring Vault's property source so we need to inspect whether the mount is a key-value v1 or v2 one to load its value properly. Closes gh-372. --- .../vault/config/VaultConfigTemplate.java | 19 ++++- .../VaultConfigTemplateIntegrationTests.java | 79 +++++++++++++++++++ .../VaultPropertySourceIntegrationTests.java | 24 ++++++ 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultConfigTemplateIntegrationTests.java diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java index e4946467..a94a7ce7 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 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. @@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.util.Assert; import org.springframework.vault.VaultException; import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.core.util.KeyValueDelegate; import org.springframework.vault.core.util.PropertyTransformer; import org.springframework.vault.support.JsonMapFlattener; import org.springframework.vault.support.VaultResponse; @@ -42,6 +43,8 @@ public class VaultConfigTemplate implements VaultConfigOperations { private final VaultProperties properties; + private final KeyValueDelegate keyValueDelegate; + /** * Create a new {@link VaultConfigTemplate} given {@link VaultOperations}. * @param vaultOperations must not be {@literal null}. @@ -55,6 +58,7 @@ public class VaultConfigTemplate implements VaultConfigOperations { this.vaultOperations = vaultOperations; this.properties = properties; + this.keyValueDelegate = new KeyValueDelegate(vaultOperations); } @Override @@ -66,8 +70,17 @@ public class VaultConfigTemplate implements VaultConfigOperations { secretBackendMetadata.getPath())); try { - VaultResponse vaultResponse = this.vaultOperations - .read(secretBackendMetadata.getPath()); + + VaultResponse vaultResponse; + + if (this.keyValueDelegate.isVersioned(secretBackendMetadata.getPath())) { + vaultResponse = this.keyValueDelegate + .getSecret(secretBackendMetadata.getPath()); + } + else { + vaultResponse = this.vaultOperations + .read(secretBackendMetadata.getPath()); + } if (vaultResponse == null) { diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultConfigTemplateIntegrationTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultConfigTemplateIntegrationTests.java new file mode 100644 index 00000000..805dbd7a --- /dev/null +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultConfigTemplateIntegrationTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2018-2019 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 + * + * https://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.Collections; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.cloud.vault.util.IntegrationTestSupport; +import org.springframework.cloud.vault.util.Settings; +import org.springframework.cloud.vault.util.Version; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assume.assumeTrue; + +/** + * Integration tests for {@link VaultConfigTemplate}. + * + * @author Mark Paluch + */ +public class VaultConfigTemplateIntegrationTests extends IntegrationTestSupport { + + @Before + public void before() { + prepare().getVaultOperations().write("secret/myapp", + Collections.singletonMap("key", "value")); + } + + @Test + public void shouldReadValue() { + + VaultProperties vaultProperties = Settings.createVaultProperties(); + + VaultConfigTemplate template = new VaultConfigTemplate( + prepare().getVaultOperations(), vaultProperties); + + Secrets secrets = template + .read(GenericSecretBackendMetadata.create("secret", "myapp")); + + assertThat(secrets.getData()).containsEntry("key", "value"); + } + + @Test + public void shouldReadVersionedValue() { + + assumeTrue(this.vaultRule.prepare().getVersion() + .isGreaterThanOrEqualTo(Version.parse("0.10.0"))); + + this.vaultRule.prepare().getVaultOperations().write("versioned/data/testVaultApp", + Collections.singletonMap("data", + Collections.singletonMap("key", "value"))); + + VaultProperties vaultProperties = Settings.createVaultProperties(); + + VaultConfigTemplate template = new VaultConfigTemplate( + prepare().getVaultOperations(), vaultProperties); + + Secrets secrets = template + .read(GenericSecretBackendMetadata.create("versioned", "testVaultApp")); + + assertThat(secrets.getData()).containsEntry("key", "value"); + } + +} diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java index f2f61fdd..0de86ae5 100644 --- a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultPropertySourceIntegrationTests.java @@ -23,8 +23,10 @@ import org.junit.Test; import org.springframework.cloud.vault.util.IntegrationTestSupport; import org.springframework.cloud.vault.util.Settings; +import org.springframework.cloud.vault.util.Version; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assume.assumeTrue; /** * Integration tests for {@link VaultPropertySource}. @@ -54,4 +56,26 @@ public class VaultPropertySourceIntegrationTests extends IntegrationTestSupport assertThat(propertySource.getProperty("key")).isEqualTo("value"); } + @Test + public void shouldReadVersionedValue() { + + assumeTrue(this.vaultRule.prepare().getVersion() + .isGreaterThanOrEqualTo(Version.parse("0.10.0"))); + + this.vaultRule.prepare().getVaultOperations().write("versioned/data/testVaultApp", + Collections.singletonMap("data", + Collections.singletonMap("key", "value"))); + + VaultProperties vaultProperties = Settings.createVaultProperties(); + + VaultPropertySource propertySource = new VaultPropertySource( + new VaultConfigTemplate(prepare().getVaultOperations(), vaultProperties), + false, GenericSecretBackendMetadata.create("versioned", "testVaultApp")); + + propertySource.init(); + + assertThat(propertySource.getPropertyNames()).contains("key"); + assertThat(propertySource.getProperty("key")).isEqualTo("value"); + } + }