From 087d665c391933010f6f245fb13627ab1d9bc345 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 28 Feb 2018 15:17:52 +0100 Subject: [PATCH] Introduce template methods to customize RestTemplate. We now provide template methods on VaultTemplate and ReactiveVaultTemplate to customize RestTemplate respective WebClient. Closes gh-200. --- .../config/AbstractVaultConfiguration.java | 3 +- .../vault/core/ReactiveVaultOperations.java | 1 + .../vault/core/ReactiveVaultTemplate.java | 53 +++++++++++++-- .../vault/core/VaultTemplate.java | 68 ++++++++++++++----- 4 files changed, 102 insertions(+), 23 deletions(-) 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 ecaac3e1..ca0fa229 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 @@ -157,7 +157,8 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw } /** - * Construct a {@link RestOperations} object configured for Vault usage. + * Construct a {@link RestOperations} object configured for Vault session management + * and authentication usage. * * @return the {@link RestOperations} to be used for Vault access. * @see #vaultEndpointProvider() diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java index c4532b60..64c60754 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java @@ -39,6 +39,7 @@ import org.springframework.web.reactive.function.client.WebClientException; * session}. * * @author Mark Paluch + * @since 2.0 * @see ReactiveVaultOperations#doWithSession(Function) * @see ReactiveVaultOperations#doWithVault(Function) * @see WebClient 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 3bc5ea24..d7ded0c5 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 @@ -56,6 +56,7 @@ import static org.springframework.web.reactive.function.client.ExchangeFilterFun * * @author Mark Paluch * @see SessionManager + * @since 2.0 */ public class ReactiveVaultTemplate implements ReactiveVaultOperations { @@ -63,6 +64,8 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations { private final WebClient sessionClient; + private final VaultTokenSupplier vaultTokenSupplier; + /** * Create a new {@link ReactiveVaultTemplate} with a {@link VaultEndpoint}, * {@link ClientHttpConnector} and {@link VaultTokenSupplier}. @@ -91,6 +94,51 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations { Assert.notNull(connector, "ClientHttpConnector must not be null"); Assert.notNull(vaultTokenSupplier, "AuthenticationSupplier must not be null"); + this.vaultTokenSupplier = vaultTokenSupplier; + this.statelessClient = doCreateWebClient(endpointProvider, connector); + this.sessionClient = doCreateSessionWebClient(endpointProvider, connector); + } + + /** + * Create a {@link WebClient} to be used by {@link ReactiveVaultTemplate} for Vault + * communication given {@link VaultEndpointProvider} and {@link ClientHttpConnector}. + * {@link VaultEndpointProvider} is used to contribute host and port details for + * relative URLs typically used by the Template API. Subclasses may override this + * method to customize the {@link WebClient}. + * + * @param endpointProvider must not be {@literal null}. + * @param connector must not be {@literal null}. + * @return the {@link WebClient} used for Vault communication. + * @since 2.1 + */ + protected WebClient doCreateWebClient(VaultEndpointProvider endpointProvider, + ClientHttpConnector connector) { + + Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null"); + Assert.notNull(connector, "ClientHttpConnector must not be null"); + + return ReactiveVaultClients.createWebClient(endpointProvider, connector); + } + + /** + * Create a session-bound {@link WebClient} to be used by {@link VaultTemplate} for + * Vault communication given {@link VaultEndpointProvider} and + * {@link ClientHttpConnector} for calls that require an authenticated context. + * {@link VaultEndpointProvider} is used to contribute host and port details for + * relative URLs typically used by the Template API. Subclasses may override this + * method to customize the {@link WebClient}. + * + * @param endpointProvider must not be {@literal null}. + * @param connector must not be {@literal null}. + * @return the {@link WebClient} used for Vault communication. + * @since 2.1 + */ + protected WebClient doCreateSessionWebClient(VaultEndpointProvider endpointProvider, + ClientHttpConnector connector) { + + Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null"); + Assert.notNull(connector, "ClientHttpConnector must not be null"); + ExchangeFilterFunction filter = ofRequestProcessor(request -> vaultTokenSupplier .getVaultToken().map(token -> { @@ -99,10 +147,7 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations { }).build(); })); - this.statelessClient = ReactiveVaultClients.createWebClient(endpointProvider, - connector); - this.sessionClient = ReactiveVaultClients - .createWebClient(endpointProvider, connector).mutate().filter(filter) + return doCreateWebClient(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 2bb55531..99d9ae32 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 @@ -52,9 +52,9 @@ import org.springframework.web.client.RestTemplate; */ public class VaultTemplate implements InitializingBean, VaultOperations, DisposableBean { - private final RestTemplate sessionTemplate; + private final RestTemplate statelessTemplate; - private final RestTemplate plainTemplate; + private final RestTemplate sessionTemplate; @Nullable private SessionManager sessionManager; @@ -82,9 +82,8 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa VaultEndpointProvider endpointProvider = SimpleVaultEndpointProvider .of(vaultEndpoint); - this.sessionTemplate = createSessionTemplate(endpointProvider, requestFactory); - this.plainTemplate = VaultClients.createRestTemplate(vaultEndpoint, - requestFactory); + this.statelessTemplate = doCreateRestTemplate(endpointProvider, requestFactory); + this.sessionTemplate = doCreateSessionTemplate(endpointProvider, requestFactory); } /** @@ -107,33 +106,66 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa * {@link ClientHttpRequestFactory} and {@link SessionManager}. * * @param endpointProvider must not be {@literal null}. - * @param clientHttpRequestFactory must not be {@literal null}. + * @param requestFactory must not be {@literal null}. * @param sessionManager must not be {@literal null}. * @since 1.1 */ public VaultTemplate(VaultEndpointProvider endpointProvider, - ClientHttpRequestFactory clientHttpRequestFactory, - SessionManager sessionManager) { + ClientHttpRequestFactory requestFactory, SessionManager sessionManager) { Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null"); - Assert.notNull(clientHttpRequestFactory, - "ClientHttpRequestFactory must not be null"); + Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null"); Assert.notNull(sessionManager, "SessionManager must not be null"); this.sessionManager = sessionManager; this.dedicatedSessionManager = false; - this.sessionTemplate = createSessionTemplate(endpointProvider, - clientHttpRequestFactory); - this.plainTemplate = VaultClients.createRestTemplate(endpointProvider, - clientHttpRequestFactory); + this.statelessTemplate = doCreateRestTemplate(endpointProvider, requestFactory); + this.sessionTemplate = doCreateSessionTemplate(endpointProvider, requestFactory); } - private RestTemplate createSessionTemplate(VaultEndpointProvider endpointProvider, + /** + * Create a {@link RestTemplate} to be used by {@link VaultTemplate} for Vault + * communication given {@link VaultEndpointProvider} and + * {@link ClientHttpRequestFactory}. {@link VaultEndpointProvider} is used to + * contribute host and port details for relative URLs typically used by the Template + * API. Subclasses may override this method to customize the {@link RestTemplate}. + * + * @param endpointProvider must not be {@literal null}. + * @param requestFactory must not be {@literal null}. + * @return the {@link RestTemplate} used for Vault communication. + * @since 2.1 + */ + protected RestTemplate doCreateRestTemplate(VaultEndpointProvider endpointProvider, ClientHttpRequestFactory requestFactory) { - RestTemplate restTemplate = VaultClients.createRestTemplate(endpointProvider, - requestFactory); + Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null"); + Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null"); + + return VaultClients.createRestTemplate(endpointProvider, requestFactory); + } + + /** + * Create a session-bound {@link RestTemplate} to be used by {@link VaultTemplate} for + * Vault communication given {@link VaultEndpointProvider} and + * {@link ClientHttpRequestFactory} for calls that require an authenticated context. + * {@link VaultEndpointProvider} is used to contribute host and port details for + * relative URLs typically used by the Template API. Subclasses may override this + * method to customize the {@link RestTemplate}. + * + * @param endpointProvider must not be {@literal null}. + * @param requestFactory must not be {@literal null}. + * @return the {@link RestTemplate} used for Vault communication. + * @since 2.1 + */ + protected RestTemplate doCreateSessionTemplate( + VaultEndpointProvider endpointProvider, + ClientHttpRequestFactory requestFactory) { + + Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null"); + Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null"); + + RestTemplate restTemplate = doCreateRestTemplate(endpointProvider, requestFactory); restTemplate.getInterceptors().add( (request, body, execution) -> { @@ -291,7 +323,7 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa Assert.notNull(clientCallback, "Client callback must not be null"); try { - return clientCallback.doWithRestOperations(plainTemplate); + return clientCallback.doWithRestOperations(statelessTemplate); } catch (HttpStatusCodeException e) { throw VaultResponses.buildException(e);