Skip to content

Commit

Permalink
Make user details only back off without custom username or password
Browse files Browse the repository at this point in the history
Closes gh-38864
  • Loading branch information
wilkinsona committed Jan 18, 2024
1 parent 0f53415 commit 961da4e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,6 +29,7 @@
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.rsocket.RSocketMessagingAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
Expand Down Expand Up @@ -58,13 +59,12 @@
*/
@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" })
@Conditional(ReactiveUserDetailsServiceAutoConfiguration.ReactiveUserDetailsServiceCondition.class)
@Conditional({ ReactiveUserDetailsServiceAutoConfiguration.RSocketEnabledOrReactiveWebApplication.class,
ReactiveUserDetailsServiceAutoConfiguration.MissingAlternativeOrUserPropertiesConfigured.class })
@EnableConfigurationProperties(SecurityProperties.class)
public class ReactiveUserDetailsServiceAutoConfiguration {

Expand Down Expand Up @@ -98,9 +98,9 @@ private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder
return NOOP_PASSWORD_PREFIX + password;
}

static class ReactiveUserDetailsServiceCondition extends AnyNestedCondition {
static class RSocketEnabledOrReactiveWebApplication extends AnyNestedCondition {

ReactiveUserDetailsServiceCondition() {
RSocketEnabledOrReactiveWebApplication() {
super(ConfigurationPhase.REGISTER_BEAN);
}

Expand All @@ -116,4 +116,29 @@ static class ReactiveWebApplicationCondition {

}

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 {

}

}

}
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand All @@ -25,12 +25,16 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
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.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.security.SecurityProperties;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration.MissingAlternativeOrUserPropertiesConfigured;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.AuthenticationProvider;
Expand All @@ -53,9 +57,7 @@
*/
@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" })
@Conditional(MissingAlternativeOrUserPropertiesConfigured.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean(value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
AuthenticationManagerResolver.class }, type = "org.springframework.security.oauth2.jwt.JwtDecoder")
Expand Down Expand Up @@ -93,4 +95,30 @@ private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder
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 {

}

}

}
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -121,6 +121,18 @@ void doesNotConfigureDefaultUserIfResourceServerIsPresent() {
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
void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
this.contextRunner
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand All @@ -23,8 +23,10 @@
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
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.runner.ApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput;
Expand Down Expand Up @@ -176,6 +178,23 @@ void userDetailsServiceWhenRelyingPartyRegistrationRepositoryPresent() {
.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() {
return (contextRunner) -> contextRunner
.withClassLoader(new FilteredClassLoader(ClientRegistrationRepository.class, OpaqueTokenIntrospector.class,
Expand Down

0 comments on commit 961da4e

Please # to comment.