Skip to content

Commit

Permalink
InMemoryUserDetailsManager preserve user type
Browse files Browse the repository at this point in the history
Closes gh-3192
  • Loading branch information
MrJovanovic13 authored and jzheaux committed Aug 9, 2024
1 parent 503d653 commit 6d657ea
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,6 +30,7 @@
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.User;
Expand Down Expand Up @@ -96,7 +97,13 @@ private User createUserDetails(String name, UserAttribute attr) {
@Override
public void createUser(UserDetails user) {
Assert.isTrue(!userExists(user.getUsername()), "user should not exist");
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));

if (user instanceof MutableUserDetails mutable) {
this.users.put(user.getUsername().toLowerCase(), mutable);
}
else {
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
}
}

@Override
Expand All @@ -107,7 +114,13 @@ public void deleteUser(String username) {
@Override
public void updateUser(UserDetails user) {
Assert.isTrue(userExists(user.getUsername()), "user should exist");
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));

if (user instanceof MutableUserDetails mutable) {
this.users.put(user.getUsername().toLowerCase(), mutable);
}
else {
this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
}
}

@Override
Expand Down Expand Up @@ -154,6 +167,9 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx
if (user == null) {
throw new UsernameNotFoundException(username);
}
if (user instanceof CredentialsContainer) {
return user;
}
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),
user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

package org.springframework.security.provisioning;

import java.util.Collection;
import java.util.Properties;

import org.junit.jupiter.api.Test;

import org.springframework.security.authentication.TestAuthentication;
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.SecurityContextImpl;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
Expand Down Expand Up @@ -105,6 +108,20 @@ public void createUserWhenUserAlreadyExistsThenException() {
.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
public void updateUserWhenUserDoesNotExistThenException() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
Expand All @@ -119,4 +136,57 @@ public void loadUserByUsernameWhenUserNullThenException() {
.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;
}

}

}

0 comments on commit 6d657ea

Please sign in to comment.