Commit 0a446cb9 authored by Madhura Bhave's avatar Madhura Bhave

Refactor Spring Security auto-configuration classes

The auto-configuration for a `UserDetailsService` and the web bits
has been split into two.

Closes gh-11915
Fixes gh-11891
parent 761bcffc
...@@ -17,29 +17,21 @@ ...@@ -17,29 +17,21 @@
package org.springframework.boot.autoconfigure.security.reactive; package org.springframework.boot.autoconfigure.security.reactive;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security in a reactive * {@link EnableAutoConfiguration Auto-configuration} for Spring Security in a reactive
* application. This auto-configuration adds {@link EnableWebFluxSecurity} and delegates * application.
* to Spring Security's content-negotiation mechanism for authentication. In a webapp this
* configuration also secures all web endpoints.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.0.0 * @since 2.0.0
*/ */
@Configuration @Configuration
@ConditionalOnClass({ EnableWebFluxSecurity.class,
AuthenticationPrincipalArgumentResolver.class })
@EnableConfigurationProperties(SecurityProperties.class) @EnableConfigurationProperties(SecurityProperties.class)
@Import({ WebFluxSecurityConfiguration.class, @Import(WebFluxSecurityConfiguration.class)
ReactiveAuthenticationManagerConfiguration.class })
public class ReactiveSecurityAutoConfiguration { public class ReactiveSecurityAutoConfiguration {
} }
...@@ -49,7 +49,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; ...@@ -49,7 +49,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
@ConditionalOnMissingBean({ ReactiveAuthenticationManager.class, @ConditionalOnMissingBean({ ReactiveAuthenticationManager.class,
ReactiveUserDetailsService.class }) ReactiveUserDetailsService.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
class ReactiveAuthenticationManagerConfiguration { public class ReactiveUserDetailsServiceAutoConfiguration {
private static final String NOOP_PASSWORD_PREFIX = "{noop}"; private static final String NOOP_PASSWORD_PREFIX = "{noop}";
...@@ -57,7 +57,7 @@ class ReactiveAuthenticationManagerConfiguration { ...@@ -57,7 +57,7 @@ class ReactiveAuthenticationManagerConfiguration {
.compile("^\\{.+}.*$"); .compile("^\\{.+}.*$");
private static final Log logger = LogFactory private static final Log logger = LogFactory
.getLog(ReactiveAuthenticationManagerConfiguration.class); .getLog(ReactiveUserDetailsServiceAutoConfiguration.class);
@Bean @Bean
public MapReactiveUserDetailsService reactiveUserDetailsService( public MapReactiveUserDetailsService reactiveUserDetailsService(
......
...@@ -24,8 +24,9 @@ import org.springframework.security.web.server.WebFilterChainProxy; ...@@ -24,8 +24,9 @@ import org.springframework.security.web.server.WebFilterChainProxy;
/** /**
* Switches on {@link EnableWebFluxSecurity} for a reactive web application if this * Switches on {@link EnableWebFluxSecurity} for a reactive web application if this
* annotation has not been added by the user. This configuration also backs off if a bean * annotation has not been added by the user. It delegates
* of type {@link WebFilterChainProxy} has been configured in any other way. * to Spring Security's content-negotiation mechanism for authentication. This configuration also
* backs off if a bean of type {@link WebFilterChainProxy} has been configured in any other way.
* *
* @author Madhura Bhave * @author Madhura Bhave
*/ */
......
...@@ -27,26 +27,20 @@ import org.springframework.context.annotation.Bean; ...@@ -27,26 +27,20 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security. Provides an * {@link EnableAutoConfiguration Auto-configuration} for Spring Security.
* {@link InMemoryUserDetailsManager} with one user (named "user") whose password is
* random and printed on the console at INFO level during startup. In a webapp, this
* configuration also secures all web endpoints (including static resources).
* *
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Madhura Bhave * @author Madhura Bhave
*/ */
@Configuration @Configuration
@ConditionalOnClass({ AuthenticationManager.class, EnableWebSecurity.class }) @ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class) @EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, @Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
AuthenticationManagerConfiguration.class, SecurityDataConfiguration.class }) SecurityDataConfiguration.class })
public class SecurityAutoConfiguration { public class SecurityAutoConfiguration {
@Bean @Bean
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.security.servlet; package org.springframework.boot.autoconfigure.security.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
...@@ -35,6 +36,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur ...@@ -35,6 +36,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.0.0 * @since 2.0.0
*/ */
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class) @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration { public class SpringBootWebSecurityConfiguration {
......
...@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory; ...@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -47,10 +48,11 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; ...@@ -47,10 +48,11 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
* @author Madhura Bhave * @author Madhura Bhave
*/ */
@Configuration @Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class, @ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
UserDetailsService.class }) UserDetailsService.class })
public class AuthenticationManagerConfiguration { public class UserDetailsServiceAutoConfiguration {
private static final String NOOP_PASSWORD_PREFIX = "{noop}"; private static final String NOOP_PASSWORD_PREFIX = "{noop}";
...@@ -58,7 +60,7 @@ public class AuthenticationManagerConfiguration { ...@@ -58,7 +60,7 @@ public class AuthenticationManagerConfiguration {
.compile("^\\{.+}.*$"); .compile("^\\{.+}.*$");
private static final Log logger = LogFactory private static final Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class); .getLog(UserDetailsServiceAutoConfiguration.class);
@Bean @Bean
@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository") @ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
......
...@@ -97,8 +97,10 @@ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ ...@@ -97,8 +97,10 @@ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
......
...@@ -17,25 +17,10 @@ ...@@ -17,25 +17,10 @@
package org.springframework.boot.autoconfigure.security.reactive; package org.springframework.boot.autoconfigure.security.reactive;
import org.junit.Test; import org.junit.Test;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.web.reactive.MockReactiveWebServerFactory;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.WebFilterChainProxy; import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -49,10 +34,11 @@ public class ReactiveSecurityAutoConfigurationTests { ...@@ -49,10 +34,11 @@ public class ReactiveSecurityAutoConfigurationTests {
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner(); private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
@Test @Test
public void enablesWebFluxSecurity() { public void importsConfigurationThatEnablesWebFluxSecurity() {
this.contextRunner.withUserConfiguration(TestConfig.class) this.contextRunner
.withConfiguration( .withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class)) AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
ReactiveUserDetailsServiceAutoConfiguration.class))
.run((context) -> { .run((context) -> {
assertThat(context).getBean(WebFilterChainProxy.class).isNotNull(); assertThat(context).getBean(WebFilterChainProxy.class).isNotNull();
assertThat(context).getBean(WebFluxSecurityConfiguration.class) assertThat(context).getBean(WebFluxSecurityConfiguration.class)
...@@ -61,90 +47,4 @@ public class ReactiveSecurityAutoConfigurationTests { ...@@ -61,90 +47,4 @@ public class ReactiveSecurityAutoConfigurationTests {
}); });
} }
@Test
public void configuresADefaultUser() {
this.contextRunner.withUserConfiguration(TestConfig.class)
.withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
.run((context) -> {
ReactiveUserDetailsService userDetailsService = context
.getBean(ReactiveUserDetailsService.class);
assertThat(userDetailsService.findByUsername("user").block())
.isNotNull();
});
}
@Test
public void doesNotConfigureDefaultUserIfUserDetailsServiceAvailable() {
this.contextRunner.withUserConfiguration(UserConfig.class, TestConfig.class)
.withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
.run((context) -> {
ReactiveUserDetailsService userDetailsService = context
.getBean(ReactiveUserDetailsService.class);
assertThat(userDetailsService.findByUsername("user").block())
.isNull();
assertThat(userDetailsService.findByUsername("foo").block())
.isNotNull();
assertThat(userDetailsService.findByUsername("admin").block())
.isNotNull();
});
}
@Test
public void doesNotConfigureDefaultUserIfAuthenticationManagerAvailable() {
this.contextRunner
.withUserConfiguration(AuthenticationManagerConfig.class,
TestConfig.class)
.withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
.run((context) -> assertThat(context)
.getBean(ReactiveUserDetailsService.class).isNull());
}
@Configuration
@EnableWebFlux
static class TestConfig {
@Bean
public HttpHandler httpHandler(ApplicationContext applicationContext) {
return WebHttpHandlerBuilder.applicationContext(applicationContext).build();
}
@Bean
public ReactiveWebServerFactory reactiveWebServerFactory() {
return new MockReactiveWebServerFactory();
}
}
@Configuration
static class UserConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails foo = User.withUsername("foo").password("foo").roles("USER")
.build();
UserDetails admin = User.withUsername("admin").password("admin")
.roles("USER", "ADMIN").build();
return new MapReactiveUserDetailsService(foo, admin);
}
}
@Configuration
static class AuthenticationManagerConfig {
@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return new ReactiveAuthenticationManager() {
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
return null;
}
};
}
}
} }
...@@ -17,34 +17,79 @@ ...@@ -17,34 +17,79 @@
package org.springframework.boot.autoconfigure.security.reactive; package org.springframework.boot.autoconfigure.security.reactive;
import org.junit.Test; import org.junit.Test;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link ReactiveAuthenticationManagerConfiguration}. * Tests for {@link ReactiveUserDetailsServiceAutoConfiguration}.
* *
* @author Madhura Bhave * @author Madhura Bhave
*/ */
public class ReactiveAuthenticationManagerConfigurationTests { public class ReactiveUserDetailsServiceAutoConfigurationTests {
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner(); private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class));
@Test
public void configuresADefaultUser() {
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class)
.run((context) -> {
ReactiveUserDetailsService userDetailsService = context
.getBean(ReactiveUserDetailsService.class);
assertThat(userDetailsService.findByUsername("user").block())
.isNotNull();
});
}
@Test
public void doesNotConfigureDefaultUserIfUserDetailsServiceAvailable() {
this.contextRunner.withUserConfiguration(UserConfig.class, TestSecurityConfiguration.class)
.run((context) -> {
ReactiveUserDetailsService userDetailsService = context
.getBean(ReactiveUserDetailsService.class);
assertThat(userDetailsService.findByUsername("user").block())
.isNull();
assertThat(userDetailsService.findByUsername("foo").block())
.isNotNull();
assertThat(userDetailsService.findByUsername("admin").block())
.isNotNull();
});
}
@Test
public void doesNotConfigureDefaultUserIfAuthenticationManagerAvailable() {
this.contextRunner
.withUserConfiguration(AuthenticationManagerConfig.class,
TestSecurityConfiguration.class)
.withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
.run((context) -> assertThat(context)
.getBean(ReactiveUserDetailsService.class).isNull());
}
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() { public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
this.contextRunner this.contextRunner
.withUserConfiguration(TestSecurityConfiguration.class, .withUserConfiguration(TestSecurityConfiguration.class)
ReactiveAuthenticationManagerConfiguration.class)
.run(((context) -> { .run(((context) -> {
MapReactiveUserDetailsService userDetailsService = context MapReactiveUserDetailsService userDetailsService = context
.getBean(MapReactiveUserDetailsService.class); .getBean(MapReactiveUserDetailsService.class);
...@@ -73,8 +118,7 @@ public class ReactiveAuthenticationManagerConfigurationTests { ...@@ -73,8 +118,7 @@ public class ReactiveAuthenticationManagerConfigurationTests {
private void testPasswordEncoding(Class<?> configClass, String providedPassword, private void testPasswordEncoding(Class<?> configClass, String providedPassword,
String expectedPassword) { String expectedPassword) {
this.contextRunner this.contextRunner
.withUserConfiguration(configClass, .withUserConfiguration(configClass)
ReactiveAuthenticationManagerConfiguration.class)
.withPropertyValues("spring.security.user.password=" + providedPassword) .withPropertyValues("spring.security.user.password=" + providedPassword)
.run(((context) -> { .run(((context) -> {
MapReactiveUserDetailsService userDetailsService = context MapReactiveUserDetailsService userDetailsService = context
...@@ -92,6 +136,35 @@ public class ReactiveAuthenticationManagerConfigurationTests { ...@@ -92,6 +136,35 @@ public class ReactiveAuthenticationManagerConfigurationTests {
} }
@Configuration
static class UserConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails foo = User.withUsername("foo").password("foo").roles("USER")
.build();
UserDetails admin = User.withUsername("admin").password("admin")
.roles("USER", "ADMIN").build();
return new MapReactiveUserDetailsService(foo, admin);
}
}
@Configuration
static class AuthenticationManagerConfig {
@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return new ReactiveAuthenticationManager() {
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
return null;
}
};
}
}
@Configuration @Configuration
@Import(TestSecurityConfiguration.class) @Import(TestSecurityConfiguration.class)
protected static class TestConfigWithPasswordEncoder { protected static class TestConfigWithPasswordEncoder {
......
/*
* Copyright 2012-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.boot.autoconfigure.security.reactive;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.server.WebFilterChainProxy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WebFluxSecurityConfiguration}.
*
* @author Madhura Bhave
*/
public class WebFluxSecurityConfigurationTests {
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
@Test
public void backsOffWhenWebFilterChainProxyBeanPresent() {
this.contextRunner.withUserConfiguration(WebFilterChainProxyConfiguration.class, WebFluxSecurityConfiguration.class)
.run(context -> assertThat(context).doesNotHaveBean(WebFluxSecurityConfiguration.class));
}
@Test
public void enablesWebFluxSecurity() {
this.contextRunner
.withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
ReactiveUserDetailsServiceAutoConfiguration.class))
.run((context) -> {
assertThat(context).getBean(WebFilterChainProxy.class).isNotNull();
assertThat(context).getBean(WebFluxSecurityConfiguration.class)
.isNotNull();
assertThat(context).getBean(WebFilterChainProxy.class).isNotNull();
});
}
@Configuration
static class WebFilterChainProxyConfiguration {
@Bean
public WebFilterChainProxy webFilterChainProxy() {
return mock(WebFilterChainProxy.class);
}
}
}
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package org.springframework.boot.autoconfigure.security.servlet; package org.springframework.boot.autoconfigure.security.servlet;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
...@@ -39,21 +38,13 @@ import org.springframework.context.annotation.Bean; ...@@ -39,21 +38,13 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.TestingAuthenticationProvider;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
...@@ -149,61 +140,6 @@ public class SecurityAutoConfigurationTests { ...@@ -149,61 +140,6 @@ public class SecurityAutoConfigurationTests {
.getOrder()).isEqualTo(12345)); .getOrder()).isEqualTo(12345));
} }
@Test
public void testDefaultUsernamePassword() {
this.contextRunner.run((context) -> {
UserDetailsService manager = context.getBean(UserDetailsService.class);
assertThat(this.outputCapture.toString())
.contains("Using generated security password:");
assertThat(manager.loadUserByUsername("user")).isNotNull();
});
}
@Test
public void defaultUserNotCreatedIfAuthenticationManagerBeanPresent() {
this.contextRunner
.withUserConfiguration(TestAuthenticationManagerConfiguration.class)
.run((context) -> {
AuthenticationManager manager = context
.getBean(AuthenticationManager.class);
assertThat(manager).isEqualTo(context.getBean(
TestAuthenticationManagerConfiguration.class).authenticationManager);
assertThat(this.outputCapture.toString())
.doesNotContain("Using generated security password: ");
TestingAuthenticationToken token = new TestingAuthenticationToken(
"foo", "bar");
assertThat(manager.authenticate(token)).isNotNull();
});
}
@Test
public void defaultUserNotCreatedIfUserDetailsServiceBeanPresent() {
this.contextRunner
.withUserConfiguration(TestUserDetailsServiceConfiguration.class)
.run((context) -> {
UserDetailsService userDetailsService = context
.getBean(UserDetailsService.class);
assertThat(this.outputCapture.toString())
.doesNotContain("Using default security password: ");
assertThat(userDetailsService.loadUserByUsername("foo")).isNotNull();
});
}
@Test
public void defaultUserNotCreatedIfAuthenticationProviderBeanPresent() {
this.contextRunner
.withUserConfiguration(TestAuthenticationProviderConfiguration.class)
.run((context) -> {
AuthenticationProvider provider = context
.getBean(AuthenticationProvider.class);
assertThat(this.outputCapture.toString())
.doesNotContain("Using default security password: ");
TestingAuthenticationToken token = new TestingAuthenticationToken(
"foo", "bar");
assertThat(provider.authenticate(token)).isNotNull();
});
}
@Test @Test
public void testJpaCoexistsHappily() { public void testJpaCoexistsHappily() {
this.contextRunner this.contextRunner
...@@ -292,42 +228,6 @@ public class SecurityAutoConfigurationTests { ...@@ -292,42 +228,6 @@ public class SecurityAutoConfigurationTests {
} }
@Configuration
protected static class TestAuthenticationManagerConfiguration {
private AuthenticationManager authenticationManager;
@Bean
public AuthenticationManager myAuthenticationManager() {
AuthenticationProvider authenticationProvider = new TestingAuthenticationProvider();
this.authenticationManager = new ProviderManager(
Collections.singletonList(authenticationProvider));
return this.authenticationManager;
}
}
@Configuration
protected static class TestUserDetailsServiceConfiguration {
@Bean
public InMemoryUserDetailsManager myUserDetailsManager() {
return new InMemoryUserDetailsManager(
User.withUsername("foo").password("bar").roles("USER").build());
}
}
@Configuration
protected static class TestAuthenticationProviderConfiguration {
@Bean
public AuthenticationProvider myAuthenticationProvider() {
return new TestingAuthenticationProvider();
}
}
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
static class WebSecurity extends WebSecurityConfigurerAdapter { static class WebSecurity extends WebSecurityConfigurerAdapter {
......
...@@ -78,6 +78,7 @@ public class SecurityFilterAutoConfigurationEarlyInitializationTests { ...@@ -78,6 +78,7 @@ public class SecurityFilterAutoConfigurationEarlyInitializationTests {
@ImportAutoConfiguration({ WebMvcAutoConfiguration.class, @ImportAutoConfiguration({ WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, SecurityAutoConfiguration.class, DispatcherServletAutoConfiguration.class, SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class,
SecurityFilterAutoConfiguration.class, SecurityFilterAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class })
static class Config { static class Config {
......
...@@ -16,15 +16,27 @@ ...@@ -16,15 +16,27 @@
package org.springframework.boot.autoconfigure.security.servlet; package org.springframework.boot.autoconfigure.security.servlet;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.TestingAuthenticationProvider;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.provisioning.InMemoryUserDetailsManager;
...@@ -33,18 +45,77 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -33,18 +45,77 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link AuthenticationManagerConfiguration}. * Tests for {@link UserDetailsServiceAutoConfiguration}.
* *
* @author Madhura Bhave * @author Madhura Bhave
*/ */
public class AuthenticationManagerConfigurationTests { public class UserDetailsServiceAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(TestSecurityConfiguration.class)
.withConfiguration(AutoConfigurations.of(UserDetailsServiceAutoConfiguration.class));
@Rule
public OutputCapture outputCapture = new OutputCapture();
@Test
public void testDefaultUsernamePassword() {
this.contextRunner.run((context) -> {
UserDetailsService manager = context.getBean(UserDetailsService.class);
assertThat(this.outputCapture.toString())
.contains("Using generated security password:");
assertThat(manager.loadUserByUsername("user")).isNotNull();
});
}
@Test
public void defaultUserNotCreatedIfAuthenticationManagerBeanPresent() {
this.contextRunner
.withUserConfiguration(TestAuthenticationManagerConfiguration.class)
.run((context) -> {
AuthenticationManager manager = context
.getBean(AuthenticationManager.class);
assertThat(manager).isEqualTo(context.getBean(
TestAuthenticationManagerConfiguration.class).authenticationManager);
assertThat(this.outputCapture.toString())
.doesNotContain("Using generated security password: ");
TestingAuthenticationToken token = new TestingAuthenticationToken(
"foo", "bar");
assertThat(manager.authenticate(token)).isNotNull();
});
}
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); @Test
public void defaultUserNotCreatedIfUserDetailsServiceBeanPresent() {
this.contextRunner
.withUserConfiguration(TestUserDetailsServiceConfiguration.class)
.run((context) -> {
UserDetailsService userDetailsService = context
.getBean(UserDetailsService.class);
assertThat(this.outputCapture.toString())
.doesNotContain("Using default security password: ");
assertThat(userDetailsService.loadUserByUsername("foo")).isNotNull();
});
}
@Test
public void defaultUserNotCreatedIfAuthenticationProviderBeanPresent() {
this.contextRunner
.withUserConfiguration(TestAuthenticationProviderConfiguration.class)
.run((context) -> {
AuthenticationProvider provider = context
.getBean(AuthenticationProvider.class);
assertThat(this.outputCapture.toString())
.doesNotContain("Using default security password: ");
TestingAuthenticationToken token = new TestingAuthenticationToken(
"foo", "bar");
assertThat(provider.authenticate(token)).isNotNull();
});
}
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() { public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class, this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run(((context) -> {
AuthenticationManagerConfiguration.class).run(((context) -> {
InMemoryUserDetailsManager userDetailsService = context InMemoryUserDetailsManager userDetailsService = context
.getBean(InMemoryUserDetailsManager.class); .getBean(InMemoryUserDetailsManager.class);
String password = userDetailsService.loadUserByUsername("user") String password = userDetailsService.loadUserByUsername("user")
...@@ -72,8 +143,7 @@ public class AuthenticationManagerConfigurationTests { ...@@ -72,8 +143,7 @@ public class AuthenticationManagerConfigurationTests {
@Test @Test
public void userDetailsServiceWhenClientRegistrationRepositoryBeanPresent() { public void userDetailsServiceWhenClientRegistrationRepositoryBeanPresent() {
this.contextRunner this.contextRunner
.withUserConfiguration(TestConfigWithClientRegistrationRepository.class, .withUserConfiguration(TestConfigWithClientRegistrationRepository.class)
AuthenticationManagerConfiguration.class)
.run(((context) -> assertThat(context) .run(((context) -> assertThat(context)
.doesNotHaveBean(InMemoryUserDetailsManager.class))); .doesNotHaveBean(InMemoryUserDetailsManager.class)));
} }
...@@ -81,8 +151,7 @@ public class AuthenticationManagerConfigurationTests { ...@@ -81,8 +151,7 @@ public class AuthenticationManagerConfigurationTests {
private void testPasswordEncoding(Class<?> configClass, String providedPassword, private void testPasswordEncoding(Class<?> configClass, String providedPassword,
String expectedPassword) { String expectedPassword) {
this.contextRunner this.contextRunner
.withUserConfiguration(configClass, .withUserConfiguration(configClass)
AuthenticationManagerConfiguration.class)
.withPropertyValues("spring.security.user.password=" + providedPassword) .withPropertyValues("spring.security.user.password=" + providedPassword)
.run(((context) -> { .run(((context) -> {
InMemoryUserDetailsManager userDetailsService = context InMemoryUserDetailsManager userDetailsService = context
...@@ -93,6 +162,42 @@ public class AuthenticationManagerConfigurationTests { ...@@ -93,6 +162,42 @@ public class AuthenticationManagerConfigurationTests {
})); }));
} }
@Configuration
protected static class TestAuthenticationManagerConfiguration {
private AuthenticationManager authenticationManager;
@Bean
public AuthenticationManager myAuthenticationManager() {
AuthenticationProvider authenticationProvider = new TestingAuthenticationProvider();
this.authenticationManager = new ProviderManager(
Collections.singletonList(authenticationProvider));
return this.authenticationManager;
}
}
@Configuration
protected static class TestUserDetailsServiceConfiguration {
@Bean
public InMemoryUserDetailsManager myUserDetailsManager() {
return new InMemoryUserDetailsManager(
User.withUsername("foo").password("bar").roles("USER").build());
}
}
@Configuration
protected static class TestAuthenticationProviderConfiguration {
@Bean
public AuthenticationProvider myAuthenticationProvider() {
return new TestingAuthenticationProvider();
}
}
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class) @EnableConfigurationProperties(SecurityProperties.class)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment