Make user details service auto-configs back off more readily
Previously auto-configuration of a user details service (imperative or reactive) would only back off on the presence of certain beans. This led to situations where the im-memory service was auto-configured and the default password was logged even though another authentication mechanism was in use. This commit updates the auto-configuration so that it backs off when depending on Spring Security's OAuth2 Client and OAuth2 Resource Server modules. In the imperative case it will also back off when depending on the SAML 2 provider. Closes gh-35338
This commit is contained in:
@@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2Clien
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg
|
||||
import org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
|
||||
@@ -92,6 +94,13 @@ class ReactiveOAuth2ClientConfigurations {
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(WebFilterChainProxy.class)
|
||||
@EnableWebFluxSecurity
|
||||
static class EnableWebFluxSecurityConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
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.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
@@ -57,11 +58,12 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
@AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class, after = RSocketMessagingAutoConfiguration.class)
|
||||
@ConditionalOnClass({ ReactiveAuthenticationManager.class })
|
||||
@ConditionalOnMissingClass({ "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
|
||||
"org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector" })
|
||||
@ConditionalOnMissingBean(
|
||||
value = { ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class,
|
||||
ReactiveAuthenticationManagerResolver.class },
|
||||
type = { "org.springframework.security.oauth2.jwt.ReactiveJwtDecoder",
|
||||
"org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector" })
|
||||
type = { "org.springframework.security.oauth2.jwt.ReactiveJwtDecoder" })
|
||||
@Conditional(ReactiveUserDetailsServiceAutoConfiguration.ReactiveUserDetailsServiceCondition.class)
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class ReactiveUserDetailsServiceAutoConfiguration {
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
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.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
@@ -43,9 +44,7 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for a Spring Security in-memory
|
||||
* {@link AuthenticationManager}. Adds an {@link InMemoryUserDetailsManager} with a
|
||||
* default user and generated password. This can be disabled by providing a bean of type
|
||||
* {@link AuthenticationManager}, {@link AuthenticationProvider} or
|
||||
* {@link UserDetailsService}.
|
||||
* default user and generated password.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Rob Winch
|
||||
@@ -54,14 +53,12 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(AuthenticationManager.class)
|
||||
@ConditionalOnMissingClass({ "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
|
||||
"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector",
|
||||
"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository" })
|
||||
@ConditionalOnBean(ObjectPostProcessor.class)
|
||||
@ConditionalOnMissingBean(
|
||||
value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
|
||||
AuthenticationManagerResolver.class },
|
||||
type = { "org.springframework.security.oauth2.jwt.JwtDecoder",
|
||||
"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector",
|
||||
"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
|
||||
"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository" })
|
||||
@ConditionalOnMissingBean(value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
|
||||
AuthenticationManagerResolver.class }, type = "org.springframework.security.oauth2.jwt.JwtDecoder")
|
||||
public class UserDetailsServiceAutoConfiguration {
|
||||
|
||||
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfi
|
||||
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
@@ -59,8 +60,11 @@ class OAuth2AuthorizationServerAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "spring-security-oauth2-client-*.jar", "spring-security-oauth2-resource-server-*.jar",
|
||||
"spring-security-saml2-service-provider-*.jar" })
|
||||
void autoConfigurationDoesNotCauseUserDetailsServiceToBackOff() {
|
||||
this.contextRunner.run((context) -> assertThat(context).hasBean("inMemoryUserDetailsManager"));
|
||||
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(UserDetailsServiceAutoConfiguration.class)
|
||||
.hasBean("inMemoryUserDetailsManager"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -26,6 +26,8 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
|
||||
@@ -39,27 +41,24 @@ import static org.mockito.Mockito.mock;
|
||||
*/
|
||||
class ReactiveSecurityAutoConfigurationTests {
|
||||
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void backsOffWhenWebFilterChainProxyBeanPresent() {
|
||||
this.contextRunner.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
|
||||
.withUserConfiguration(WebFilterChainProxyConfiguration.class)
|
||||
this.contextRunner.withUserConfiguration(WebFilterChainProxyConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasSingleBean(WebFilterChainProxy.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffWhenReactiveAuthenticationManagerNotPresent() {
|
||||
this.contextRunner.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context).hasSingleBean(ReactiveSecurityAutoConfiguration.class)
|
||||
.doesNotHaveBean(EnableWebFluxSecurityConfiguration.class));
|
||||
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ReactiveSecurityAutoConfiguration.class)
|
||||
.doesNotHaveBean(EnableWebFluxSecurityConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void enablesWebFluxSecurity() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveUserDetailsServiceAutoConfiguration.class))
|
||||
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
.run((context) -> assertThat(context).getBean(WebFilterChainProxy.class).isNotNull());
|
||||
}
|
||||
|
||||
@@ -68,8 +67,7 @@ class ReactiveSecurityAutoConfigurationTests {
|
||||
this.contextRunner
|
||||
.withClassLoader(new FilteredClassLoader(Flux.class, EnableWebFluxSecurity.class, WebFilterChainProxy.class,
|
||||
WebFluxConfigurer.class))
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveUserDetailsServiceAutoConfiguration.class))
|
||||
.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(WebFilterChainProxy.class));
|
||||
}
|
||||
|
||||
@@ -83,4 +81,15 @@ class ReactiveSecurityAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UserDetailsServiceConfiguration {
|
||||
|
||||
@Bean
|
||||
MapReactiveUserDetailsService userDetailsService() {
|
||||
return new MapReactiveUserDetailsService(
|
||||
User.withUsername("alice").password("secret").roles("admin").build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfig
|
||||
import org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -39,6 +40,7 @@ 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.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
|
||||
|
||||
@@ -58,15 +60,21 @@ class ReactiveUserDetailsServiceAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void configuresADefaultUser() {
|
||||
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run((context) -> {
|
||||
ReactiveUserDetailsService userDetailsService = context.getBean(ReactiveUserDetailsService.class);
|
||||
assertThat(userDetailsService.findByUsername("user").block(Duration.ofSeconds(30))).isNotNull();
|
||||
});
|
||||
this.contextRunner
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(ClientRegistrationRepository.class, ReactiveOpaqueTokenIntrospector.class))
|
||||
.withUserConfiguration(TestSecurityConfiguration.class)
|
||||
.run((context) -> {
|
||||
ReactiveUserDetailsService userDetailsService = context.getBean(ReactiveUserDetailsService.class);
|
||||
assertThat(userDetailsService.findByUsername("user").block(Duration.ofSeconds(30))).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void userDetailsServiceWhenRSocketConfigured() {
|
||||
new ApplicationContextRunner()
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(ClientRegistrationRepository.class, ReactiveOpaqueTokenIntrospector.class))
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class,
|
||||
RSocketMessagingAutoConfiguration.class, RSocketStrategiesAutoConfiguration.class))
|
||||
.withUserConfiguration(TestRSocketSecurityConfiguration.class)
|
||||
@@ -109,20 +117,21 @@ class ReactiveUserDetailsServiceAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotConfigureDefaultUserIfResourceServerWithOpaqueIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(ReactiveOpaqueTokenIntrospectorConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(ReactiveOpaqueTokenIntrospector.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class);
|
||||
});
|
||||
void doesNotConfigureDefaultUserIfResourceServerIsPresent() {
|
||||
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
|
||||
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run(((context) -> {
|
||||
MapReactiveUserDetailsService userDetailsService = context.getBean(MapReactiveUserDetailsService.class);
|
||||
String password = userDetailsService.findByUsername("user").block(Duration.ofSeconds(30)).getPassword();
|
||||
assertThat(password).startsWith("{noop}");
|
||||
}));
|
||||
this.contextRunner
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(ClientRegistrationRepository.class, ReactiveOpaqueTokenIntrospector.class))
|
||||
.withUserConfiguration(TestSecurityConfiguration.class)
|
||||
.run(((context) -> {
|
||||
MapReactiveUserDetailsService userDetailsService = context.getBean(MapReactiveUserDetailsService.class);
|
||||
String password = userDetailsService.findByUsername("user").block(Duration.ofSeconds(30)).getPassword();
|
||||
assertThat(password).startsWith("{noop}");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,7 +151,10 @@ class ReactiveUserDetailsServiceAutoConfigurationTests {
|
||||
}
|
||||
|
||||
private void testPasswordEncoding(Class<?> configClass, String providedPassword, String expectedPassword) {
|
||||
this.contextRunner.withUserConfiguration(configClass)
|
||||
this.contextRunner
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(ClientRegistrationRepository.class, ReactiveOpaqueTokenIntrospector.class))
|
||||
.withUserConfiguration(configClass)
|
||||
.withPropertyValues("spring.security.user.password=" + providedPassword)
|
||||
.run(((context) -> {
|
||||
MapReactiveUserDetailsService userDetailsService = context.getBean(MapReactiveUserDetailsService.class);
|
||||
|
||||
@@ -22,12 +22,15 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
|
||||
import org.springframework.boot.rsocket.server.RSocketServerCustomizer;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
||||
import org.springframework.security.config.annotation.rsocket.RSocketSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.messaging.handler.invocation.reactive.AuthenticationPrincipalArgumentResolver;
|
||||
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
||||
|
||||
@@ -42,9 +45,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class RSocketSecurityAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class,
|
||||
RSocketSecurityAutoConfiguration.class, RSocketMessagingAutoConfiguration.class,
|
||||
RSocketStrategiesAutoConfiguration.class));
|
||||
.withConfiguration(AutoConfigurations.of(RSocketSecurityAutoConfiguration.class,
|
||||
RSocketMessagingAutoConfiguration.class, RSocketStrategiesAutoConfiguration.class))
|
||||
.withUserConfiguration(UserDetailsServiceConfiguration.class);
|
||||
|
||||
@Test
|
||||
void autoConfigurationEnablesRSocketSecurity() {
|
||||
@@ -81,4 +84,15 @@ class RSocketSecurityAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UserDetailsServiceConfiguration {
|
||||
|
||||
@Bean
|
||||
MapReactiveUserDetailsService userDetailsService() {
|
||||
return new MapReactiveUserDetailsService(
|
||||
User.withUsername("alice").password("secret").roles("admin").build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2023 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.
|
||||
@@ -37,6 +37,8 @@ import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -63,6 +65,9 @@ class SecurityFilterAutoConfigurationEarlyInitializationTests {
|
||||
Pattern.MULTILINE);
|
||||
|
||||
@Test
|
||||
@DirtiesUrlFactories
|
||||
@ClassPathExclusions({ "spring-security-oauth2-client-*.jar", "spring-security-oauth2-resource-server-*.jar",
|
||||
"spring-security-saml2-service-provider-*.jar" })
|
||||
void testSecurityFilterDoesNotCauseEarlyInitialization(CapturedOutput output) {
|
||||
try (AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext()) {
|
||||
TestPropertyValues.of("server.port:0").applyTo(context);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.autoconfigure.security.servlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -24,6 +25,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
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.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
@@ -64,7 +66,7 @@ class UserDetailsServiceAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void testDefaultUsernamePassword(CapturedOutput output) {
|
||||
this.contextRunner.run((context) -> {
|
||||
this.contextRunner.with(noOtherFormsOfAuthenticationOnTheClasspath()).run((context) -> {
|
||||
UserDetailsService manager = context.getBean(UserDetailsService.class);
|
||||
assertThat(output).contains("Using generated security password:");
|
||||
assertThat(manager.loadUserByUsername("user")).isNotNull();
|
||||
@@ -126,11 +128,13 @@ class UserDetailsServiceAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
|
||||
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run(((context) -> {
|
||||
InMemoryUserDetailsManager userDetailsService = context.getBean(InMemoryUserDetailsManager.class);
|
||||
String password = userDetailsService.loadUserByUsername("user").getPassword();
|
||||
assertThat(password).startsWith("{noop}");
|
||||
}));
|
||||
this.contextRunner.with(noOtherFormsOfAuthenticationOnTheClasspath())
|
||||
.withUserConfiguration(TestSecurityConfiguration.class)
|
||||
.run(((context) -> {
|
||||
InMemoryUserDetailsManager userDetailsService = context.getBean(InMemoryUserDetailsManager.class);
|
||||
String password = userDetailsService.loadUserByUsername("user").getPassword();
|
||||
assertThat(password).startsWith("{noop}");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -150,20 +154,39 @@ class UserDetailsServiceAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void userDetailsServiceWhenClientRegistrationRepositoryBeanPresent() {
|
||||
this.contextRunner.withUserConfiguration(TestConfigWithClientRegistrationRepository.class)
|
||||
void userDetailsServiceWhenClientRegistrationRepositoryPresent() {
|
||||
this.contextRunner
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(OpaqueTokenIntrospector.class, RelyingPartyRegistrationRepository.class))
|
||||
.run(((context) -> assertThat(context).doesNotHaveBean(InMemoryUserDetailsManager.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void userDetailsServiceWhenRelyingPartyRegistrationRepositoryBeanPresent() {
|
||||
void userDetailsServiceWhenOpaqueTokenIntrospectorPresent() {
|
||||
this.contextRunner
|
||||
.withBean(RelyingPartyRegistrationRepository.class, () -> mock(RelyingPartyRegistrationRepository.class))
|
||||
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class,
|
||||
RelyingPartyRegistrationRepository.class))
|
||||
.run(((context) -> assertThat(context).doesNotHaveBean(InMemoryUserDetailsManager.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void userDetailsServiceWhenRelyingPartyRegistrationRepositoryPresent() {
|
||||
this.contextRunner
|
||||
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class))
|
||||
.run(((context) -> assertThat(context).doesNotHaveBean(InMemoryUserDetailsManager.class)));
|
||||
}
|
||||
|
||||
private Function<ApplicationContextRunner, ApplicationContextRunner> noOtherFormsOfAuthenticationOnTheClasspath() {
|
||||
return (contextRunner) -> contextRunner
|
||||
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class,
|
||||
RelyingPartyRegistrationRepository.class));
|
||||
}
|
||||
|
||||
private void testPasswordEncoding(Class<?> configClass, String providedPassword, String expectedPassword) {
|
||||
this.contextRunner.withUserConfiguration(configClass)
|
||||
this.contextRunner.with(noOtherFormsOfAuthenticationOnTheClasspath())
|
||||
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class,
|
||||
RelyingPartyRegistrationRepository.class))
|
||||
.withUserConfiguration(configClass)
|
||||
.withPropertyValues("spring.security.user.password=" + providedPassword)
|
||||
.run(((context) -> {
|
||||
InMemoryUserDetailsManager userDetailsService = context.getBean(InMemoryUserDetailsManager.class);
|
||||
|
||||
Reference in New Issue
Block a user