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 @@
package org.springframework.boot.autoconfigure.security.reactive;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
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
* application. This auto-configuration adds {@link EnableWebFluxSecurity} and delegates
* to Spring Security's content-negotiation mechanism for authentication. In a webapp this
* configuration also secures all web endpoints.
* application.
*
* @author Madhura Bhave
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass({ EnableWebFluxSecurity.class,
AuthenticationPrincipalArgumentResolver.class })
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ WebFluxSecurityConfiguration.class,
ReactiveAuthenticationManagerConfiguration.class })
@Import(WebFluxSecurityConfiguration.class)
public class ReactiveSecurityAutoConfiguration {
}
......@@ -49,7 +49,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
@ConditionalOnMissingBean({ ReactiveAuthenticationManager.class,
ReactiveUserDetailsService.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
class ReactiveAuthenticationManagerConfiguration {
public class ReactiveUserDetailsServiceAutoConfiguration {
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
......@@ -57,7 +57,7 @@ class ReactiveAuthenticationManagerConfiguration {
.compile("^\\{.+}.*$");
private static final Log logger = LogFactory
.getLog(ReactiveAuthenticationManagerConfiguration.class);
.getLog(ReactiveUserDetailsServiceAutoConfiguration.class);
@Bean
public MapReactiveUserDetailsService reactiveUserDetailsService(
......
......@@ -24,8 +24,9 @@ import org.springframework.security.web.server.WebFilterChainProxy;
/**
* 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
* of type {@link WebFilterChainProxy} has been configured in any other way.
* annotation has not been added by the user. It delegates
* 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
*/
......
......@@ -27,26 +27,20 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
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 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).
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Madhura Bhave
*/
@Configuration
@ConditionalOnClass({ AuthenticationManager.class, EnableWebSecurity.class })
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
AuthenticationManagerConfiguration.class, SecurityDataConfiguration.class })
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
......
......@@ -16,6 +16,7 @@
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.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
......@@ -35,6 +36,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
* @author Madhura Bhave
* @since 2.0.0
*/
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
......
......@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
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.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
......@@ -47,10 +48,11 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
* @author Madhura Bhave
*/
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
UserDetailsService.class })
public class AuthenticationManagerConfiguration {
public class UserDetailsServiceAutoConfiguration {
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
......@@ -58,7 +60,7 @@ public class AuthenticationManagerConfiguration {
.compile("^\\{.+}.*$");
private static final Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class);
.getLog(UserDetailsServiceAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
......
......@@ -97,8 +97,10 @@ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
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.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
......
......@@ -17,25 +17,10 @@
package org.springframework.boot.autoconfigure.security.reactive;
import org.junit.Test;
import reactor.core.publisher.Mono;
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.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.web.reactive.config.EnableWebFlux;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import static org.assertj.core.api.Assertions.assertThat;
......@@ -49,10 +34,11 @@ public class ReactiveSecurityAutoConfigurationTests {
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
@Test
public void enablesWebFluxSecurity() {
this.contextRunner.withUserConfiguration(TestConfig.class)
public void importsConfigurationThatEnablesWebFluxSecurity() {
this.contextRunner
.withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
ReactiveUserDetailsServiceAutoConfiguration.class))
.run((context) -> {
assertThat(context).getBean(WebFilterChainProxy.class).isNotNull();
assertThat(context).getBean(WebFluxSecurityConfiguration.class)
......@@ -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 @@
package org.springframework.boot.autoconfigure.security.reactive;
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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.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.crypto.password.PasswordEncoder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ReactiveAuthenticationManagerConfiguration}.
* Tests for {@link ReactiveUserDetailsServiceAutoConfiguration}.
*
* @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
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
this.contextRunner
.withUserConfiguration(TestSecurityConfiguration.class,
ReactiveAuthenticationManagerConfiguration.class)
.withUserConfiguration(TestSecurityConfiguration.class)
.run(((context) -> {
MapReactiveUserDetailsService userDetailsService = context
.getBean(MapReactiveUserDetailsService.class);
......@@ -73,8 +118,7 @@ public class ReactiveAuthenticationManagerConfigurationTests {
private void testPasswordEncoding(Class<?> configClass, String providedPassword,
String expectedPassword) {
this.contextRunner
.withUserConfiguration(configClass,
ReactiveAuthenticationManagerConfiguration.class)
.withUserConfiguration(configClass)
.withPropertyValues("spring.security.user.password=" + providedPassword)
.run(((context) -> {
MapReactiveUserDetailsService userDetailsService = context
......@@ -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
@Import(TestSecurityConfiguration.class)
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 @@
package org.springframework.boot.autoconfigure.security.servlet;
import java.util.Collections;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
......@@ -39,21 +38,13 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
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.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.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.util.ReflectionTestUtils;
......@@ -149,61 +140,6 @@ public class SecurityAutoConfigurationTests {
.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
public void testJpaCoexistsHappily() {
this.contextRunner
......@@ -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
@EnableWebSecurity
static class WebSecurity extends WebSecurityConfigurerAdapter {
......
......@@ -78,6 +78,7 @@ public class SecurityFilterAutoConfigurationEarlyInitializationTests {
@ImportAutoConfiguration({ WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class,
SecurityFilterAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
static class Config {
......
......@@ -16,15 +16,27 @@
package org.springframework.boot.autoconfigure.security.servlet;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.Configuration;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
......@@ -33,18 +45,77 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AuthenticationManagerConfiguration}.
* Tests for {@link UserDetailsServiceAutoConfiguration}.
*
* @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
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class,
AuthenticationManagerConfiguration.class).run(((context) -> {
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run(((context) -> {
InMemoryUserDetailsManager userDetailsService = context
.getBean(InMemoryUserDetailsManager.class);
String password = userDetailsService.loadUserByUsername("user")
......@@ -72,8 +143,7 @@ public class AuthenticationManagerConfigurationTests {
@Test
public void userDetailsServiceWhenClientRegistrationRepositoryBeanPresent() {
this.contextRunner
.withUserConfiguration(TestConfigWithClientRegistrationRepository.class,
AuthenticationManagerConfiguration.class)
.withUserConfiguration(TestConfigWithClientRegistrationRepository.class)
.run(((context) -> assertThat(context)
.doesNotHaveBean(InMemoryUserDetailsManager.class)));
}
......@@ -81,8 +151,7 @@ public class AuthenticationManagerConfigurationTests {
private void testPasswordEncoding(Class<?> configClass, String providedPassword,
String expectedPassword) {
this.contextRunner
.withUserConfiguration(configClass,
AuthenticationManagerConfiguration.class)
.withUserConfiguration(configClass)
.withPropertyValues("spring.security.user.password=" + providedPassword)
.run(((context) -> {
InMemoryUserDetailsManager userDetailsService = context
......@@ -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
@EnableWebSecurity
@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