Browse Source

Add passwordEncoder to UserBuilder

Fixes gh-4677
Rob Winch 8 years ago
parent
commit
a0fb324e1d

+ 18 - 1
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.Set;
 import java.util.SortedSet;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.TreeSet;
+import java.util.function.Function;
 
 
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.CredentialsContainer;
 import org.springframework.security.core.CredentialsContainer;
@@ -285,6 +286,7 @@ public class User implements UserDetails, CredentialsContainer {
 		private boolean accountLocked;
 		private boolean accountLocked;
 		private boolean credentialsExpired;
 		private boolean credentialsExpired;
 		private boolean disabled;
 		private boolean disabled;
+		private Function<String,String> passwordEncoder = password -> password;
 
 
 		/**
 		/**
 		 * Creates a new instance
 		 * Creates a new instance
@@ -314,10 +316,25 @@ public class User implements UserDetails, CredentialsContainer {
 		 */
 		 */
 		public UserBuilder password(String password) {
 		public UserBuilder password(String password) {
 			Assert.notNull(password, "password cannot be null");
 			Assert.notNull(password, "password cannot be null");
-			this.password = password;
+			String encodedPassword = this.passwordEncoder.apply(password);
+			this.password = encodedPassword;
 			return this;
 			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<String,String> 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
 		 * Populates the roles. This method is a shortcut for calling
 		 * {@link #authorities(String...)}, but automatically prefixes each entry with
 		 * {@link #authorities(String...)}, but automatically prefixes each entry with

+ 33 - 0
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.isCredentialsNonExpired()).isEqualTo(expected.isCredentialsNonExpired());
 		assertThat(actual.isEnabled()).isEqualTo(expected.isEnabled());
 		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");
+	}
 }
 }