From 1653439c71d646b07fd25ae958962f9db3f91a4d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 13 Feb 2020 09:00:44 +0100 Subject: [PATCH] Omit Vault namespace header for sys/init and sys/health endpoints We now no longer add the default namespace header when issuing requests to opsForSys().init()/health(). Closes gh-534. --- .../vault/client/RestTemplateBuilder.java | 39 +++++++++------ .../vault/core/VaultSysTemplate.java | 25 +++++++--- .../client/RestTemplateBuilderUnitTests.java | 16 ------ .../VaultNamespaceSecretIntegrationTests.java | 50 ++++++++++++++++--- 4 files changed, 84 insertions(+), 46 deletions(-) diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateBuilder.java b/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateBuilder.java index 72c6d0fe..0dee40ff 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateBuilder.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateBuilder.java @@ -233,27 +233,42 @@ public class RestTemplateBuilder { protected RestTemplate createTemplate() { ClientHttpRequestFactory requestFactory = this.requestFactory.get(); - RestTemplateBuilderClientHttpRequestFactoryWrapper wrapper = new RestTemplateBuilderClientHttpRequestFactoryWrapper( - requestFactory, new LinkedHashMap<>(defaultHeaders), - new LinkedHashSet<>(requestCustomizers)); - return VaultClients.createRestTemplate(endpointProvider, wrapper); + LinkedHashMap defaultHeaders = new LinkedHashMap<>( + this.defaultHeaders); + LinkedHashSet> requestCustomizers = new LinkedHashSet<>( + this.requestCustomizers); + + RestTemplate restTemplate = VaultClients.createRestTemplate(this.endpointProvider, + new RestTemplateBuilderClientHttpRequestFactoryWrapper(requestFactory, + requestCustomizers)); + + restTemplate.getInterceptors() + .add((httpRequest, bytes, clientHttpRequestExecution) -> { + + HttpHeaders headers = httpRequest.getHeaders(); + defaultHeaders.forEach((key, value) -> { + if (!headers.containsKey(key)) { + headers.add(key, value); + } + }); + + return clientHttpRequestExecution.execute(httpRequest, bytes); + }); + + return restTemplate; } static class RestTemplateBuilderClientHttpRequestFactoryWrapper extends AbstractClientHttpRequestFactoryWrapper { - private final Map defaultHeaders; - private final Set> requestCustomizers; RestTemplateBuilderClientHttpRequestFactoryWrapper( ClientHttpRequestFactory requestFactory, - Map defaultHeaders, Set> requestCustomizers) { super(requestFactory); - this.defaultHeaders = defaultHeaders; this.requestCustomizers = requestCustomizers; } @@ -262,15 +277,7 @@ public class RestTemplateBuilder { ClientHttpRequestFactory requestFactory) throws IOException { ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod); - HttpHeaders headers = request.getHeaders(); - this.defaultHeaders.forEach((key, value) -> { - - if (!headers.containsKey(key)) { - headers.add(key, value); - } - - }); this.requestCustomizers.forEach(it -> it.customize(request)); return request; diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java index 3e7263c3..86305d0a 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java @@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import org.springframework.core.ParameterizedTypeReference; 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; @@ -39,6 +40,7 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.vault.VaultException; +import org.springframework.vault.client.VaultHttpHeaders; import org.springframework.vault.client.VaultResponses; import org.springframework.vault.support.Policy; import org.springframework.vault.support.VaultHealth; @@ -99,15 +101,17 @@ public class VaultSysTemplate implements VaultSysOperations { @SuppressWarnings("unchecked") public boolean isInitialized() { - return requireResponse(vaultOperations.doWithVault(restOperations -> { + return requireResponse(vaultOperations.doWithSession(restOperations -> { try { - Map body = restOperations.getForObject("sys/init", - Map.class); + ResponseEntity> body = (ResponseEntity) restOperations + .exchange("sys/init", HttpMethod.GET, emptyNamespace(null), + Map.class); - Assert.state(body != null, "Initialization response must not be null"); + Assert.state(body.getBody() != null, + "Initialization response must not be null"); - return body.get("initialized"); + return body.getBody().get("initialized"); } catch (HttpStatusCodeException e) { throw VaultResponses.buildException(e); @@ -127,7 +131,7 @@ public class VaultSysTemplate implements VaultSysOperations { try { ResponseEntity exchange = restOperations .exchange("sys/init", HttpMethod.PUT, - new HttpEntity(vaultInitializationRequest), + emptyNamespace(vaultInitializationRequest), VaultInitializationResponseImpl.class); Assert.state(exchange.getBody() != null, @@ -388,6 +392,12 @@ public class VaultSysTemplate implements VaultSysOperations { } } + private static HttpEntity emptyNamespace(@Nullable T body) { + HttpHeaders headers = new HttpHeaders(); + headers.add(VaultHttpHeaders.VAULT_NAMESPACE, ""); + return new HttpEntity<>(body, headers); + } + private static class Health implements RestOperationsCallback { @Override @@ -395,7 +405,8 @@ public class VaultSysTemplate implements VaultSysOperations { try { ResponseEntity healthResponse = restOperations.exchange( - "sys/health", HttpMethod.GET, null, VaultHealthImpl.class); + "sys/health", HttpMethod.GET, emptyNamespace(null), + VaultHealthImpl.class); return healthResponse.getBody(); } catch (RestClientResponseException responseError) { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/client/RestTemplateBuilderUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/client/RestTemplateBuilderUnitTests.java index f4bb8d2b..069d7525 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/client/RestTemplateBuilderUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/client/RestTemplateBuilderUnitTests.java @@ -60,22 +60,6 @@ class RestTemplateBuilderUnitTests { assertThat(restTemplate.getErrorHandler()).isSameAs(errorHandler); } - @Test - void shouldApplyDefaultHeaders() throws IOException { - - RestTemplate restTemplate = RestTemplateBuilder.builder() - .endpoint(VaultEndpoint.create("localhost", 8200)) - .defaultHeader("header", "value").build(); - - restTemplate.getInterceptors().clear(); - - ClientHttpRequest request = restTemplate.getRequestFactory() - .createRequest(URI.create("/"), HttpMethod.GET); - - assertThat(request.getHeaders()).containsEntry("header", - Collections.singletonList("value")); - } - @Test void shouldApplyRequestCustomizers() throws IOException { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultNamespaceSecretIntegrationTests.java b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultNamespaceSecretIntegrationTests.java index 47fbc1de..973b4f91 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultNamespaceSecretIntegrationTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultNamespaceSecretIntegrationTests.java @@ -69,6 +69,12 @@ class VaultNamespaceSecretIntegrationTests extends IntegrationTestSupport { RestTemplateBuilder devRestTemplate; RestTemplateBuilder maketingRestTemplate; + WebClientBuilder marketingWebClientBuilder = WebClientBuilder.builder() + .httpConnector(ClientHttpConnectorFactory.create(new ClientOptions(), + Settings.createSslConfiguration())) + .endpoint(TestRestTemplateFactory.TEST_VAULT_ENDPOINT) + .defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE, "marketing"); + String devToken; String marketingToken; @@ -166,14 +172,9 @@ class VaultNamespaceSecretIntegrationTests extends IntegrationTestSupport { VaultTemplate marketing = new VaultTemplate(maketingRestTemplate, new SimpleSessionManager(new TokenAuthentication(marketingToken))); - WebClientBuilder webClientBuilder = WebClientBuilder.builder() - .httpConnector(ClientHttpConnectorFactory.create(new ClientOptions(), - Settings.createSslConfiguration())) - .endpoint(TestRestTemplateFactory.TEST_VAULT_ENDPOINT) - .defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE, "marketing"); - ReactiveVaultTemplate reactiveMarketing = new ReactiveVaultTemplate( - webClientBuilder, () -> Mono.just(VaultToken.of(marketingToken))); + marketingWebClientBuilder, + () -> Mono.just(VaultToken.of(marketingToken))); marketing.write("marketing-secrets/my-secret", Collections.singletonMap("key", "marketing")); @@ -187,6 +188,41 @@ class VaultNamespaceSecretIntegrationTests extends IntegrationTestSupport { }).verifyComplete(); } + @Test + void shouldReportInitialized() { + + VaultTemplate marketing = new VaultTemplate(maketingRestTemplate, + new SimpleSessionManager(new TokenAuthentication(marketingToken))); + + assertThat(marketing.opsForSys().isInitialized()).isTrue(); + } + + @Test + void shouldReportHealth() { + + VaultTemplate marketing = new VaultTemplate(maketingRestTemplate, + new SimpleSessionManager(new TokenAuthentication(marketingToken))); + + assertThat(marketing.opsForSys().health().isInitialized()).isTrue(); + } + + @Test + void shouldReportReactiveInitialized() { + + ReactiveVaultTemplate reactiveMarketing = new ReactiveVaultTemplate( + marketingWebClientBuilder, + () -> Mono.just(VaultToken.of(marketingToken))); + + reactiveMarketing.doWithSession(webClient -> { + return webClient.get().uri("sys/init") + .header(VaultHttpHeaders.VAULT_NAMESPACE, "").exchange() + .flatMap(it -> it.bodyToMono(Map.class)); + }).as(StepVerifier::create) + .assertNext( + actual -> assertThat(actual).containsEntry("initialized", true)) + .verifyComplete(); + } + @Configuration static class NamespaceConfiguration extends AbstractVaultConfiguration {