From fcf5985f8f7f270a980d6170f31f83010d4f50a0 Mon Sep 17 00:00:00 2001 From: BenchmarkingBuffalo <46448799+benchmarkingbuffalo@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:34:34 +0100 Subject: [PATCH] Making ReactiveUserDetailsServiceAutoConfiguration conditional The UserDetailsServiceAutoConfiguration is only to be active in a reactive web application See gh-43345 --- ...veUserDetailsServiceAutoConfiguration.java | 3 ++ ...rDetailsServiceAutoConfigurationTests.java | 43 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java index 596f0d9c0b9d..6d84bc792da2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java @@ -31,6 +31,7 @@ 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.rsocket.RSocketMessagingAutoConfiguration; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -55,6 +56,7 @@ * {@link ReactiveAuthenticationManagerResolver}. * * @author Madhura Bhave + * @author Lasse Wulff * @since 2.0.0 */ @AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class, after = RSocketMessagingAutoConfiguration.class) @@ -65,6 +67,7 @@ type = { "org.springframework.security.oauth2.jwt.ReactiveJwtDecoder" }) @Conditional({ ReactiveUserDetailsServiceAutoConfiguration.RSocketEnabledOrReactiveWebApplication.class, ReactiveUserDetailsServiceAutoConfiguration.MissingAlternativeOrUserPropertiesConfigured.class }) +@ConditionalOnWebApplication(type = Type.REACTIVE) @EnableConfigurationProperties(SecurityProperties.class) public class ReactiveUserDetailsServiceAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java index 1b673b6dc962..a7e75a7f3725 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.security.reactive; import java.time.Duration; +import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -28,6 +29,7 @@ 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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -52,12 +54,34 @@ * * @author Madhura Bhave * @author HaiTao Zhang + * @author Lasse Wulff */ class ReactiveUserDetailsServiceAutoConfigurationTests { private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class)); + @Test + void shouldSupplyUserDetailsServiceInReactiveApp() { + this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class) + .with(AuthenticationExclude.reactiveApp()) + .run((context) -> assertThat(context).hasSingleBean(ReactiveUserDetailsService.class)); + } + @Test + void shouldNotSupplyUserDetailsServiceInServletApp() { + new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class)) + .with(AuthenticationExclude.servletApp()) + .run((context) -> assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class)); + } + @Test + void shouldNotSupplyUserDetailsServiceInNonWebApp() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class)) + .with(AuthenticationExclude.noWebApp()) + .run((context) -> assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class)); + } + @Test void configuresADefaultUser() { this.contextRunner @@ -72,7 +96,7 @@ void configuresADefaultUser() { @Test void userDetailsServiceWhenRSocketConfigured() { - new ApplicationContextRunner() + this.contextRunner .withClassLoader( new FilteredClassLoader(ClientRegistrationRepository.class, ReactiveOpaqueTokenIntrospector.class)) .withConfiguration(AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class, @@ -98,7 +122,7 @@ void doesNotConfigureDefaultUserIfUserDetailsServiceAvailable() { void doesNotConfigureDefaultUserIfAuthenticationManagerAvailable() { this.contextRunner.withUserConfiguration(AuthenticationManagerConfig.class, TestSecurityConfiguration.class) .withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class)) - .run((context) -> assertThat(context).getBean(ReactiveUserDetailsService.class).isNull()); + .run((context) -> assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class)); } @Test @@ -175,6 +199,21 @@ private void testPasswordEncoding(Class configClass, String providedPassword, })); } + private static final class AuthenticationExclude { + private static final FilteredClassLoader filteredClassLoader = new FilteredClassLoader( + ClientRegistrationRepository.class, ReactiveOpaqueTokenIntrospector.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) @EnableWebFluxSecurity @EnableConfigurationProperties(SecurityProperties.class)