Configuration Support for UserDetailsPasswordManager
Issue: gh-2778
This commit is contained in:
parent
7aaf70d582
commit
3ca5810bc8
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue