From e6b2d9eec083b170adefe4e6cb0ac55dedff0f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haroun=20Pacqu=C3=A9e?= Date: Mon, 4 Jun 2018 23:13:27 +0200 Subject: [PATCH] Added support for Vault's 0.10.0+ versioning enabled kv engine. (#1018) --- .../main/asciidoc/spring-cloud-config.adoc | 5 + .../VaultEnvironmentProperties.java | 13 +++ .../VaultEnvironmentRepository.java | 102 ++---------------- .../environment/VaultKvAccessStrategy.java | 90 ++++++++++++++++ .../VaultKvAccessStrategyFactory.java | 93 ++++++++++++++++ .../VaultKvAccessStrategySupport.java | 79 ++++++++++++++ .../VaultEnvironmentRepositoryTests.java | 86 +++++++++++++-- .../VaultKvAccessStrategyFactoryTest.java | 48 +++++++++ .../VaultKvAccessStrategyTest.java | 86 +++++++++++++++ 9 files changed, 502 insertions(+), 100 deletions(-) create mode 100644 spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategy.java create mode 100644 spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactory.java create mode 100644 spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategySupport.java create mode 100644 spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactoryTest.java create mode 100644 spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyTest.java diff --git a/docs/src/main/asciidoc/spring-cloud-config.adoc b/docs/src/main/asciidoc/spring-cloud-config.adoc index b820cb2a..edfd4773 100644 --- a/docs/src/main/asciidoc/spring-cloud-config.adoc +++ b/docs/src/main/asciidoc/spring-cloud-config.adoc @@ -546,12 +546,17 @@ The following table describes configurable Vault properties: |profileSeparator |, +|kvVersion +|1 + |=== IMPORTANT: All of the properties in the preceding table must be prefixed with `spring.cloud.config.server.vault`. All configurable properties can be found in `org.springframework.cloud.config.server.environment.VaultEnvironmentRepository`. +Vault 0.10.0 introduced a versioned key-value backend (k/v backend version 2) that exposes a different API than earlier versions, it now requires a `data/` between the mount path and the actual context path and wraps secrets in a `data` object. Setting `kvVersion=2` will take this into account. + With your config server running, you can make HTTP requests to the server to retrieve values from the Vault backend. To do so, you need a token for your Vault server. diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java index 76dda1fc..cacb87fc 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentProperties.java @@ -25,6 +25,7 @@ import org.springframework.core.Ordered; /** * @author Dylan Roberts + * @author Haroun Pacquee */ @ConfigurationProperties("spring.cloud.config.server.vault") public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProperties { @@ -52,6 +53,10 @@ public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProp private Map proxy = new HashMap<>(); private int order = Ordered.LOWEST_PRECEDENCE; + /** + * Value to indicate which version of Vault kv backend is used. Defaults to 1. + */ + private int kvVersion = 1; public String getHost() { return host; @@ -127,4 +132,12 @@ public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProp public void setOrder(int order) { this.order = order; } + + public int getKvVersion() { + return kvVersion; + } + + public void setKvVersion(int kvVersion) { + this.kvVersion = kvVersion; + } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java index d8d53ae6..3ebe01dd 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java @@ -26,25 +26,15 @@ import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRawValue; -import com.fasterxml.jackson.databind.JsonNode; - import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; 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.http.HttpEntity; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; -import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER; @@ -53,6 +43,7 @@ import static org.springframework.cloud.config.client.ConfigClientProperties.TOK /** * @author Spencer Gibb * @author Mark Paluch + * @author Haroun Pacquee */ @Validated public class VaultEnvironmentRepository implements EnvironmentRepository, Ordered { @@ -84,18 +75,18 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere private int order; - private RestTemplate rest; + private VaultKvAccessStrategy accessStrategy; // TODO: move to watchState:String on findOne? private ObjectProvider request; private EnvironmentWatch watch; - public VaultEnvironmentRepository(ObjectProvider request, EnvironmentWatch watch, RestTemplate rest, - VaultEnvironmentProperties properties) { + public VaultEnvironmentRepository(ObjectProvider request, + EnvironmentWatch watch, RestTemplate rest, + VaultEnvironmentProperties properties) { this.request = request; this.watch = watch; - this.rest = rest; this.backend = properties.getBackend(); this.defaultKey = properties.getDefaultKey(); this.host = properties.getHost(); @@ -103,6 +94,11 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere this.port = properties.getPort(); this.profileSeparator = properties.getProfileSeparator(); this.scheme = properties.getScheme(); + + String baseUrl = String.format("%s://%s:%s", this.scheme, this.host, this.port); + + this.accessStrategy = VaultKvAccessStrategyFactory.forVersion(rest, baseUrl, + properties.getKvVersion()); } @Override @@ -172,7 +168,6 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere } String read(HttpServletRequest servletRequest, String key) { - String url = String.format("%s://%s:%s/v1/{backend}/{key}", this.scheme, this.host, this.port); HttpHeaders headers = new HttpHeaders(); @@ -181,22 +176,7 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere throw new IllegalArgumentException("Missing required header: " + TOKEN_HEADER); } headers.add(VAULT_TOKEN, token); - try { - ResponseEntity response = this.rest.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), - VaultResponse.class, this.backend, key); - - HttpStatus status = response.getStatusCode(); - if (status == HttpStatus.OK) { - return response.getBody().getData(); - } - } catch (HttpStatusCodeException e) { - if (e.getStatusCode() == HttpStatus.NOT_FOUND) { - return null; - } - throw e; - } - - return null; + return accessStrategy.getData(headers, backend, key); } public void setHost(String host) { @@ -231,64 +211,4 @@ public class VaultEnvironmentRepository implements EnvironmentRepository, Ordere public int getOrder() { return order; } - - @JsonIgnoreProperties(ignoreUnknown = true) - static class VaultResponse { - - private String auth; - - private Object data; - - @JsonProperty("lease_duration") - private long leaseDuration; - - @JsonProperty("lease_id") - private String leaseId; - - private boolean renewable; - - public VaultResponse() { - } - - public String getAuth() { - return auth; - } - - public void setAuth(String auth) { - this.auth = auth; - } - - @JsonRawValue - public String getData() { - return data == null ? null : data.toString(); - } - - public void setData(JsonNode data) { - this.data = data; - } - - public long getLeaseDuration() { - return leaseDuration; - } - - public void setLeaseDuration(long leaseDuration) { - this.leaseDuration = leaseDuration; - } - - public String getLeaseId() { - return leaseId; - } - - public void setLeaseId(String leaseId) { - this.leaseId = leaseId; - } - - public boolean isRenewable() { - return renewable; - } - - public void setRenewable(boolean renewable) { - this.renewable = renewable; - } - } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategy.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategy.java new file mode 100644 index 00000000..177aaaf6 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategy.java @@ -0,0 +1,90 @@ +package org.springframework.cloud.config.server.environment; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; + +import org.springframework.http.HttpHeaders; +import org.springframework.web.client.RestClientException; + +/** + * Strategy interface to obtain secrets from Vault's key-value secret backend. + * @author Haroun Pacquee + * @author Mark Paluch + * @since 2.0 + */ +@FunctionalInterface +public interface VaultKvAccessStrategy { + + /** + * Return secrets from Vault. The response is represented as JSON object marshaled to + * {@link String}. + * @param headers must not be {@literal null}. + * @param backend secret backend mount path, must not be {@literal null}. + * @param key key within the key-value secret backend, must not be {@literal null}. + * @return the marshaled JSON object or {@literal null} if the key was not found. + * @throws RestClientException in case of a transport/access failure. + * @see com.fasterxml.jackson.annotation.JsonRawValue + */ + String getData(HttpHeaders headers, String backend, String key) + throws RestClientException; + + @JsonIgnoreProperties(ignoreUnknown = true) + class VaultResponse { + + private String auth; + + private Object data; + + @JsonProperty("lease_duration") + private long leaseDuration; + + @JsonProperty("lease_id") + private String leaseId; + + private boolean renewable; + + public VaultResponse() { + } + + public String getAuth() { + return auth; + } + + public void setAuth(String auth) { + this.auth = auth; + } + + public Object getData() { + return data; + } + + public void setData(JsonNode data) { + this.data = data; + } + + public long getLeaseDuration() { + return leaseDuration; + } + + public void setLeaseDuration(long leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public String getLeaseId() { + return leaseId; + } + + public void setLeaseId(String leaseId) { + this.leaseId = leaseId; + } + + public boolean isRenewable() { + return renewable; + } + + public void setRenewable(boolean renewable) { + this.renewable = renewable; + } + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactory.java new file mode 100644 index 00000000..1cb9679e --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactory.java @@ -0,0 +1,93 @@ +/* + * Copyright 2018 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.config.server.environment; + +import com.fasterxml.jackson.databind.JsonNode; + +import org.springframework.web.client.RestOperations; + +/** + * Factory for {@link VaultKvAccessStrategy}. + * @author Haroun Pacquee + * @author Mark Paluch + * @since 2.0 + */ +public class VaultKvAccessStrategyFactory { + + /** + * Create a new {@link VaultKvAccessStrategy} given {@link RestOperations}, + * {@code baseUrl}, and {@code version}. + * @param rest must not be {@literal null}. + * @param baseUrl the Vault base URL. + * @param version version of the Vault key-value backend. + * @return the access strategy. + */ + public static VaultKvAccessStrategy forVersion(RestOperations rest, String baseUrl, + int version) { + + switch (version) { + case 1: + return new V1VaultKvAccessStrategy(baseUrl, rest); + case 2: + return new V2VaultKvAccessStrategy(baseUrl, rest); + default: + throw new IllegalArgumentException( + "No support for given Vault k/v backend version " + version); + } + } + + /** + * Strategy for the key-value backend API version 1. + */ + static class V1VaultKvAccessStrategy extends VaultKvAccessStrategySupport { + + V1VaultKvAccessStrategy(String baseUrl, RestOperations rest) { + super(baseUrl, rest); + } + + @Override + public String getPath() { + return "/v1/{backend}/{key}"; + } + + @Override + public String extractDataFromBody(VaultResponse body) { + return body.getData() == null ? null : body.getData().toString(); + } + } + + /** + * Strategy for the key-value backend API version 2. + */ + static class V2VaultKvAccessStrategy extends VaultKvAccessStrategySupport { + + V2VaultKvAccessStrategy(String baseUrl, RestOperations rest) { + super(baseUrl, rest); + } + + @Override + public String getPath() { + return "/v1/{backend}/data/{key}"; + } + + @Override + public String extractDataFromBody(VaultResponse body) { + JsonNode nestedDataNode = body.getData() == null ? null + : ((JsonNode) body.getData()).get("data"); + return nestedDataNode == null ? null : nestedDataNode.toString(); + } + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategySupport.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategySupport.java new file mode 100644 index 00000000..692d4c87 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategySupport.java @@ -0,0 +1,79 @@ +/* + * Copyright 2018 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.config.server.environment; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestOperations; + +/** + * Base class for {@link VaultKvAccessStrategy} implementors. + * @author Mark Paluch + * @since 2.0 + */ +abstract class VaultKvAccessStrategySupport implements VaultKvAccessStrategy { + + private final String baseUrl; + private final RestOperations rest; + + VaultKvAccessStrategySupport(String baseUrl, RestOperations rest) { + this.baseUrl = baseUrl; + this.rest = rest; + } + + /** + * @return the context path to append to the {@code baseUrl}. + */ + abstract String getPath(); + + /** + * Extract the raw JSON from the + * {@link org.springframework.cloud.config.server.environment.VaultKvAccessStrategy.VaultResponse}. + * @param body + * @return + */ + abstract String extractDataFromBody(VaultResponse body); + + /** + * @param headers must not be {@literal null}. + * @param backend secret backend mount path, must not be {@literal null}. + * @param key key within the key-value secret backend, must not be {@literal null}. + * @return + */ + @Override + public String getData(HttpHeaders headers, String backend, String key) { + try { + ResponseEntity response = rest.exchange(baseUrl + getPath(), + HttpMethod.GET, new HttpEntity<>(headers), VaultResponse.class, + backend, key); + HttpStatus status = response.getStatusCode(); + if (status == HttpStatus.OK) { + return extractDataFromBody(response.getBody()); + } + } + catch (HttpStatusCodeException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + return null; + } + throw e; + } + return null; + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java index 703e1ec1..bffb0abf 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepositoryTests.java @@ -1,16 +1,26 @@ +/* + * Copyright 2018 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.config.server.environment; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; - +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Test; - import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.config.environment.Environment; -import org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.VaultResponse; +import org.springframework.cloud.config.server.environment.VaultKvAccessStrategy.VaultResponse; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -18,7 +28,12 @@ import org.springframework.http.ResponseEntity; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.client.RestTemplate; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -27,11 +42,16 @@ import static org.mockito.Mockito.when; /** * @author Spencer Gibb * @author Ryan Baxter + * @author Haroun Pacquee */ public class VaultEnvironmentRepositoryTests { - @Before - public void init() {} + private ObjectMapper objectMapper; + + @Before + public void init() { + objectMapper = new ObjectMapper(); + } @SuppressWarnings("unchecked") private ObjectProvider mockProvide(HttpServletRequest request) { @@ -199,4 +219,52 @@ public class VaultEnvironmentRepositoryTests { new EnvironmentWatch.Default(), rest, new VaultEnvironmentProperties()); repo.findOne("myapp", null, null); } + + @Test + @SuppressWarnings("unchecked") + public void testVaultVersioning() { + MockHttpServletRequest configRequest = new MockHttpServletRequest(); + configRequest.addHeader("X-CONFIG-TOKEN", "mytoken"); + + RestTemplate rest = mock(RestTemplate.class); + + ResponseEntity myAppResp = mock(ResponseEntity.class); + when(myAppResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse myAppVaultResp = getVaultResponse("{\"data\": {\"data\": {\"foo\": \"bar\"}}}"); + when(myAppResp.getBody()).thenReturn(myAppVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/data/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("myapp"))).thenReturn(myAppResp); + + ResponseEntity appResp = mock(ResponseEntity.class); + when(appResp.getStatusCode()).thenReturn(HttpStatus.OK); + VaultResponse appVaultResp = getVaultResponse("{\"data\": {\"data\": {\"def-foo\":\"def-bar\"}}}"); + when(appResp.getBody()).thenReturn(appVaultResp); + when(rest.exchange(eq("http://127.0.0.1:8200/v1/{backend}/data/{key}"), + eq(HttpMethod.GET), any(HttpEntity.class), eq(VaultResponse.class), + eq("secret"), eq("application"))).thenReturn(appResp); + + final VaultEnvironmentProperties vaultEnvironmentProperties = new VaultEnvironmentProperties(); + vaultEnvironmentProperties.setKvVersion(2); + VaultEnvironmentRepository repo = new VaultEnvironmentRepository(mockProvide(configRequest), + new EnvironmentWatch.Default(), rest, vaultEnvironmentProperties); + + Environment e = repo.findOne("myapp", null, null); + assertEquals("Name should be the same as the application argument", "myapp", e.getName()); + assertEquals("Properties for specified application and default application with key 'application' should be returned", + 2, e.getPropertySources().size()); + Map firstResult = new HashMap(); + firstResult.put("foo", "bar"); + assertEquals("Properties for specified application should be returned in priority position", + firstResult, e.getPropertySources().get(0).getSource()); + } + + private VaultResponse getVaultResponse(String json) { + try { + return objectMapper.readValue(json, VaultResponse.class); + } catch (Exception e) { + fail(e.getMessage()); + } + return null; + } } diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactoryTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactoryTest.java new file mode 100644 index 00000000..faa64605 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyFactoryTest.java @@ -0,0 +1,48 @@ +/* + * Copyright 2018 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.config.server.environment; + +import org.junit.Test; + +import org.springframework.cloud.config.server.environment.VaultKvAccessStrategyFactory.V1VaultKvAccessStrategy; +import org.springframework.cloud.config.server.environment.VaultKvAccessStrategyFactory.V2VaultKvAccessStrategy; + +import static org.junit.Assert.*; + +/** + * @author Haroun Pacquee + */ +public class VaultKvAccessStrategyFactoryTest { + + @Test + public void testGetV1Strategy() { + VaultKvAccessStrategy vaultKvAccessStrategy = VaultKvAccessStrategyFactory + .forVersion(null, "foo", 1); + assertTrue(vaultKvAccessStrategy instanceof V1VaultKvAccessStrategy); + } + + @Test + public void testGetV2Strategy() { + VaultKvAccessStrategy vaultKvAccessStrategy = VaultKvAccessStrategyFactory + .forVersion(null, "foo", 2); + assertTrue(vaultKvAccessStrategy instanceof V2VaultKvAccessStrategy); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetUnsupportedStrategy() { + VaultKvAccessStrategyFactory.forVersion(null, "foo", 0); + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyTest.java new file mode 100644 index 00000000..93e73411 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/VaultKvAccessStrategyTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2018 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.config.server.environment; + +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +import org.springframework.cloud.config.server.environment.VaultKvAccessStrategy.VaultResponse; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +/** + * @author Haroun Pacquee + */ +public class VaultKvAccessStrategyTest { + + private ObjectMapper objectMapper = new ObjectMapper(); + + private static final String FOO_BAR = "{\"foo\":\"bar\"}"; + + @Test + public void testV1ExtractFromBody() { + String json = "{\"data\": {\"foo\": \"bar\"}}"; + String s = getStrategy(1).extractDataFromBody(getVaultResponse(json)); + assertThat(s, is(FOO_BAR)); + } + + @Test + public void testV1ExtractFromBodyNoData() { + String json = "{}"; + String s = getStrategy(1).extractDataFromBody(getVaultResponse(json)); + assertNull(s); + } + + @Test + public void testV2ExtractFromBody() { + String json = "{\"data\": {\"data\": {\"foo\": \"bar\"}}}"; + String s = getStrategy(2).extractDataFromBody(getVaultResponse(json)); + assertThat(s, is(FOO_BAR)); + } + + @Test + public void testV2ExtractFromBodyEmptyNestedData() { + String json = "{\"data\": {}}"; + String s = getStrategy(2).extractDataFromBody(getVaultResponse(json)); + assertNull(s); + } + + @Test + public void testV2ExtractFromBodyNoData() { + String json = "{}"; + String s = getStrategy(2).extractDataFromBody(getVaultResponse(json)); + assertNull(s); + } + + private VaultResponse getVaultResponse(String json) { + try { + return objectMapper.readValue(json, VaultResponse.class); + } + catch (IOException e) { + throw new UndeclaredThrowableException(e); + } + } + + private static VaultKvAccessStrategySupport getStrategy(int version) { + return (VaultKvAccessStrategySupport) VaultKvAccessStrategyFactory + .forVersion(null, "foo", version); + } +}