From c5d41f14697927babcf4ae0d0ffe07b49fcaab76 Mon Sep 17 00:00:00 2001 From: BenchmarkingBuffalo <46448799+benchmarkingbuffalo@users.noreply.github.com> Date: Sun, 1 Dec 2024 00:00:04 +0100 Subject: [PATCH 1/2] Make UserDetailsServiceAutoConfiguration conditional on servlet app See gh-43334 --- .../servlet/UserDetailsServiceAutoConfiguration.java | 4 ++++ .../servlet/UserDetailsServiceAutoConfigurationTests.java | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java index eac402fe0a..a6b8030837 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java @@ -31,6 +31,8 @@ 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.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration.MissingAlternativeOrUserPropertiesConfigured; import org.springframework.context.annotation.Bean; @@ -53,6 +55,7 @@ import org.springframework.util.StringUtils; * @author Dave Syer * @author Rob Winch * @author Madhura Bhave + * @author Lasse Wulff * @since 2.0.0 */ @AutoConfiguration @@ -61,6 +64,7 @@ import org.springframework.util.StringUtils; @ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnMissingBean(value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class }, type = "org.springframework.security.oauth2.jwt.JwtDecoder") +@ConditionalOnWebApplication(type = Type.SERVLET) public class UserDetailsServiceAutoConfiguration { private static final String NOOP_PASSWORD_PREFIX = "{noop}"; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java index 6de9275db2..d80e3a686c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java @@ -26,7 +26,7 @@ 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.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.context.annotation.Bean; @@ -56,11 +56,12 @@ import static org.mockito.Mockito.mock; * * @author Madhura Bhave * @author HaiTao Zhang + * @author Lasse Wulff */ @ExtendWith(OutputCaptureExtension.class) class UserDetailsServiceAutoConfigurationTests { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() .withUserConfiguration(TestSecurityConfiguration.class) .withConfiguration(AutoConfigurations.of(UserDetailsServiceAutoConfiguration.class)); @@ -192,7 +193,7 @@ class UserDetailsServiceAutoConfigurationTests { .run(((context) -> assertThat(context).hasSingleBean(InMemoryUserDetailsManager.class))); } - private Function noOtherFormsOfAuthenticationOnTheClasspath() { + private Function noOtherFormsOfAuthenticationOnTheClasspath() { return (contextRunner) -> contextRunner .withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class, RelyingPartyRegistrationRepository.class)); From ff9fde0ef4a116bf13f0c5860414b0b2798fbdda Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Mon, 2 Dec 2024 13:30:36 +0100 Subject: [PATCH 2/2] Polish "Make UserDetailsServiceAutoConfiguration conditional on servlet app" See gh-43334 --- ...rDetailsServiceAutoConfigurationTests.java | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java index d80e3a686c..6c4ee3c918 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java @@ -26,6 +26,8 @@ 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.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; @@ -57,6 +59,7 @@ import static org.mockito.Mockito.mock; * @author Madhura Bhave * @author HaiTao Zhang * @author Lasse Wulff + * @author Moritz Halbritter */ @ExtendWith(OutputCaptureExtension.class) class UserDetailsServiceAutoConfigurationTests { @@ -65,9 +68,31 @@ class UserDetailsServiceAutoConfigurationTests { .withUserConfiguration(TestSecurityConfiguration.class) .withConfiguration(AutoConfigurations.of(UserDetailsServiceAutoConfiguration.class)); + @Test + void shouldSupplyUserDetailsServiceInServletApp() { + this.contextRunner.with(AuthenticationExclude.servletApp()) + .run((context) -> assertThat(context).hasSingleBean(UserDetailsService.class)); + } + + @Test + void shouldNotSupplyUserDetailsServiceInReactiveApp() { + new ReactiveWebApplicationContextRunner().withUserConfiguration(TestSecurityConfiguration.class) + .withConfiguration(AutoConfigurations.of(UserDetailsServiceAutoConfiguration.class)) + .with(AuthenticationExclude.reactiveApp()) + .run((context) -> assertThat(context).doesNotHaveBean(UserDetailsService.class)); + } + + @Test + void shouldNotSupplyUserDetailsServiceInNonWebApp() { + new ApplicationContextRunner().withUserConfiguration(TestSecurityConfiguration.class) + .withConfiguration(AutoConfigurations.of(UserDetailsServiceAutoConfiguration.class)) + .with(AuthenticationExclude.noWebApp()) + .run((context) -> assertThat(context).doesNotHaveBean(UserDetailsService.class)); + } + @Test void testDefaultUsernamePassword(CapturedOutput output) { - this.contextRunner.with(noOtherFormsOfAuthenticationOnTheClasspath()).run((context) -> { + this.contextRunner.with(AuthenticationExclude.servletApp()).run((context) -> { UserDetailsService manager = context.getBean(UserDetailsService.class); assertThat(output).contains("Using generated security password:"); assertThat(manager.loadUserByUsername("user")).isNotNull(); @@ -129,7 +154,7 @@ class UserDetailsServiceAutoConfigurationTests { @Test void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() { - this.contextRunner.with(noOtherFormsOfAuthenticationOnTheClasspath()) + this.contextRunner.with(AuthenticationExclude.servletApp()) .withUserConfiguration(TestSecurityConfiguration.class) .run(((context) -> { InMemoryUserDetailsManager userDetailsService = context.getBean(InMemoryUserDetailsManager.class); @@ -193,14 +218,8 @@ class UserDetailsServiceAutoConfigurationTests { .run(((context) -> assertThat(context).hasSingleBean(InMemoryUserDetailsManager.class))); } - private Function noOtherFormsOfAuthenticationOnTheClasspath() { - return (contextRunner) -> contextRunner - .withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class, - RelyingPartyRegistrationRepository.class)); - } - private void testPasswordEncoding(Class configClass, String providedPassword, String expectedPassword) { - this.contextRunner.with(noOtherFormsOfAuthenticationOnTheClasspath()) + this.contextRunner.with(AuthenticationExclude.servletApp()) .withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class, RelyingPartyRegistrationRepository.class)) .withUserConfiguration(configClass) @@ -212,6 +231,26 @@ class UserDetailsServiceAutoConfigurationTests { })); } + private static final class AuthenticationExclude { + + private static final FilteredClassLoader filteredClassLoader = new FilteredClassLoader( + ClientRegistrationRepository.class, OpaqueTokenIntrospector.class, + RelyingPartyRegistrationRepository.class); + + static Function servletApp() { + return (contextRunner) -> contextRunner.withClassLoader(filteredClassLoader); + } + + static Function reactiveApp() { + return (contextRunner) -> contextRunner.withClassLoader(filteredClassLoader); + } + + static Function noWebApp() { + return (contextRunner) -> contextRunner.withClassLoader(filteredClassLoader); + } + + } + @Configuration(proxyBeanMethods = false) static class TestAuthenticationManagerConfiguration {