Skip to content

Commit 7f321bf

Browse files
committed
InMemoryUserDetailsManager preserve user type
Closes gh-3192
1 parent ef35c4a commit 7f321bf

File tree

2 files changed

+102
-3
lines changed

2 files changed

+102
-3
lines changed

core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
import org.springframework.security.authentication.AuthenticationManager;
3131
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
3232
import org.springframework.security.core.Authentication;
33+
import org.springframework.security.core.CredentialsContainer;
3334
import org.springframework.security.core.context.SecurityContextHolder;
3435
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3536
import org.springframework.security.core.userdetails.User;
@@ -96,7 +97,13 @@ private User createUserDetails(String name, UserAttribute attr) {
9697
@Override
9798
public void createUser(UserDetails user) {
9899
Assert.isTrue(!userExists(user.getUsername()), "user should not exist");
99-
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
100+
101+
if (user instanceof MutableUserDetails mutable) {
102+
this.users.put(user.getUsername().toLowerCase(), mutable);
103+
}
104+
else {
105+
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
106+
}
100107
}
101108

102109
@Override
@@ -154,6 +161,9 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx
154161
if (user == null) {
155162
throw new UsernameNotFoundException(username);
156163
}
164+
if (user instanceof CredentialsContainer) {
165+
return user;
166+
}
157167
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),
158168
user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
159169
}

core/src/test/java/org/springframework/security/provisioning/InMemoryUserDetailsManagerTests.java

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,19 +16,24 @@
1616

1717
package org.springframework.security.provisioning;
1818

19+
import java.util.Collection;
1920
import java.util.Properties;
2021

2122
import org.junit.jupiter.api.Test;
2223

2324
import org.springframework.security.authentication.TestAuthentication;
2425
import org.springframework.security.core.Authentication;
26+
import org.springframework.security.core.CredentialsContainer;
27+
import org.springframework.security.core.GrantedAuthority;
2528
import org.springframework.security.core.context.SecurityContextHolderStrategy;
2629
import org.springframework.security.core.context.SecurityContextImpl;
2730
import org.springframework.security.core.userdetails.PasswordEncodedUser;
2831
import org.springframework.security.core.userdetails.User;
2932
import org.springframework.security.core.userdetails.UserDetails;
33+
import org.springframework.security.core.userdetails.UsernameNotFoundException;
3034

3135
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3237
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3338
import static org.mockito.BDDMockito.given;
3439
import static org.mockito.Mockito.mock;
@@ -97,4 +102,88 @@ public void changePasswordWhenCustomSecurityContextHolderStrategyThenUses() {
97102
verify(strategy).getContext();
98103
}
99104

105+
@Test
106+
public void createUserWhenUserAlreadyExistsThenException() {
107+
assertThatIllegalArgumentException().isThrownBy(() -> this.manager.createUser(this.user))
108+
.withMessage("user should not exist");
109+
}
110+
111+
@Test
112+
public void createUserWhenNotInstanceOfMutableUserDetailsThenShouldWrapIntoMutableUser() {
113+
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
114+
final UserDetails user = User.withUserDetails(PasswordEncodedUser.user()).build();
115+
manager.createUser(user);
116+
UserDetails userDetails = manager.loadUserByUsername(user.getUsername());
117+
assertThat(userDetails).isInstanceOf(User.class);
118+
}
119+
120+
@Test
121+
public void createUserWhenInstanceOfMutableUserDetailsThenShouldNotWrapIntoMutableUser() {
122+
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
123+
CustomUser user = new CustomUser(User.withUserDetails(PasswordEncodedUser.user()).build());
124+
manager.createUser(user);
125+
UserDetails userDetails = manager.loadUserByUsername(user.getUsername());
126+
assertThat(userDetails).isInstanceOf(CustomUser.class);
127+
}
128+
129+
@Test
130+
public void loadUserByUsernameWhenUserNullThenException() {
131+
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
132+
assertThatExceptionOfType(UsernameNotFoundException.class)
133+
.isThrownBy(() -> manager.loadUserByUsername(this.user.getUsername()));
134+
}
135+
136+
@Test
137+
public void loadUserByUsernameWhenUserDetailsNotInstanceOfCredentialsContainerThenReturnUser() {
138+
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(this.user);
139+
UserDetails userDetails = manager.loadUserByUsername(this.user.getUsername());
140+
assertThat(userDetails).isInstanceOf(User.class);
141+
}
142+
143+
@Test
144+
public void loadUserByUsernameWhenUserDetailsInstanceOfCredentialsContainerThenReturnUserDetails() {
145+
CustomUser user = new CustomUser(User.withUserDetails(PasswordEncodedUser.user()).build());
146+
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(user);
147+
UserDetails userDetails = manager.loadUserByUsername(user.getUsername());
148+
assertThat(userDetails).isInstanceOf(CustomUser.class);
149+
}
150+
151+
static class CustomUser implements MutableUserDetails, CredentialsContainer {
152+
153+
private final UserDetails delegate;
154+
155+
private String password;
156+
157+
CustomUser(UserDetails user) {
158+
this.delegate = user;
159+
this.password = user.getPassword();
160+
}
161+
162+
@Override
163+
public Collection<? extends GrantedAuthority> getAuthorities() {
164+
return this.delegate.getAuthorities();
165+
}
166+
167+
@Override
168+
public String getPassword() {
169+
return this.delegate.getPassword();
170+
}
171+
172+
@Override
173+
public void setPassword(final String password) {
174+
this.password = password;
175+
}
176+
177+
@Override
178+
public String getUsername() {
179+
return this.delegate.getUsername();
180+
}
181+
182+
@Override
183+
public void eraseCredentials() {
184+
this.password = null;
185+
}
186+
187+
}
188+
100189
}

0 commit comments

Comments
 (0)