Adapt to newly introduced ReactiveSessionManager.

Closes gh-188.
This commit is contained in:
Mark Paluch
2018-01-17 15:54:15 +01:00
parent 6281174be2
commit edb14d6ac8
4 changed files with 136 additions and 18 deletions

View File

@@ -19,21 +19,34 @@ import java.net.URI;
import java.time.Duration;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectFactory;
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.cloud.vault.config.VaultBootstrapConfiguration.TaskSchedulerWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
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.ReactiveVaultClients;
import org.springframework.vault.client.VaultEndpoint;
@@ -47,12 +60,17 @@ import org.springframework.web.reactive.function.client.WebClient;
/**
* {@link EnableAutoConfiguration Auto-configuration} for reactive Spring Vault support.
* <p>
* This auto-configuration only supports static endpoints without
* {@link org.springframework.vault.client.VaultEndpointProvider} support as endpoint
* providers could be potentially blocking implementations.
*
* @author Mark Paluch
* @since 2.0.0
*/
@Configuration
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@ConditionalOnExpression("${spring.cloud.vault.reactive.enabled:true}")
@ConditionalOnClass({ Flux.class, WebClient.class, ReactiveVaultOperations.class })
@EnableConfigurationProperties({ VaultProperties.class })
@Order(Ordered.LOWEST_PRECEDENCE - 10)
@@ -139,28 +157,105 @@ public class ReactiveVaultBootstrapConfiguration {
* Creates a {@link ReactiveVaultTemplate}.
*
* @return
* @see #vaultTokenSupplier(AuthenticationStepsFactory)
* @see #reactiveVaultSessionManager(BeanFactory, ObjectFactory)
*/
@Bean
@ConditionalOnMissingBean
public ReactiveVaultTemplate reactiveVaultTemplate(VaultTokenSupplier tokenSupplier) {
public ReactiveVaultTemplate reactiveVaultTemplate(
ReactiveSessionManager tokenSupplier) {
return new ReactiveVaultTemplate(vaultEndpoint, clientHttpConnector,
tokenSupplier);
}
/**
* @return the {@link VaultTokenSupplier} for reactive Vault session management.
* @return {@link ReactiveSessionManager} for reactive session use.
* @see ReactiveSessionManager
* @see ReactiveLifecycleAwareSessionManager
*/
@Bean
@ConditionalOnMissingBean
public ReactiveSessionManager reactiveVaultSessionManager(BeanFactory beanFactory,
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory) {
VaultTokenSupplier vaultTokenSupplier = beanFactory.getBean("vaultTokenSupplier",
VaultTokenSupplier.class);
if (vaultProperties.getConfig().getLifecycle().isEnabled()) {
WebClient webClient = ReactiveVaultClients.createWebClient(vaultEndpoint,
clientHttpConnector);
return new ReactiveLifecycleAwareSessionManager(vaultTokenSupplier,
asyncTaskExecutorFactory.getObject().getTaskScheduler(), webClient);
}
return CachingVaultTokenSupplier.of(vaultTokenSupplier);
}
/**
* @return {@link SessionManager} adapter wrapping {@link ReactiveSessionManager}.
*/
@Bean
@ConditionalOnMissingBean
public SessionManager vaultSessionManager(ReactiveSessionManager sessionManager) {
return sessionManager.getSessionToken()::block;
}
/**
* @return the {@link VaultTokenSupplier} for reactive Vault session management
* adapting {@link ClientAuthentication} that also implement
* {@link AuthenticationStepsFactory}.
* @see AuthenticationStepsFactory
*/
@Bean
@ConditionalOnMissingBean(VaultTokenSupplier.class)
public VaultTokenSupplier vaultTokenSupplier(AuthenticationStepsFactory factory) {
@ConditionalOnMissingBean(name = "vaultTokenSupplier")
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 = ReactiveVaultClients.createWebClient(this.vaultEndpoint,
this.clientHttpConnector);
AuthenticationStepsOperator operator = new AuthenticationStepsOperator(
factory.getAuthenticationSteps(), webClient);
return CachingVaultTokenSupplier.of(operator);
return new AuthenticationStepsOperator(factory.getAuthenticationSteps(),
webClient);
}
}

View File

@@ -215,7 +215,7 @@ public class VaultBootstrapConfiguration implements InitializingBean {
*/
@Bean
@ConditionalOnMissingBean
public SessionManager sessionManager(ClientAuthentication clientAuthentication,
public SessionManager vaultSessionManager(ClientAuthentication clientAuthentication,
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory) {
if (vaultProperties.getConfig().getLifecycle().isEnabled()) {

View File

@@ -15,13 +15,13 @@
*/
package org.springframework.cloud.vault.config;
import static org.assertj.core.api.Assertions.*;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import static org.assertj.core.api.Assertions.*;
/**
* Unit tests for {@link PropertyNameTransformer}.
*
@@ -35,7 +35,7 @@ public class PropertyNameTransformerUnitTests {
PropertyNameTransformer transformer = new PropertyNameTransformer();
transformer.addKeyTransformation("old-key", "new-key");
Map<String, String> map = new HashMap<>();
Map<String, Object> map = new HashMap<>();
map.put("old-key", "value");
map.put("other-key", "other-value");
@@ -51,4 +51,4 @@ public class PropertyNameTransformerUnitTests {
assertThat(transformer.transformProperties(null)).isNull();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2018 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.
@@ -24,6 +24,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.authentication.AuthenticationSteps;
import org.springframework.vault.authentication.AuthenticationStepsFactory;
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.SimpleSessionManager;
import org.springframework.vault.authentication.VaultTokenSupplier;
import org.springframework.vault.core.ReactiveVaultOperations;
import org.springframework.vault.support.VaultToken;
@@ -43,22 +46,42 @@ public class ReactiveVaultBootstrapConfigurationTests {
@Test
public void shouldConfigureTemplate() {
load(AuthenticationFactoryConfiguration.class);
load(AuthenticationFactoryConfiguration.class,
"spring.cloud.vault.config.lifecycle.enabled=false");
assertThat(context.getBean(ReactiveVaultOperations.class)).isNotNull();
assertThat(context.getBean(AuthenticationStepsFactory.class)).isNotNull();
assertThat(context.getBean(SessionManager.class)).isNotNull()
.isNotInstanceOf(LifecycleAwareSessionManager.class)
.isNotInstanceOf(SimpleSessionManager.class);
assertThat(context.getBeanNamesForType(WebClient.class)).isEmpty();
}
@Test
public void shouldConfigureTemplateWithTokenSupplier() {
load(TokeSupplierConfiguration.class);
load(TokenSupplierConfiguration.class,
"spring.cloud.vault.config.lifecycle.enabled=false");
assertThat(context.getBean(ReactiveVaultOperations.class)).isNotNull();
assertThat(context.getBean(SessionManager.class)).isNotNull()
.isNotInstanceOf(LifecycleAwareSessionManager.class)
.isNotInstanceOf(SimpleSessionManager.class);
assertThat(context.getBeanNamesForType(WebClient.class)).isEmpty();
}
@Test
public void shouldNotConfigureReactiveSupport() {
load(VaultBootstrapConfiguration.class,
"spring.cloud.vault.reactive.enabled=false",
"spring.cloud.vault.token=foo");
assertThat(context.getBeanNamesForType(ReactiveVaultOperations.class)).isEmpty();
assertThat(context.getBean(SessionManager.class)).isInstanceOf(
LifecycleAwareSessionManager.class);
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -82,7 +105,7 @@ public class ReactiveVaultBootstrapConfigurationTests {
}
@Configuration
static class TokeSupplierConfiguration {
static class TokenSupplierConfiguration {
@Bean
VaultTokenSupplier vaultTokenSupplier() {