Introduce VaultEndpointProvider to allow usage of multiple endpoints within an application.

Closes gh-113.
This commit is contained in:
Mark Paluch
2017-07-13 22:28:27 +02:00
parent f53b2f98dc
commit 87bb19fc82
5 changed files with 179 additions and 19 deletions

View File

@@ -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;
}
}

View File

@@ -63,11 +63,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.
* <p>
* 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(createUriTemplateHandler(endpointProvider));
return restTemplate;
}
@@ -109,18 +134,23 @@ 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 class PrefixAwareUriTemplateHandler extends DefaultUriTemplateHandler {
private final VaultEndpointProvider endpointProvider;
public PrefixAwareUriTemplateHandler() {
this.endpointProvider = null;
}
public PrefixAwareUriTemplateHandler(VaultEndpointProvider endpointProvider) {
this.endpointProvider = endpointProvider;
}
@Override
protected URI expandInternal(String uriTemplate, Map<String, ?> uriVariables) {
return super.expandInternal(prepareUriTemplate(uriTemplate), uriVariables);
@@ -131,6 +161,20 @@ public class VaultClients {
return super.expandInternal(prepareUriTemplate(uriTemplate), uriVariables);
}
@Override
public String getBaseUrl() {
if (endpointProvider != null) {
VaultEndpoint endpoint = endpointProvider.getVaultEndpoint();
return endpoint.getScheme() + "://" + endpoint.getHost() + ":"
+ endpoint.getPort() + "/v1";
}
return super.getBaseUrl();
}
/**
* Strip/add leading slashes from {@code uriTemplate} depending on whetner the
* base url has a trailing slash.

View File

@@ -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();
}

View File

@@ -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());
}

View File

@@ -36,8 +36,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;
@@ -89,7 +91,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);
}
@@ -105,8 +110,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");
@@ -114,16 +135,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(new ClientHttpRequestInterceptor() {