|  | @@ -17,12 +17,16 @@
 | 
	
		
			
				|  |  |  package org.springframework.security.authentication.dao;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import static org.assertj.core.api.Assertions.assertThat;
 | 
	
		
			
				|  |  | +import static org.assertj.core.api.Assertions.assertThatThrownBy;
 | 
	
		
			
				|  |  |  import static org.assertj.core.api.Assertions.fail;
 | 
	
		
			
				|  |  | +import static org.mockito.ArgumentMatchers.any;
 | 
	
		
			
				|  |  | +import static org.mockito.ArgumentMatchers.eq;
 | 
	
		
			
				|  |  |  import static org.mockito.Matchers.anyString;
 | 
	
		
			
				|  |  |  import static org.mockito.Matchers.isA;
 | 
	
		
			
				|  |  |  import static org.mockito.Mockito.mock;
 | 
	
		
			
				|  |  |  import static org.mockito.Mockito.times;
 | 
	
		
			
				|  |  |  import static org.mockito.Mockito.verify;
 | 
	
		
			
				|  |  | +import static org.mockito.Mockito.verifyZeroInteractions;
 | 
	
		
			
				|  |  |  import static org.mockito.Mockito.when;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import java.security.SecureRandom;
 | 
	
	
		
			
				|  | @@ -43,6 +47,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
 | 
	
		
			
				|  |  |  import org.springframework.security.core.Authentication;
 | 
	
		
			
				|  |  |  import org.springframework.security.core.GrantedAuthority;
 | 
	
		
			
				|  |  |  import org.springframework.security.core.authority.AuthorityUtils;
 | 
	
		
			
				|  |  | +import org.springframework.security.core.userdetails.PasswordEncodedUser;
 | 
	
		
			
				|  |  |  import org.springframework.security.core.userdetails.User;
 | 
	
		
			
				|  |  |  import org.springframework.security.core.userdetails.UserDetails;
 | 
	
		
			
				|  |  |  import org.springframework.security.core.userdetails.UserDetailsService;
 | 
	
	
		
			
				|  | @@ -53,6 +58,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 | 
	
		
			
				|  |  |  import org.springframework.security.crypto.factory.PasswordEncoderFactories;
 | 
	
		
			
				|  |  |  import org.springframework.security.crypto.password.NoOpPasswordEncoder;
 | 
	
		
			
				|  |  |  import org.springframework.security.crypto.password.PasswordEncoder;
 | 
	
		
			
				|  |  | +import org.springframework.security.core.userdetails.UserDetailsPasswordService;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * Tests {@link DaoAuthenticationProvider}.
 | 
	
	
		
			
				|  | @@ -399,6 +405,80 @@ public class DaoAuthenticationProviderTests {
 | 
	
		
			
				|  |  |  		assertThat(castResult.getPrincipal()).isEqualTo("rod");
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void authenticateWhenSuccessAndPasswordManagerThenUpdates() {
 | 
	
		
			
				|  |  | +		String password = "password";
 | 
	
		
			
				|  |  | +		String encodedPassword = "encoded";
 | 
	
		
			
				|  |  | +		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
 | 
	
		
			
				|  |  | +				"user", password);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		PasswordEncoder encoder = mock(PasswordEncoder.class);
 | 
	
		
			
				|  |  | +		UserDetailsService userDetailsService = mock(UserDetailsService.class);
 | 
	
		
			
				|  |  | +		UserDetailsPasswordService passwordManager = mock(UserDetailsPasswordService.class);
 | 
	
		
			
				|  |  | +		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
 | 
	
		
			
				|  |  | +		provider.setPasswordEncoder(encoder);
 | 
	
		
			
				|  |  | +		provider.setUserDetailsService(userDetailsService);
 | 
	
		
			
				|  |  | +		provider.setUserDetailsPasswordService(passwordManager);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		UserDetails user = PasswordEncodedUser.user();
 | 
	
		
			
				|  |  | +		when(encoder.matches(any(), any())).thenReturn(true);
 | 
	
		
			
				|  |  | +		when(encoder.upgradeEncoding(any())).thenReturn(true);
 | 
	
		
			
				|  |  | +		when(encoder.encode(any())).thenReturn(encodedPassword);
 | 
	
		
			
				|  |  | +		when(userDetailsService.loadUserByUsername(any())).thenReturn(user);
 | 
	
		
			
				|  |  | +		when(passwordManager.updatePassword(any(), any())).thenReturn(user);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		Authentication result = provider.authenticate(token);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		verify(encoder).encode(password);
 | 
	
		
			
				|  |  | +		verify(passwordManager).updatePassword(eq(user), eq(encodedPassword));
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void authenticateWhenBadCredentialsAndPasswordManagerThenNoUpdate() {
 | 
	
		
			
				|  |  | +		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
 | 
	
		
			
				|  |  | +				"user", "password");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		PasswordEncoder encoder = mock(PasswordEncoder.class);
 | 
	
		
			
				|  |  | +		UserDetailsService userDetailsService = mock(UserDetailsService.class);
 | 
	
		
			
				|  |  | +		UserDetailsPasswordService passwordManager = mock(UserDetailsPasswordService.class);
 | 
	
		
			
				|  |  | +		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
 | 
	
		
			
				|  |  | +		provider.setPasswordEncoder(encoder);
 | 
	
		
			
				|  |  | +		provider.setUserDetailsService(userDetailsService);
 | 
	
		
			
				|  |  | +		provider.setUserDetailsPasswordService(passwordManager);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		UserDetails user = PasswordEncodedUser.user();
 | 
	
		
			
				|  |  | +		when(encoder.matches(any(), any())).thenReturn(false);
 | 
	
		
			
				|  |  | +		when(userDetailsService.loadUserByUsername(any())).thenReturn(user);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		assertThatThrownBy(() -> provider.authenticate(token))
 | 
	
		
			
				|  |  | +			.isInstanceOf(BadCredentialsException.class);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		verifyZeroInteractions(passwordManager);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void authenticateWhenNotUpgradeAndPasswordManagerThenNoUpdate() {
 | 
	
		
			
				|  |  | +		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
 | 
	
		
			
				|  |  | +				"user", "password");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		PasswordEncoder encoder = mock(PasswordEncoder.class);
 | 
	
		
			
				|  |  | +		UserDetailsService userDetailsService = mock(UserDetailsService.class);
 | 
	
		
			
				|  |  | +		UserDetailsPasswordService passwordManager = mock(UserDetailsPasswordService.class);
 | 
	
		
			
				|  |  | +		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
 | 
	
		
			
				|  |  | +		provider.setPasswordEncoder(encoder);
 | 
	
		
			
				|  |  | +		provider.setUserDetailsService(userDetailsService);
 | 
	
		
			
				|  |  | +		provider.setUserDetailsPasswordService(passwordManager);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		UserDetails user = PasswordEncodedUser.user();
 | 
	
		
			
				|  |  | +		when(encoder.matches(any(), any())).thenReturn(true);
 | 
	
		
			
				|  |  | +		when(encoder.upgradeEncoding(any())).thenReturn(false);
 | 
	
		
			
				|  |  | +		when(userDetailsService.loadUserByUsername(any())).thenReturn(user);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		Authentication result = provider.authenticate(token);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		verifyZeroInteractions(passwordManager);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	@Test
 | 
	
		
			
				|  |  |  	public void testDetectsNullBeingReturnedFromAuthenticationDao() {
 | 
	
		
			
				|  |  |  		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
 |