diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/ReactiveVaultClients.java b/spring-vault-core/src/main/java/org/springframework/vault/client/ReactiveVaultClients.java
index 3ce69d0f..250f2b2c 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/client/ReactiveVaultClients.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/client/ReactiveVaultClients.java
@@ -49,12 +49,28 @@ public class ReactiveVaultClients {
*/
public static WebClient createWebClient(VaultEndpoint endpoint,
ClientHttpConnector connector) {
+ return createWebClient(SimpleVaultEndpointProvider.of(endpoint), connector);
+ }
- Assert.notNull(endpoint, "VaultEndpoint must not be null");
+ /**
+ * Create a {@link WebClient} configured with {@link VaultEndpoint} and
+ * {@link ClientHttpConnector}. The client accepts relative URIs without a leading
+ * slash that are expanded to use {@link VaultEndpoint}.
+ *
+ * Requires Jackson 2 for Object-to-JSON mapping.
+ *
+ * @param endpointProvider must not be {@literal null}.
+ * @param connector must not be {@literal null}.
+ * @return the configured {@link WebClient}.
+ */
+ public static WebClient createWebClient(VaultEndpointProvider endpointProvider,
+ ClientHttpConnector connector) {
+
+ Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(connector, "ClientHttpConnector must not be null");
UriBuilderFactory uriBuilderFactory = VaultClients
- .createUriBuilderFactory(endpoint);
+ .createUriBuilderFactory(endpointProvider);
ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(configurer -> {
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/SimpleVaultEndpointProvider.java b/spring-vault-core/src/main/java/org/springframework/vault/client/SimpleVaultEndpointProvider.java
new file mode 100644
index 00000000..ca00a9fb
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/client/SimpleVaultEndpointProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 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.vault.client;
+
+import org.springframework.util.Assert;
+
+/**
+ * {@link VaultEndpointProvider} returning a static {@link VaultEndpoint}.
+ *
+ * @author Mark Paluch
+ * @since 1.1
+ */
+public class SimpleVaultEndpointProvider implements VaultEndpointProvider {
+
+ private final VaultEndpoint endpoint;
+
+ private SimpleVaultEndpointProvider(VaultEndpoint endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ /**
+ * Creates a new {@link VaultEndpointProvider} given {@link VaultEndpoint}.
+ *
+ * @param endpoint must not be {@literal null}.
+ */
+ public static VaultEndpointProvider of(VaultEndpoint endpoint) {
+
+ Assert.notNull(endpoint, "VaultEndpoint must not be null");
+
+ return new SimpleVaultEndpointProvider(endpoint);
+ }
+
+ @Override
+ public VaultEndpoint getVaultEndpoint() {
+ return endpoint;
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/VaultClients.java b/spring-vault-core/src/main/java/org/springframework/vault/client/VaultClients.java
index 21478e8b..50c8260d 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/client/VaultClients.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/client/VaultClients.java
@@ -32,6 +32,8 @@ import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.DefaultUriTemplateHandler;
import org.springframework.web.util.UriBuilder;
import org.springframework.web.util.UriBuilderFactory;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
/**
* Vault Client factory to create {@link RestTemplate} configured to the needs of
@@ -63,11 +65,36 @@ public class VaultClients {
*/
public static RestTemplate createRestTemplate(VaultEndpoint endpoint,
ClientHttpRequestFactory requestFactory) {
+ return createRestTemplate(SimpleVaultEndpointProvider.of(endpoint),
+ requestFactory);
+ }
+
+ /**
+ * Create a {@link RestTemplate} configured with {@link VaultEndpointProvider} and
+ * {@link ClientHttpRequestFactory}. The template accepts relative URIs without a
+ * leading slash that are expanded to use {@link VaultEndpoint}. {@link RestTemplate}
+ * is configured with a {@link ClientHttpRequestInterceptor} to enforce serialization
+ * to a byte array prior continuing the request. Eager serialization leads to a known
+ * request body size that is required to send a
+ * {@link org.springframework.http.HttpHeaders#CONTENT_LENGTH} request header.
+ * Otherwise, Vault will deny body processing.
+ *
+ * Requires Jackson 2 for Object-to-JSON mapping.
+ *
+ * @param endpointProvider must not be {@literal null}.
+ * @param requestFactory must not be {@literal null}.
+ * @return the {@link RestTemplate}.
+ * @see org.springframework.http.client.Netty4ClientHttpRequestFactory
+ * @see MappingJackson2HttpMessageConverter
+ * @since 1.1
+ */
+ public static RestTemplate createRestTemplate(VaultEndpointProvider endpointProvider,
+ ClientHttpRequestFactory requestFactory) {
RestTemplate restTemplate = createRestTemplate();
restTemplate.setRequestFactory(requestFactory);
- restTemplate.setUriTemplateHandler(createUriTemplateHandler(endpoint));
+ restTemplate.setUriTemplateHandler(createUriBuilderFactory(endpointProvider));
return restTemplate;
}
@@ -102,26 +129,29 @@ public class VaultClients {
}
private static DefaultUriTemplateHandler createUriTemplateHandler(
- VaultEndpoint endpoint) {
+ VaultEndpointProvider endpointProvider) {
- String baseUrl = String.format("%s://%s:%s/%s/", endpoint.getScheme(),
- endpoint.getHost(), endpoint.getPort(), "v1");
-
- DefaultUriTemplateHandler defaultUriTemplateHandler = new PrefixAwareUriTemplateHandler();
- defaultUriTemplateHandler.setBaseUrl(baseUrl);
- return defaultUriTemplateHandler;
+ return new PrefixAwareUriTemplateHandler(endpointProvider);
}
- public static UriBuilderFactory createUriBuilderFactory(VaultEndpoint endpoint) {
-
- String baseUrl = String.format("%s://%s:%s/%s/", endpoint.getScheme(),
- endpoint.getHost(), endpoint.getPort(), "v1");
-
- return new PrefixAwareUriBuilderFactory(baseUrl);
+ public static UriBuilderFactory createUriBuilderFactory(
+ VaultEndpointProvider endpointProvider) {
+ return new PrefixAwareUriBuilderFactory(endpointProvider);
}
public static class PrefixAwareUriTemplateHandler extends DefaultUriTemplateHandler {
+ @Nullable
+ private final VaultEndpointProvider endpointProvider;
+
+ public PrefixAwareUriTemplateHandler() {
+ this.endpointProvider = null;
+ }
+
+ public PrefixAwareUriTemplateHandler(VaultEndpointProvider endpointProvider) {
+ this.endpointProvider = endpointProvider;
+ }
+
@Override
protected URI expandInternal(String uriTemplate, Map uriVariables) {
return super.expandInternal(prepareUriTemplate(getBaseUrl(), uriTemplate),
@@ -133,6 +163,18 @@ public class VaultClients {
return super.expandInternal(prepareUriTemplate(getBaseUrl(), uriTemplate),
uriVariables);
}
+
+ @Override
+ public String getBaseUrl() {
+
+ if (endpointProvider != null) {
+
+ VaultEndpoint endpoint = endpointProvider.getVaultEndpoint();
+ return toBaseUri(endpoint);
+ }
+
+ return super.getBaseUrl();
+ }
}
/**
@@ -140,22 +182,34 @@ public class VaultClients {
*/
public static class PrefixAwareUriBuilderFactory extends DefaultUriBuilderFactory {
- private final String baseUri;
+ private final VaultEndpointProvider endpointProvider;
- public PrefixAwareUriBuilderFactory(String baseUri) {
- super(baseUri);
- this.baseUri = baseUri;
+ public PrefixAwareUriBuilderFactory(VaultEndpointProvider endpointProvider) {
+ this.endpointProvider = endpointProvider;
}
@Override
public UriBuilder uriString(String uriTemplate) {
- return super.uriString(prepareUriTemplate(baseUri, uriTemplate));
+
+ VaultEndpoint endpoint = endpointProvider.getVaultEndpoint();
+
+ String baseUri = toBaseUri(endpoint);
+ UriComponents uriComponents = UriComponentsBuilder.fromUriString(
+ prepareUriTemplate(baseUri, uriTemplate)).build();
+
+ return UriComponentsBuilder.fromUriString(baseUri).uriComponents(
+ uriComponents);
}
}
+ private static String toBaseUri(VaultEndpoint endpoint) {
+ return endpoint.getScheme() + "://" + endpoint.getHost() + ":"
+ + endpoint.getPort() + "/v1";
+ }
+
/**
- * Strip/add leading slashes from {@code uriTemplate} depending on wheter the base url
- * has a trailing slash.
+ * Strip/add leading slashes from {@code uriTemplate} depending on wheter the base
+ * url* has a trailing slash.
*
* @param uriTemplate
* @return
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/VaultEndpointProvider.java b/spring-vault-core/src/main/java/org/springframework/vault/client/VaultEndpointProvider.java
new file mode 100644
index 00000000..973145b6
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/client/VaultEndpointProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 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.vault.client;
+
+/**
+ * Component that provides a {@link VaultEndpoint}. Allows to use a different
+ * {@link VaultEndpoint} for each Vault request.
+ *
+ * @author Mark Paluch
+ * @since 1.1
+ */
+public interface VaultEndpointProvider {
+
+ /**
+ * Provides access to {@link VaultEndpoint}.
+ *
+ * @return the {@link VaultEndpoint}.
+ */
+ VaultEndpoint getVaultEndpoint();
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java b/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java
index 20ee52c9..44f9769a 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java
@@ -29,8 +29,10 @@ import org.springframework.util.Assert;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
import org.springframework.vault.authentication.SessionManager;
+import org.springframework.vault.client.SimpleVaultEndpointProvider;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.client.VaultEndpoint;
+import org.springframework.vault.client.VaultEndpointProvider;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.lease.SecretLeaseContainer;
import org.springframework.vault.support.ClientOptions;
@@ -54,6 +56,15 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw
*/
public abstract VaultEndpoint vaultEndpoint();
+ /**
+ * @return a {@link VaultEndpointProvider} returning the value of
+ * {@link #vaultEndpoint()}.
+ * @since 1.1
+ */
+ public VaultEndpointProvider vaultEndpointProvider() {
+ return SimpleVaultEndpointProvider.of(vaultEndpoint());
+ }
+
/**
* Annotate with {@link Bean} in case you want to expose a
* {@link ClientAuthentication} instance to the
@@ -67,14 +78,15 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw
* Create a {@link VaultTemplate}.
*
* @return the {@link VaultTemplate}.
- * @see #vaultEndpoint()
+ * @see #vaultEndpointProvider()
* @see #clientHttpRequestFactoryWrapper()
* @see #sessionManager()
*/
@Bean
public VaultTemplate vaultTemplate() {
- return new VaultTemplate(vaultEndpoint(), clientHttpRequestFactoryWrapper()
- .getClientHttpRequestFactory(), sessionManager());
+ return new VaultTemplate(vaultEndpointProvider(),
+ clientHttpRequestFactoryWrapper().getClientHttpRequestFactory(),
+ sessionManager());
}
/**
@@ -147,11 +159,11 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw
* Construct a {@link RestOperations} object configured for Vault usage.
*
* @return the {@link RestOperations} to be used for Vault access.
- * @see #vaultEndpoint()
+ * @see #vaultEndpointProvider()
* @see #clientHttpRequestFactoryWrapper()
*/
public RestOperations restOperations() {
- return VaultClients.createRestTemplate(vaultEndpoint(),
+ return VaultClients.createRestTemplate(vaultEndpointProvider(),
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory());
}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java
index 5703720e..df0ac143 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java
@@ -31,7 +31,9 @@ import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.VaultTokenSupplier;
import org.springframework.vault.client.ReactiveVaultClients;
+import org.springframework.vault.client.SimpleVaultEndpointProvider;
import org.springframework.vault.client.VaultEndpoint;
+import org.springframework.vault.client.VaultEndpointProvider;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
@@ -69,8 +71,21 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations {
*/
public ReactiveVaultTemplate(VaultEndpoint vaultEndpoint,
ClientHttpConnector connector, VaultTokenSupplier vaultTokenSupplier) {
+ this(SimpleVaultEndpointProvider.of(vaultEndpoint), connector, vaultTokenSupplier);
+ }
- Assert.notNull(vaultEndpoint, "VaultEndpoint must not be null");
+ /**
+ * Create a new {@link ReactiveVaultTemplate} with a {@link VaultEndpointProvider},
+ * {@link ClientHttpConnector} and {@link VaultTokenSupplier}.
+ *
+ * @param endpointProvider must not be {@literal null}.
+ * @param connector must not be {@literal null}.
+ * @param vaultTokenSupplier must not be {@literal null}.
+ */
+ public ReactiveVaultTemplate(VaultEndpointProvider endpointProvider,
+ ClientHttpConnector connector, VaultTokenSupplier vaultTokenSupplier) {
+
+ Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(connector, "ClientHttpConnector must not be null");
Assert.notNull(vaultTokenSupplier, "AuthenticationSupplier must not be null");
@@ -82,10 +97,10 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations {
}).build();
}));
- this.statelessClient = ReactiveVaultClients.createWebClient(vaultEndpoint,
+ this.statelessClient = ReactiveVaultClients.createWebClient(endpointProvider,
connector);
this.sessionClient = ReactiveVaultClients
- .createWebClient(vaultEndpoint, connector).mutate().filter(filter)
+ .createWebClient(endpointProvider, connector).mutate().filter(filter)
.build();
}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java
index e5672633..6a3de550 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java
@@ -32,8 +32,10 @@ import org.springframework.util.Assert;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.SimpleSessionManager;
+import org.springframework.vault.client.SimpleVaultEndpointProvider;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.client.VaultEndpoint;
+import org.springframework.vault.client.VaultEndpointProvider;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
@@ -77,7 +79,10 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
- this.sessionTemplate = createSessionTemplate(vaultEndpoint, requestFactory);
+ VaultEndpointProvider endpointProvider = SimpleVaultEndpointProvider
+ .of(vaultEndpoint);
+
+ this.sessionTemplate = createSessionTemplate(endpointProvider, requestFactory);
this.plainTemplate = VaultClients.createRestTemplate(vaultEndpoint,
requestFactory);
}
@@ -93,8 +98,24 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
public VaultTemplate(VaultEndpoint vaultEndpoint,
ClientHttpRequestFactory clientHttpRequestFactory,
SessionManager sessionManager) {
+ this(SimpleVaultEndpointProvider.of(vaultEndpoint), clientHttpRequestFactory,
+ sessionManager);
+ }
- Assert.notNull(vaultEndpoint, "VaultEndpoint must not be null");
+ /**
+ * Create a new {@link VaultTemplate} with a {@link VaultEndpointProvider},
+ * {@link ClientHttpRequestFactory} and {@link SessionManager}.
+ *
+ * @param endpointProvider must not be {@literal null}.
+ * @param clientHttpRequestFactory must not be {@literal null}.
+ * @param sessionManager must not be {@literal null}.
+ * @since 1.1
+ */
+ public VaultTemplate(VaultEndpointProvider endpointProvider,
+ ClientHttpRequestFactory clientHttpRequestFactory,
+ SessionManager sessionManager) {
+
+ Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(clientHttpRequestFactory,
"ClientHttpRequestFactory must not be null");
Assert.notNull(sessionManager, "SessionManager must not be null");
@@ -102,16 +123,16 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
this.sessionManager = sessionManager;
this.dedicatedSessionManager = false;
- this.sessionTemplate = createSessionTemplate(vaultEndpoint,
+ this.sessionTemplate = createSessionTemplate(endpointProvider,
clientHttpRequestFactory);
- this.plainTemplate = VaultClients.createRestTemplate(vaultEndpoint,
+ this.plainTemplate = VaultClients.createRestTemplate(endpointProvider,
clientHttpRequestFactory);
}
- private RestTemplate createSessionTemplate(VaultEndpoint endpoint,
+ private RestTemplate createSessionTemplate(VaultEndpointProvider endpointProvider,
ClientHttpRequestFactory requestFactory) {
- RestTemplate restTemplate = VaultClients.createRestTemplate(endpoint,
+ RestTemplate restTemplate = VaultClients.createRestTemplate(endpointProvider,
requestFactory);
restTemplate.getInterceptors().add(