InMemoryUserDetailsManager preserve user type

Closes gh-3192
This commit is contained in:
MrJovanovic13 2024-08-01 01:11:39 +02:00 committed by Josh Cummings
parent 503d653cea
commit 6d657ea3da
2 changed files with 89 additions and 3 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-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.
@ -30,6 +30,7 @@ import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
@ -96,8 +97,14 @@ public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetai
@Override @Override
public void createUser(UserDetails user) { public void createUser(UserDetails user) {
Assert.isTrue(!userExists(user.getUsername()), "user should not exist"); Assert.isTrue(!userExists(user.getUsername()), "user should not exist");
if (user instanceof MutableUserDetails mutable) {
this.users.put(user.getUsername().toLowerCase(), mutable);
}
else {
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user)); this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
} }
}
@Override @Override
public void deleteUser(String username) { public void deleteUser(String username) {
@ -107,8 +114,14 @@ public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetai
@Override @Override
public void updateUser(UserDetails user) { public void updateUser(UserDetails user) {
Assert.isTrue(userExists(user.getUsername()), "user should exist"); Assert.isTrue(userExists(user.getUsername()), "user should exist");
if (user instanceof MutableUserDetails mutable) {
this.users.put(user.getUsername().toLowerCase(), mutable);
}
else {
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user)); this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
} }
}
@Override @Override
public boolean userExists(String username) { public boolean userExists(String username) {
@ -154,6 +167,9 @@ public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetai
if (user == null) { if (user == null) {
throw new UsernameNotFoundException(username); throw new UsernameNotFoundException(username);
} }
if (user instanceof CredentialsContainer) {
return user;
}
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),
user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities()); user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
} }

View File

@ -16,12 +16,15 @@
package org.springframework.security.provisioning; package org.springframework.security.provisioning;
import java.util.Collection;
import java.util.Properties; import java.util.Properties;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.TestAuthentication; import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.core.userdetails.PasswordEncodedUser;
@ -105,6 +108,20 @@ public class InMemoryUserDetailsManagerTests {
.withMessage("user should not exist"); .withMessage("user should not exist");
} }
@Test
public void createUserWhenInstanceOfMutableUserDetailsThenChangePasswordWorks() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
CustomUser user = new CustomUser(User.withUserDetails(PasswordEncodedUser.user()).build());
Authentication authentication = TestAuthentication.authenticated(user);
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
given(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));
manager.setSecurityContextHolderStrategy(strategy);
manager.createUser(user);
String newPassword = "newPassword";
manager.changePassword(user.getPassword(), newPassword);
assertThat(manager.loadUserByUsername(user.getUsername()).getPassword()).isEqualTo(newPassword);
}
@Test @Test
public void updateUserWhenUserDoesNotExistThenException() { public void updateUserWhenUserDoesNotExistThenException() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
@ -119,4 +136,57 @@ public class InMemoryUserDetailsManagerTests {
.isThrownBy(() -> manager.loadUserByUsername(this.user.getUsername())); .isThrownBy(() -> manager.loadUserByUsername(this.user.getUsername()));
} }
@Test
public void loadUserByUsernameWhenNotInstanceOfCredentialsContainerThenReturnInstanceOfCredentialsContainer() {
MutableUser user = new MutableUser(User.withUserDetails(PasswordEncodedUser.user()).build());
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(user);
assertThat(user).isNotInstanceOf(CredentialsContainer.class);
assertThat(manager.loadUserByUsername(user.getUsername())).isInstanceOf(CredentialsContainer.class);
}
@Test
public void loadUserByUsernameWhenInstanceOfCredentialsContainerThenReturnInstance() {
CustomUser user = new CustomUser(User.withUserDetails(PasswordEncodedUser.user()).build());
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(user);
assertThat(manager.loadUserByUsername(user.getUsername())).isSameAs(user);
}
static class CustomUser implements MutableUserDetails, CredentialsContainer {
private final UserDetails delegate;
private String password;
CustomUser(UserDetails user) {
this.delegate = user;
this.password = user.getPassword();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.delegate.getAuthorities();
}
@Override
public String getPassword() {
return this.password;
}
@Override
public void setPassword(final String password) {
this.password = password;
}
@Override
public String getUsername() {
return this.delegate.getUsername();
}
@Override
public void eraseCredentials() {
this.password = null;
}
}
} }