Introduce AutoConfiguration classes for Vault
This commit is contained in:
@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
@@ -38,19 +39,15 @@ import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
|
||||
import org.springframework.vault.authentication.LifecycleAwareSessionManagerSupport;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.SimpleSessionManager;
|
||||
import org.springframework.vault.client.RestTemplateBuilder;
|
||||
import org.springframework.vault.client.RestTemplateCustomizer;
|
||||
import org.springframework.vault.client.RestTemplateFactory;
|
||||
import org.springframework.vault.client.RestTemplateRequestCustomizer;
|
||||
import org.springframework.vault.client.SimpleVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
@@ -63,39 +60,26 @@ import org.springframework.web.client.RestTemplate;
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
* @since 3.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(VaultProperties.class)
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 5)
|
||||
public class VaultAutoConfiguration implements InitializingBean {
|
||||
public class VaultAutoConfiguration {
|
||||
|
||||
private final ConfigurableApplicationContext applicationContext;
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
private final VaultConfiguration configuration;
|
||||
|
||||
private final VaultEndpointProvider endpointProvider;
|
||||
|
||||
private final List<RestTemplateCustomizer> customizers;
|
||||
|
||||
private final List<RestTemplateRequestCustomizer<?>> requestCustomizers;
|
||||
|
||||
private ClientFactoryWrapper clientFactoryWrapper;
|
||||
|
||||
/**
|
||||
* Used for Vault communication.
|
||||
*/
|
||||
private RestTemplateBuilder restTemplateBuilder;
|
||||
|
||||
/**
|
||||
* Used for Vault communication.
|
||||
*/
|
||||
private RestTemplateFactory restTemplateFactory;
|
||||
|
||||
/**
|
||||
* Used for external (AWS, GCP) communication.
|
||||
*/
|
||||
private RestTemplate externalRestOperations;
|
||||
|
||||
public VaultAutoConfiguration(ConfigurableApplicationContext applicationContext, VaultProperties vaultProperties,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<RestTemplateCustomizer>> customizers,
|
||||
@@ -103,11 +87,12 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.configuration = new VaultConfiguration(vaultProperties);
|
||||
|
||||
VaultEndpointProvider provider = endpointProvider.getIfAvailable();
|
||||
|
||||
if (provider == null) {
|
||||
provider = SimpleVaultEndpointProvider.of(VaultConfigurationUtil.createVaultEndpoint(vaultProperties));
|
||||
provider = SimpleVaultEndpointProvider.of(this.configuration.createVaultEndpoint());
|
||||
}
|
||||
|
||||
this.endpointProvider = provider;
|
||||
@@ -118,41 +103,18 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
AnnotationAwareOrderComparator.sort(this.requestCustomizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
this.clientFactoryWrapper = createClientFactoryWrapper();
|
||||
|
||||
this.restTemplateBuilder = restTemplateBuilder(this.clientFactoryWrapper.getClientHttpRequestFactory());
|
||||
|
||||
this.externalRestOperations = new RestTemplate(this.clientFactoryWrapper.getClientHttpRequestFactory());
|
||||
|
||||
this.restTemplateFactory = new DefaultRestTemplateFactory(
|
||||
this.clientFactoryWrapper.getClientHttpRequestFactory(), this::restTemplateBuilder);
|
||||
|
||||
this.customizers.forEach(customizer -> customizer.customize(this.externalRestOperations));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RestTemplateBuilder} initialized with {@link VaultEndpointProvider}
|
||||
* and {@link ClientHttpRequestFactory}. May be overridden by subclasses.
|
||||
* @param requestFactory the {@link ClientHttpRequestFactory}.
|
||||
* @return the {@link RestTemplateBuilder}.
|
||||
* @since 2.3
|
||||
* @see #clientHttpRequestFactoryWrapper()
|
||||
*/
|
||||
protected RestTemplateBuilder restTemplateBuilder(ClientHttpRequestFactory requestFactory) {
|
||||
|
||||
RestTemplateBuilder builder = RestTemplateBuilder.builder().requestFactory(requestFactory)
|
||||
.endpointProvider(this.endpointProvider);
|
||||
|
||||
this.customizers.forEach(builder::customizers);
|
||||
this.requestCustomizers.forEach(builder::requestCustomizers);
|
||||
|
||||
if (StringUtils.hasText(this.vaultProperties.getNamespace())) {
|
||||
builder.defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE, this.vaultProperties.getNamespace());
|
||||
}
|
||||
|
||||
return builder;
|
||||
return this.configuration.createRestTemplateBuilder(requestFactory, this.endpointProvider, this.customizers,
|
||||
this.requestCustomizers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,37 +129,43 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClientFactoryWrapper clientHttpRequestFactoryWrapper() {
|
||||
return this.clientFactoryWrapper;
|
||||
return new ClientFactoryWrapper(this.configuration.createClientHttpRequestFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RestTemplateFactory} bean that is used to produce
|
||||
* {@link RestTemplate}.
|
||||
* @param clientFactoryWrapper the {@link ClientFactoryWrapper}.
|
||||
* @return the {@link RestTemplateFactory}.
|
||||
* @see #clientHttpRequestFactoryWrapper()
|
||||
* @since 3.0
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplateFactory vaultRestTemplateFactory() {
|
||||
return this.restTemplateFactory;
|
||||
@ConditionalOnMissingBean
|
||||
public RestTemplateFactory vaultRestTemplateFactory(ClientFactoryWrapper clientFactoryWrapper) {
|
||||
return new DefaultRestTemplateFactory(clientFactoryWrapper.getClientHttpRequestFactory(),
|
||||
this::restTemplateBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link VaultTemplate}.
|
||||
* @param clientFactoryWrapper the {@link ClientFactoryWrapper}.
|
||||
* @return the {@link VaultTemplate} bean.
|
||||
* @see VaultAutoConfiguration#clientHttpRequestFactoryWrapper()
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(VaultOperations.class)
|
||||
public VaultTemplate vaultTemplate() {
|
||||
public VaultTemplate vaultTemplate(ClientFactoryWrapper clientFactoryWrapper) {
|
||||
|
||||
VaultProperties.AuthenticationMethod authentication = this.vaultProperties.getAuthentication();
|
||||
RestTemplateBuilder restTemplateBuilder = restTemplateBuilder(
|
||||
clientFactoryWrapper.getClientHttpRequestFactory());
|
||||
|
||||
if (authentication == VaultProperties.AuthenticationMethod.NONE) {
|
||||
return new VaultTemplate(this.restTemplateBuilder);
|
||||
return new VaultTemplate(restTemplateBuilder);
|
||||
}
|
||||
|
||||
return new VaultTemplate(this.restTemplateBuilder, this.applicationContext.getBean(SessionManager.class));
|
||||
return new VaultTemplate(restTemplateBuilder, this.applicationContext.getBean(SessionManager.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,10 +179,7 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
@ConditionalOnMissingBean(TaskSchedulerWrapper.class)
|
||||
public TaskSchedulerWrapper vaultTaskScheduler() {
|
||||
|
||||
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
threadPoolTaskScheduler.setPoolSize(2);
|
||||
threadPoolTaskScheduler.setDaemon(true);
|
||||
threadPoolTaskScheduler.setThreadNamePrefix("Spring-Cloud-Vault-");
|
||||
ThreadPoolTaskScheduler threadPoolTaskScheduler = VaultConfiguration.createScheduler();
|
||||
|
||||
// This is to destroy bootstrap resources
|
||||
// otherwise, the bootstrap context is not shut down cleanly
|
||||
@@ -228,6 +193,7 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
* @param clientAuthentication the {@link ClientAuthentication}.
|
||||
* @param asyncTaskExecutorFactory the {@link ObjectFactory} for
|
||||
* {@link TaskSchedulerWrapper}.
|
||||
* @param restTemplateFactory the {@link RestTemplateFactory}.
|
||||
* @see SessionManager
|
||||
* @see LifecycleAwareSessionManager
|
||||
*/
|
||||
@@ -235,43 +201,37 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnAuthentication
|
||||
public SessionManager vaultSessionManager(ClientAuthentication clientAuthentication,
|
||||
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory) {
|
||||
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory, RestTemplateFactory restTemplateFactory) {
|
||||
|
||||
VaultProperties.SessionLifecycle lifecycle = this.vaultProperties.getSession().getLifecycle();
|
||||
|
||||
if (lifecycle.isEnabled()) {
|
||||
RestTemplate restTemplate = this.restTemplateFactory.create();
|
||||
LifecycleAwareSessionManagerSupport.RefreshTrigger trigger = new LifecycleAwareSessionManagerSupport.FixedTimeoutRefreshTrigger(
|
||||
lifecycle.getRefreshBeforeExpiry(), lifecycle.getExpiryThreshold());
|
||||
return new LifecycleAwareSessionManager(clientAuthentication,
|
||||
asyncTaskExecutorFactory.getObject().getTaskScheduler(), restTemplate, trigger);
|
||||
}
|
||||
|
||||
return new SimpleSessionManager(clientAuthentication);
|
||||
return this.configuration.createSessionManager(clientAuthentication,
|
||||
() -> asyncTaskExecutorFactory.getObject().getTaskScheduler(), restTemplateFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ClientAuthentication} to obtain a
|
||||
* {@link org.springframework.vault.support.VaultToken}.
|
||||
* @param clientFactoryWrapper the {@link ClientFactoryWrapper}.
|
||||
* @param restTemplateFactory the {@link RestTemplateFactory}.
|
||||
* @see SessionManager
|
||||
* @see LifecycleAwareSessionManager
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnAuthentication
|
||||
public ClientAuthentication clientAuthentication() {
|
||||
public ClientAuthentication clientAuthentication(ClientFactoryWrapper clientFactoryWrapper,
|
||||
RestTemplateFactory restTemplateFactory) {
|
||||
|
||||
RestTemplate restTemplate = this.restTemplateFactory.create();
|
||||
RestTemplate externalRestOperations = new RestTemplate(clientFactoryWrapper.getClientHttpRequestFactory());
|
||||
|
||||
this.customizers.forEach(customizer -> customizer.customize(externalRestOperations));
|
||||
|
||||
RestTemplate restTemplate = restTemplateFactory.create();
|
||||
ClientAuthenticationFactory factory = new ClientAuthenticationFactory(this.vaultProperties, restTemplate,
|
||||
this.externalRestOperations);
|
||||
externalRestOperations);
|
||||
|
||||
return factory.createClientAuthentication();
|
||||
}
|
||||
|
||||
protected ClientFactoryWrapper createClientFactoryWrapper() {
|
||||
return new ClientFactoryWrapper(VaultConfigurationUtil.createClientHttpRequestFactory(this.vaultProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to keep {@link TaskScheduler} local to Spring Cloud Vault.
|
||||
*/
|
||||
@@ -279,8 +239,15 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
|
||||
private final ThreadPoolTaskScheduler taskScheduler;
|
||||
|
||||
private final boolean acceptAfterPropertiesSet;
|
||||
|
||||
public TaskSchedulerWrapper(ThreadPoolTaskScheduler taskScheduler) {
|
||||
this(taskScheduler, true);
|
||||
}
|
||||
|
||||
TaskSchedulerWrapper(ThreadPoolTaskScheduler taskScheduler, boolean acceptAfterPropertiesSet) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.acceptAfterPropertiesSet = acceptAfterPropertiesSet;
|
||||
}
|
||||
|
||||
ThreadPoolTaskScheduler getTaskScheduler() {
|
||||
@@ -288,13 +255,16 @@ public class VaultAutoConfiguration implements InitializingBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
public void destroy() {
|
||||
this.taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.taskScheduler.afterPropertiesSet();
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
if (this.acceptAfterPropertiesSet) {
|
||||
this.taskScheduler.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ package org.springframework.cloud.vault.config;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bootstrap.BootstrapConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -30,21 +30,23 @@ import org.springframework.vault.client.RestTemplateRequestCustomizer;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Vault support.
|
||||
* {@link BootstrapConfiguration Boostrap configuration} for Spring Vault support.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 3.0, use {@link VaultReactiveAutoConfiguration} through
|
||||
* {@code @EnableAutoConfiguration}.
|
||||
*/
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(VaultProperties.class)
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 5)
|
||||
@Deprecated
|
||||
public class VaultBootstrapConfiguration extends VaultAutoConfiguration {
|
||||
|
||||
public VaultBootstrapConfiguration(ConfigurableApplicationContext applicationContext,
|
||||
VaultProperties vaultProperties, ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<RestTemplateCustomizer>> customizers,
|
||||
ObjectProvider<List<RestTemplateRequestCustomizer<?>>> requestCustomizers) {
|
||||
|
||||
super(applicationContext, vaultProperties, endpointProvider, customizers, requestCustomizers);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2017-2020 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
|
||||
*
|
||||
* https://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.vault.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.authentication.AuthenticationStepsFactory;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.ReactiveLifecycleAwareSessionManager;
|
||||
import org.springframework.vault.authentication.ReactiveSessionManager;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.VaultTokenSupplier;
|
||||
import org.springframework.vault.client.SimpleVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.WebClientBuilder;
|
||||
import org.springframework.vault.client.WebClientCustomizer;
|
||||
import org.springframework.vault.client.WebClientFactory;
|
||||
import org.springframework.vault.core.ReactiveVaultOperations;
|
||||
import org.springframework.vault.core.ReactiveVaultTemplate;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.vault.config.VaultAutoConfiguration.TaskSchedulerWrapper;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for reactive Spring Vault support.
|
||||
* <p>
|
||||
* This auto-configuration only supports static endpoints without
|
||||
* {@link VaultEndpointProvider} support as endpoint providers could be potentially
|
||||
* blocking implementations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
@ConditionalOnExpression("${spring.cloud.vault.reactive.enabled:true}")
|
||||
@ConditionalOnClass({ Flux.class, WebClient.class, ReactiveVaultOperations.class, HttpClient.class })
|
||||
@EnableConfigurationProperties({ VaultProperties.class })
|
||||
@AutoConfigureBefore(VaultAutoConfiguration.class)
|
||||
public class VaultReactiveAutoConfiguration implements InitializingBean {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
private final VaultReactiveConfiguration configuration;
|
||||
|
||||
private final VaultEndpointProvider endpointProvider;
|
||||
|
||||
private final List<WebClientCustomizer> customizers;
|
||||
|
||||
private ClientHttpConnector clientHttpConnector;
|
||||
|
||||
/**
|
||||
* Used for Vault communication.
|
||||
*/
|
||||
private WebClientBuilder webClientBuilder;
|
||||
|
||||
public VaultReactiveAutoConfiguration(VaultProperties vaultProperties,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<WebClientCustomizer>> webClientCustomizers) {
|
||||
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.configuration = new VaultReactiveConfiguration(vaultProperties);
|
||||
|
||||
this.endpointProvider = endpointProvider.getIfAvailable(
|
||||
() -> SimpleVaultEndpointProvider.of(new VaultConfiguration(vaultProperties).createVaultEndpoint()));
|
||||
this.customizers = new ArrayList<>(webClientCustomizers.getIfAvailable(Collections::emptyList));
|
||||
AnnotationAwareOrderComparator.sort(this.customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
this.clientHttpConnector = createConnector(this.vaultProperties);
|
||||
|
||||
this.webClientBuilder = webClientBuilder(this.clientHttpConnector);
|
||||
}
|
||||
|
||||
protected WebClientBuilder webClientBuilder(ClientHttpConnector connector) {
|
||||
return this.configuration.createWebClientBuilder(connector, this.endpointProvider, this.customizers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ClientHttpConnector} configured with {@link ClientOptions} and
|
||||
* {@link SslConfiguration} which are not necessarily applicable for the whole
|
||||
* application.
|
||||
* @param vaultProperties the Vault properties.
|
||||
* @return the {@link ClientHttpConnector}.
|
||||
*/
|
||||
protected ClientHttpConnector createConnector(VaultProperties vaultProperties) {
|
||||
return new VaultReactiveConfiguration(vaultProperties).createClientHttpConnector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link WebClientFactory} bean that is used to produce {@link WebClient}.
|
||||
* @return the {@link WebClientFactory}.
|
||||
* @since 3.0
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebClientFactory vaultWebClientFactory() {
|
||||
return new DefaultWebClientFactory(this.clientHttpConnector, this::webClientBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ReactiveVaultTemplate}.
|
||||
* @param sessionManager object provider for {@link ReactiveSessionManager}.
|
||||
* @return the {@link ReactiveVaultTemplate} bean.
|
||||
* @see #reactiveVaultSessionManager(BeanFactory, ObjectFactory, WebClientFactory)
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ReactiveVaultOperations.class)
|
||||
public ReactiveVaultTemplate reactiveVaultTemplate(ObjectProvider<ReactiveSessionManager> sessionManager) {
|
||||
|
||||
if (this.vaultProperties.getAuthentication() == VaultProperties.AuthenticationMethod.NONE) {
|
||||
return new ReactiveVaultTemplate(this.webClientBuilder);
|
||||
}
|
||||
|
||||
return new ReactiveVaultTemplate(this.webClientBuilder, sessionManager.getObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beanFactory the {@link BeanFactory}.
|
||||
* @param asyncTaskExecutorFactory the {@link ObjectFactory} for
|
||||
* {@link TaskSchedulerWrapper}.
|
||||
* @param webClientFactory the web client factory
|
||||
* @return {@link ReactiveSessionManager} for reactive session use.
|
||||
* @see ReactiveSessionManager
|
||||
* @see ReactiveLifecycleAwareSessionManager
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnAuthentication
|
||||
public ReactiveSessionManager reactiveVaultSessionManager(BeanFactory beanFactory,
|
||||
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory, WebClientFactory webClientFactory) {
|
||||
|
||||
VaultTokenSupplier vaultTokenSupplier = beanFactory.getBean("vaultTokenSupplier", VaultTokenSupplier.class);
|
||||
|
||||
return this.configuration.createReactiveSessionManager(vaultTokenSupplier,
|
||||
() -> asyncTaskExecutorFactory.getObject().getTaskScheduler(), webClientFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sessionManager the {@link ReactiveSessionManager}.
|
||||
* @return {@link SessionManager} adapter wrapping {@link ReactiveSessionManager}.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnAuthentication
|
||||
public SessionManager vaultSessionManager(ReactiveSessionManager sessionManager) {
|
||||
return this.configuration.createSessionManager(sessionManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beanFactory the {@link BeanFactory}.
|
||||
* @param webClientFactory the {@link WebClientFactory}.
|
||||
* @return the {@link VaultTokenSupplier} for reactive Vault session management
|
||||
* adapting {@link ClientAuthentication} that also implement
|
||||
* {@link AuthenticationStepsFactory}.
|
||||
* @see AuthenticationStepsFactory
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "vaultTokenSupplier")
|
||||
@ConditionalOnAuthentication
|
||||
public VaultTokenSupplier vaultTokenSupplier(ListableBeanFactory beanFactory, WebClientFactory webClientFactory) {
|
||||
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
|
||||
return this.configuration.createVaultTokenSupplier(webClientFactory,
|
||||
() -> beanFactory.getBeanProvider(AuthenticationStepsFactory.class, false).getIfAvailable(),
|
||||
() -> beanFactory.getBeanProvider(ClientAuthentication.class, false).getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,61 +16,25 @@
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.authentication.AuthenticationStepsFactory;
|
||||
import org.springframework.vault.authentication.AuthenticationStepsOperator;
|
||||
import org.springframework.vault.authentication.CachingVaultTokenSupplier;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.ReactiveLifecycleAwareSessionManager;
|
||||
import org.springframework.vault.authentication.ReactiveSessionManager;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.authentication.VaultTokenSupplier;
|
||||
import org.springframework.vault.client.ClientHttpConnectorFactory;
|
||||
import org.springframework.vault.client.SimpleVaultEndpointProvider;
|
||||
import org.springframework.cloud.bootstrap.BootstrapConfiguration;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.client.WebClientBuilder;
|
||||
import org.springframework.vault.client.WebClientCustomizer;
|
||||
import org.springframework.vault.client.WebClientFactory;
|
||||
import org.springframework.vault.core.ReactiveVaultOperations;
|
||||
import org.springframework.vault.core.ReactiveVaultTemplate;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.vault.config.VaultAutoConfiguration.TaskSchedulerWrapper;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for reactive Spring Vault support.
|
||||
* {@link BootstrapConfiguration Bootstrap-configuration} for reactive Spring Vault
|
||||
* support.
|
||||
* <p>
|
||||
* This auto-configuration only supports static endpoints without
|
||||
* {@link org.springframework.vault.client.VaultEndpointProvider} support as endpoint
|
||||
@@ -78,206 +42,20 @@ import static org.springframework.cloud.vault.config.VaultAutoConfiguration.Task
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0.0
|
||||
* @deprecated since 3.0, use {@link VaultReactiveAutoConfiguration} through
|
||||
* {@code @EnableAutoConfiguration}.
|
||||
*/
|
||||
@Deprecated
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
@ConditionalOnExpression("${spring.cloud.vault.reactive.enabled:true}")
|
||||
@ConditionalOnClass({ Flux.class, WebClient.class, ReactiveVaultOperations.class, HttpClient.class })
|
||||
@EnableConfigurationProperties({ VaultProperties.class })
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 10)
|
||||
public class VaultReactiveBootstrapConfiguration implements InitializingBean {
|
||||
|
||||
// TODO: Expose as AutoConfiguration
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
private final VaultEndpointProvider endpointProvider;
|
||||
|
||||
private final List<WebClientCustomizer> customizers;
|
||||
|
||||
private ClientHttpConnector clientHttpConnector;
|
||||
|
||||
/**
|
||||
* Used for Vault communication.
|
||||
*/
|
||||
private WebClientBuilder webClientBuilder;
|
||||
|
||||
/**
|
||||
* Used for Vault communication.
|
||||
*/
|
||||
private WebClientFactory webClientFactory;
|
||||
public class VaultReactiveBootstrapConfiguration extends VaultReactiveAutoConfiguration {
|
||||
|
||||
public VaultReactiveBootstrapConfiguration(VaultProperties vaultProperties,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<WebClientCustomizer>> webClientCustomizers) {
|
||||
|
||||
this.vaultProperties = vaultProperties;
|
||||
|
||||
this.endpointProvider = endpointProvider.getIfAvailable(
|
||||
() -> SimpleVaultEndpointProvider.of(VaultConfigurationUtil.createVaultEndpoint(vaultProperties)));
|
||||
this.customizers = new ArrayList<>(webClientCustomizers.getIfAvailable(Collections::emptyList));
|
||||
AnnotationAwareOrderComparator.sort(this.customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
this.clientHttpConnector = createConnector(this.vaultProperties);
|
||||
|
||||
this.webClientBuilder = webClientBuilder(this.clientHttpConnector);
|
||||
|
||||
this.webClientFactory = new DefaultWebClientFactory(this.clientHttpConnector, this::webClientBuilder);
|
||||
}
|
||||
|
||||
protected WebClientBuilder webClientBuilder(ClientHttpConnector connector) {
|
||||
|
||||
WebClientBuilder builder = WebClientBuilder.builder().httpConnector(connector)
|
||||
.endpointProvider(this.endpointProvider);
|
||||
|
||||
this.customizers.forEach(builder::customizers);
|
||||
|
||||
if (StringUtils.hasText(this.vaultProperties.getNamespace())) {
|
||||
builder.defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE, this.vaultProperties.getNamespace());
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ClientHttpConnector} configured with {@link ClientOptions} and
|
||||
* {@link SslConfiguration} which are not necessarily applicable for the whole
|
||||
* application.
|
||||
* @param vaultProperties the Vault properties.
|
||||
* @return the {@link ClientHttpConnector}.
|
||||
*/
|
||||
protected ClientHttpConnector createConnector(VaultProperties vaultProperties) {
|
||||
|
||||
ClientOptions clientOptions = new ClientOptions(Duration.ofMillis(vaultProperties.getConnectionTimeout()),
|
||||
Duration.ofMillis(vaultProperties.getReadTimeout()));
|
||||
|
||||
SslConfiguration sslConfiguration = VaultConfigurationUtil.createSslConfiguration(vaultProperties.getSsl());
|
||||
|
||||
return ClientHttpConnectorFactory.create(clientOptions, sslConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link WebClientFactory} bean that is used to produce {@link WebClient}.
|
||||
* @return the {@link WebClientFactory}.
|
||||
* @since 3.0
|
||||
*/
|
||||
@Bean
|
||||
public WebClientFactory vaultWebClientFactory() {
|
||||
return this.webClientFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ReactiveVaultTemplate}.
|
||||
* @return the {@link ReactiveVaultTemplate} bean.
|
||||
* @see #reactiveVaultSessionManager(BeanFactory, ObjectFactory)
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ReactiveVaultOperations.class)
|
||||
public ReactiveVaultTemplate reactiveVaultTemplate(ObjectProvider<ReactiveSessionManager> sessionManager) {
|
||||
|
||||
if (this.vaultProperties.getAuthentication() == VaultProperties.AuthenticationMethod.NONE) {
|
||||
return new ReactiveVaultTemplate(this.webClientBuilder);
|
||||
}
|
||||
|
||||
return new ReactiveVaultTemplate(this.webClientBuilder, sessionManager.getObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beanFactory the {@link BeanFactory}.
|
||||
* @param asyncTaskExecutorFactory the {@link ObjectFactory} for
|
||||
* {@link TaskSchedulerWrapper}.
|
||||
* @return {@link ReactiveSessionManager} for reactive session use.
|
||||
* @see ReactiveSessionManager
|
||||
* @see ReactiveLifecycleAwareSessionManager
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnAuthentication
|
||||
public ReactiveSessionManager reactiveVaultSessionManager(BeanFactory beanFactory,
|
||||
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory) {
|
||||
|
||||
VaultTokenSupplier vaultTokenSupplier = beanFactory.getBean("vaultTokenSupplier", VaultTokenSupplier.class);
|
||||
|
||||
VaultProperties.SessionLifecycle lifecycle = this.vaultProperties.getSession().getLifecycle();
|
||||
if (lifecycle.isEnabled()) {
|
||||
WebClient webClient = this.webClientFactory.create();
|
||||
ReactiveLifecycleAwareSessionManager.RefreshTrigger trigger = new ReactiveLifecycleAwareSessionManager.FixedTimeoutRefreshTrigger(
|
||||
lifecycle.getRefreshBeforeExpiry(), lifecycle.getExpiryThreshold());
|
||||
return new ReactiveLifecycleAwareSessionManager(vaultTokenSupplier,
|
||||
asyncTaskExecutorFactory.getObject().getTaskScheduler(), webClient, trigger);
|
||||
}
|
||||
|
||||
return CachingVaultTokenSupplier.of(vaultTokenSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sessionManager the {@link ReactiveSessionManager}.
|
||||
* @return {@link SessionManager} adapter wrapping {@link ReactiveSessionManager}.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnAuthentication
|
||||
public SessionManager vaultSessionManager(ReactiveSessionManager sessionManager) {
|
||||
return () -> {
|
||||
|
||||
VaultToken token = sessionManager.getSessionToken().block();
|
||||
Assert.state(token != null, "ReactiveSessionManager returned a null VaultToken");
|
||||
return token;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beanFactory the {@link BeanFactory}.
|
||||
* @return the {@link VaultTokenSupplier} for reactive Vault session management
|
||||
* adapting {@link ClientAuthentication} that also implement
|
||||
* {@link AuthenticationStepsFactory}.
|
||||
* @see AuthenticationStepsFactory
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "vaultTokenSupplier")
|
||||
@ConditionalOnAuthentication
|
||||
public VaultTokenSupplier vaultTokenSupplier(ListableBeanFactory beanFactory) {
|
||||
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
|
||||
String[] authStepsFactories = beanFactory.getBeanNamesForType(AuthenticationStepsFactory.class);
|
||||
|
||||
if (!ObjectUtils.isEmpty(authStepsFactories)) {
|
||||
|
||||
AuthenticationStepsFactory factory = beanFactory.getBean(AuthenticationStepsFactory.class);
|
||||
return createAuthenticationStepsOperator(factory);
|
||||
}
|
||||
|
||||
String[] clientAuthentications = beanFactory.getBeanNamesForType(ClientAuthentication.class);
|
||||
|
||||
if (!ObjectUtils.isEmpty(clientAuthentications)) {
|
||||
|
||||
ClientAuthentication clientAuthentication = beanFactory.getBean(ClientAuthentication.class);
|
||||
|
||||
if (clientAuthentication instanceof TokenAuthentication) {
|
||||
|
||||
TokenAuthentication authentication = (TokenAuthentication) clientAuthentication;
|
||||
return () -> Mono.just(authentication.login());
|
||||
}
|
||||
|
||||
if (clientAuthentication instanceof AuthenticationStepsFactory) {
|
||||
return createAuthenticationStepsOperator((AuthenticationStepsFactory) clientAuthentication);
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format("Cannot construct VaultTokenSupplier from %s. "
|
||||
+ "ClientAuthentication must implement AuthenticationStepsFactory or be TokenAuthentication",
|
||||
clientAuthentication));
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"Cannot construct VaultTokenSupplier. Please configure VaultTokenSupplier bean named vaultTokenSupplier.");
|
||||
}
|
||||
|
||||
private VaultTokenSupplier createAuthenticationStepsOperator(AuthenticationStepsFactory factory) {
|
||||
WebClient webClient = this.webClientFactory.create();
|
||||
return new AuthenticationStepsOperator(factory.getAuthenticationSteps(), webClient);
|
||||
super(vaultProperties, endpointProvider, webClientCustomizers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user