Configuration Support for UserDetailsPasswordManager

Issue: gh-2778
This commit is contained in:
Rob Winch 2018-07-14 21:13:39 -05:00
parent 7aaf70d582
commit 3ca5810bc8
5 changed files with 114 additions and 11 deletions

View File

@ -22,6 +22,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
/**
* Lazily initializes the global authentication with a {@link UserDetailsService} if it is
@ -65,12 +66,16 @@ class InitializeUserDetailsBeanManagerConfigurer
}
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
}
if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager);
}
provider.afterPropertiesSet();
auth.authenticationProvider(provider);
@ -90,4 +95,4 @@ class InitializeUserDetailsBeanManagerConfigurer
.getBean(userDetailsBeanNames[0], type);
}
}
}
}

View File

@ -21,6 +21,7 @@ import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
/**
* Allows configuring a {@link DaoAuthenticationProvider}
@ -46,6 +47,9 @@ abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuil
protected AbstractDaoAuthenticationConfigurer(U userDetailsService) {
this.userDetailsService = userDetailsService;
provider.setUserDetailsService(userDetailsService);
if (userDetailsService instanceof UserDetailsPasswordService) {
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
}
}
/**
@ -73,6 +77,11 @@ abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuil
return (C) this;
}
public C userDetailsPasswordManager(UserDetailsPasswordService passwordManager) {
provider.setUserDetailsPasswordService(passwordManager);
return (C) this;
}
@Override
public void configure(B builder) throws Exception {
provider = postProcess(provider);

View File

@ -52,6 +52,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import java.util.ArrayList;
import java.util.Arrays;
@ -60,6 +61,8 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -395,6 +398,35 @@ public class AuthenticationConfigurationTests {
}
}
@Test
public void getAuthenticationWhenUserDetailsServiceAndPasswordManagerThenManagerUsed() throws Exception {
UserDetails user = new User("user", "{noop}password",
AuthorityUtils.createAuthorityList("ROLE_USER"));
this.spring.register(UserDetailsPasswordManagerBeanConfig.class).autowire();
UserDetailsPasswordManagerBeanConfig.Manager manager = this.spring.getContext().getBean(UserDetailsPasswordManagerBeanConfig.Manager.class);
AuthenticationManager am = this.spring.getContext().getBean(AuthenticationConfiguration.class).getAuthenticationManager();
when(manager.loadUserByUsername("user")).thenReturn(User.withUserDetails(user).build(), User.withUserDetails(user).build());
when(manager.updatePassword(any(), any())).thenReturn(user);
am.authenticate(new UsernamePasswordAuthenticationToken("user", "password"));
verify(manager).updatePassword(eq(user), startsWith("{bcrypt}"));
}
@Configuration
@Import({AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class})
static class UserDetailsPasswordManagerBeanConfig {
Manager manager = mock(Manager.class);
@Bean
UserDetailsService userDetailsService() {
return this.manager;
}
interface Manager extends UserDetailsService, UserDetailsPasswordService {
}
}
//gh-3091
@Test
public void getAuthenticationWhenAuthenticationProviderBeanThenUsed() throws Exception {

View File

@ -59,7 +59,9 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.ThrowableAssert.catchThrowable;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
@ -175,6 +177,62 @@ public class WebSecurityConfigurerAdapterTests {
}
}
@Test
public void loadConfigWhenInMemoryConfigureProtectedThenPasswordUpgraded() throws Exception {
this.spring.register(InMemoryConfigureProtectedConfig.class).autowire();
this.mockMvc.perform(formLogin())
.andExpect(status().is3xxRedirection());
UserDetailsService uds = this.spring.getContext()
.getBean(UserDetailsService.class);
assertThat(uds.loadUserByUsername("user").getPassword()).startsWith("{bcrypt}");
}
@EnableWebSecurity
static class InMemoryConfigureProtectedConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
}
@Override
@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
}
@Test
public void loadConfigWhenInMemoryConfigureGlobalThenPasswordUpgraded() throws Exception {
this.spring.register(InMemoryConfigureGlobalConfig.class).autowire();
this.mockMvc.perform(formLogin())
.andExpect(status().is3xxRedirection());
UserDetailsService uds = this.spring.getContext()
.getBean(UserDetailsService.class);
assertThat(uds.loadUserByUsername("user").getPassword()).startsWith("{bcrypt}");
}
@EnableWebSecurity
static class InMemoryConfigureGlobalConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
}
@Override
@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
}
@Test
public void loadConfigWhenCustomContentNegotiationStrategyBeanThenOverridesDefault() throws Exception {
OverrideContentNegotiationStrategySharedObjectConfig.CONTENT_NEGOTIATION_STRATEGY_BEAN = mock(ContentNegotiationStrategy.class);

View File

@ -16,14 +16,6 @@
package org.springframework.security.config.annotation.web.reactive;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.basicAuthenticationCredentials;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -69,9 +61,16 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.result.view.AbstractView;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.basicAuthenticationCredentials;
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
/**
* @author Rob Winch
* @since 5.0