Make user details only back off without custom username or password
Closes gh-38864
This commit is contained in:
parent
0f53415451
commit
961da4e428
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
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.ConditionalOnMissingClass;
|
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;
|
||||||
import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
|
import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||||
|
@ -58,13 +59,12 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class, after = RSocketMessagingAutoConfiguration.class)
|
@AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class, after = RSocketMessagingAutoConfiguration.class)
|
||||||
@ConditionalOnClass({ ReactiveAuthenticationManager.class })
|
@ConditionalOnClass({ ReactiveAuthenticationManager.class })
|
||||||
@ConditionalOnMissingClass({ "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
|
|
||||||
"org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector" })
|
|
||||||
@ConditionalOnMissingBean(
|
@ConditionalOnMissingBean(
|
||||||
value = { ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class,
|
value = { ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class,
|
||||||
ReactiveAuthenticationManagerResolver.class },
|
ReactiveAuthenticationManagerResolver.class },
|
||||||
type = { "org.springframework.security.oauth2.jwt.ReactiveJwtDecoder" })
|
type = { "org.springframework.security.oauth2.jwt.ReactiveJwtDecoder" })
|
||||||
@Conditional(ReactiveUserDetailsServiceAutoConfiguration.ReactiveUserDetailsServiceCondition.class)
|
@Conditional({ ReactiveUserDetailsServiceAutoConfiguration.RSocketEnabledOrReactiveWebApplication.class,
|
||||||
|
ReactiveUserDetailsServiceAutoConfiguration.MissingAlternativeOrUserPropertiesConfigured.class })
|
||||||
@EnableConfigurationProperties(SecurityProperties.class)
|
@EnableConfigurationProperties(SecurityProperties.class)
|
||||||
public class ReactiveUserDetailsServiceAutoConfiguration {
|
public class ReactiveUserDetailsServiceAutoConfiguration {
|
||||||
|
|
||||||
|
@ -98,9 +98,9 @@ public class ReactiveUserDetailsServiceAutoConfiguration {
|
||||||
return NOOP_PASSWORD_PREFIX + password;
|
return NOOP_PASSWORD_PREFIX + password;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ReactiveUserDetailsServiceCondition extends AnyNestedCondition {
|
static class RSocketEnabledOrReactiveWebApplication extends AnyNestedCondition {
|
||||||
|
|
||||||
ReactiveUserDetailsServiceCondition() {
|
RSocketEnabledOrReactiveWebApplication() {
|
||||||
super(ConfigurationPhase.REGISTER_BEAN);
|
super(ConfigurationPhase.REGISTER_BEAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,4 +116,29 @@ public class ReactiveUserDetailsServiceAutoConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class MissingAlternativeOrUserPropertiesConfigured extends AnyNestedCondition {
|
||||||
|
|
||||||
|
MissingAlternativeOrUserPropertiesConfigured() {
|
||||||
|
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnMissingClass({
|
||||||
|
"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
|
||||||
|
"org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector" })
|
||||||
|
static final class MissingAlternative {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnProperty(prefix = "spring.security.user", name = "name")
|
||||||
|
static final class NameConfigured {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnProperty(prefix = "spring.security.user", name = "password")
|
||||||
|
static final class PasswordConfigured {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -25,12 +25,16 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||||
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.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration.MissingAlternativeOrUserPropertiesConfigured;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
@ -53,9 +57,7 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@ConditionalOnClass(AuthenticationManager.class)
|
@ConditionalOnClass(AuthenticationManager.class)
|
||||||
@ConditionalOnMissingClass({ "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
|
@Conditional(MissingAlternativeOrUserPropertiesConfigured.class)
|
||||||
"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector",
|
|
||||||
"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository" })
|
|
||||||
@ConditionalOnBean(ObjectPostProcessor.class)
|
@ConditionalOnBean(ObjectPostProcessor.class)
|
||||||
@ConditionalOnMissingBean(value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
|
@ConditionalOnMissingBean(value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
|
||||||
AuthenticationManagerResolver.class }, type = "org.springframework.security.oauth2.jwt.JwtDecoder")
|
AuthenticationManagerResolver.class }, type = "org.springframework.security.oauth2.jwt.JwtDecoder")
|
||||||
|
@ -93,4 +95,30 @@ public class UserDetailsServiceAutoConfiguration {
|
||||||
return NOOP_PASSWORD_PREFIX + password;
|
return NOOP_PASSWORD_PREFIX + password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class MissingAlternativeOrUserPropertiesConfigured extends AnyNestedCondition {
|
||||||
|
|
||||||
|
MissingAlternativeOrUserPropertiesConfigured() {
|
||||||
|
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnMissingClass({
|
||||||
|
"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
|
||||||
|
"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector",
|
||||||
|
"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository" })
|
||||||
|
static final class MissingAlternative {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnProperty(prefix = "spring.security.user", name = "name")
|
||||||
|
static final class NameConfigured {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnProperty(prefix = "spring.security.user", name = "password")
|
||||||
|
static final class PasswordConfigured {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -121,6 +121,18 @@ class ReactiveUserDetailsServiceAutoConfigurationTests {
|
||||||
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class));
|
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void configuresDefaultUserWhenResourceServerIsPresentAndUsernameIsConfigured() {
|
||||||
|
this.contextRunner.withPropertyValues("spring.security.user.name=carol")
|
||||||
|
.run((context) -> assertThat(context).hasSingleBean(ReactiveUserDetailsService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void configuresDefaultUserWhenResourceServerIsPresentAndPasswordIsConfigured() {
|
||||||
|
this.contextRunner.withPropertyValues("spring.security.user.password=p4ssw0rd")
|
||||||
|
.run((context) -> assertThat(context).hasSingleBean(ReactiveUserDetailsService.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
|
void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -23,8 +23,10 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
|
||||||
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.logging.LogLevel;
|
||||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
import org.springframework.boot.test.system.CapturedOutput;
|
import org.springframework.boot.test.system.CapturedOutput;
|
||||||
|
@ -176,6 +178,23 @@ class UserDetailsServiceAutoConfigurationTests {
|
||||||
.run(((context) -> assertThat(context).doesNotHaveBean(InMemoryUserDetailsManager.class)));
|
.run(((context) -> assertThat(context).doesNotHaveBean(InMemoryUserDetailsManager.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void userDetailsServiceWhenRelyingPartyRegistrationRepositoryPresentAndUsernameConfigured() {
|
||||||
|
this.contextRunner
|
||||||
|
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class))
|
||||||
|
.withPropertyValues("spring.security.user.name=alice")
|
||||||
|
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
|
||||||
|
.run(((context) -> assertThat(context).hasSingleBean(InMemoryUserDetailsManager.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void userDetailsServiceWhenRelyingPartyRegistrationRepositoryPresentAndPasswordConfigured() {
|
||||||
|
this.contextRunner
|
||||||
|
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class))
|
||||||
|
.withPropertyValues("spring.security.user.password=secret")
|
||||||
|
.run(((context) -> assertThat(context).hasSingleBean(InMemoryUserDetailsManager.class)));
|
||||||
|
}
|
||||||
|
|
||||||
private Function<ApplicationContextRunner, ApplicationContextRunner> noOtherFormsOfAuthenticationOnTheClasspath() {
|
private Function<ApplicationContextRunner, ApplicationContextRunner> noOtherFormsOfAuthenticationOnTheClasspath() {
|
||||||
return (contextRunner) -> contextRunner
|
return (contextRunner) -> contextRunner
|
||||||
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class,
|
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class,
|
||||||
|
|
Loading…
Reference in New Issue