From a0fb324e1db5ecad6c1c2f3bab941662edae91e7 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 20 Oct 2017 13:56:03 -0500 Subject: [PATCH] Add passwordEncoder to UserBuilder Fixes gh-4677 --- .../security/core/userdetails/User.java | 19 ++++++++++- .../security/core/userdetails/UserTests.java | 33 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/springframework/security/core/userdetails/User.java b/core/src/main/java/org/springframework/security/core/userdetails/User.java index c7e484671e..51d899fe2b 100644 --- a/core/src/main/java/org/springframework/security/core/userdetails/User.java +++ b/core/src/main/java/org/springframework/security/core/userdetails/User.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.function.Function; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.CredentialsContainer; @@ -285,6 +286,7 @@ public class User implements UserDetails, CredentialsContainer { private boolean accountLocked; private boolean credentialsExpired; private boolean disabled; + private Function passwordEncoder = password -> password; /** * Creates a new instance @@ -314,10 +316,25 @@ public class User implements UserDetails, CredentialsContainer { */ public UserBuilder password(String password) { Assert.notNull(password, "password cannot be null"); - this.password = password; + String encodedPassword = this.passwordEncoder.apply(password); + this.password = encodedPassword; return this; } + /** + * Encodes the current password (if non-null) and any future passwords supplied + * to {@link #password(String)}. + * + * @param encoder the encoder to use + * @return the {@link UserBuilder} for method chaining (i.e. to populate + * additional attributes for this user) + */ + public UserBuilder passwordEncoder(Function encoder) { + Assert.notNull(encoder, "encoder cannot be null"); + this.passwordEncoder = encoder; + return this.password == null ? this : password(this.password); + } + /** * Populates the roles. This method is a shortcut for calling * {@link #authorities(String...)}, but automatically prefixes each entry with diff --git a/core/src/test/java/org/springframework/security/core/userdetails/UserTests.java b/core/src/test/java/org/springframework/security/core/userdetails/UserTests.java index e9836a2cfb..e763c61f7c 100644 --- a/core/src/test/java/org/springframework/security/core/userdetails/UserTests.java +++ b/core/src/test/java/org/springframework/security/core/userdetails/UserTests.java @@ -177,4 +177,37 @@ public class UserTests { assertThat(actual.isCredentialsNonExpired()).isEqualTo(expected.isCredentialsNonExpired()); assertThat(actual.isEnabled()).isEqualTo(expected.isEnabled()); } + + @Test + public void withUserWhenDetailsPasswordEncoderThenEncodes() { + UserDetails userDetails = User.withUsername("user").password("password").roles("USER").build(); + + UserDetails withEncodedPassword = User.withUserDetails(userDetails) + .passwordEncoder(p -> p + "encoded") + .build(); + + assertThat(withEncodedPassword.getPassword()).isEqualTo("passwordencoded"); + } + + @Test + public void withUsernameWhenPasswordEncoderAndPasswordThenEncodes() { + UserDetails withEncodedPassword = User.withUsername("user") + .password("password") + .passwordEncoder(p -> p + "encoded") + .roles("USER") + .build(); + + assertThat(withEncodedPassword.getPassword()).isEqualTo("passwordencoded"); + } + + @Test + public void withUsernameWhenPasswordAndPasswordEncoderThenEncodes() { + UserDetails withEncodedPassword = User.withUsername("user") + .passwordEncoder(p -> p + "encoded") + .password("password") + .roles("USER") + .build(); + + assertThat(withEncodedPassword.getPassword()).isEqualTo("passwordencoded"); + } }