Skip to content

Commit 6f1e1d4

Browse files
abimaelrsergiojzheaux
authored andcommitted
Improve PasswordEncoder Error Messaging
Closes gh-14880
1 parent c35e107 commit 6f1e1d4

File tree

2 files changed

+29
-8
lines changed

2 files changed

+29
-8
lines changed

crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java

+11-2
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.
@@ -19,6 +19,8 @@
1919
import java.util.HashMap;
2020
import java.util.Map;
2121

22+
import org.springframework.util.StringUtils;
23+
2224
/**
2325
* A password encoder that delegates to another PasswordEncoder based upon a prefixed
2426
* identifier.
@@ -129,6 +131,10 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
129131

130132
private static final String DEFAULT_ID_SUFFIX = "}";
131133

134+
public static final String NO_PASSWORD_ENCODER_MAPPED = "There is no PasswordEncoder mapped for the id \"%s\"";
135+
136+
public static final String NO_PASSWORD_ENCODER_PREFIX = "You have entered a password with no PasswordEncoder. If that is your intent, it should be prefixed with `{noop}`.";
137+
132138
private final String idPrefix;
133139

134140
private final String idSuffix;
@@ -286,7 +292,10 @@ public String encode(CharSequence rawPassword) {
286292
@Override
287293
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
288294
String id = extractId(prefixEncodedPassword);
289-
throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\"");
295+
if (StringUtils.hasText(id)) {
296+
throw new IllegalArgumentException(String.format(NO_PASSWORD_ENCODER_MAPPED, id));
297+
}
298+
throw new IllegalArgumentException(NO_PASSWORD_ENCODER_PREFIX);
290299
}
291300

292301
}

crypto/src/test/java/org/springframework/security/crypto/password/DelegatingPasswordEncoderTests.java

+18-6
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.
@@ -43,6 +43,8 @@
4343
@ExtendWith(MockitoExtension.class)
4444
public class DelegatingPasswordEncoderTests {
4545

46+
public static final String NO_PASSWORD_ENCODER = "You have entered a password with no PasswordEncoder. If that is your intent, it should be prefixed with `{noop}`.";
47+
4648
@Mock
4749
private PasswordEncoder bcrypt;
4850

@@ -201,23 +203,23 @@ public void matchesWhenUnMappedThenIllegalArgumentException() {
201203
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentException() {
202204
assertThatIllegalArgumentException()
203205
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{bcrypt" + this.rawPassword))
204-
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
206+
.withMessage(NO_PASSWORD_ENCODER);
205207
verifyNoMoreInteractions(this.bcrypt, this.noop);
206208
}
207209

208210
@Test
209211
public void matchesWhenNoStartingPrefixStringThenFalse() {
210212
assertThatIllegalArgumentException()
211213
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "bcrypt}" + this.rawPassword))
212-
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
214+
.withMessage(NO_PASSWORD_ENCODER);
213215
verifyNoMoreInteractions(this.bcrypt, this.noop);
214216
}
215217

216218
@Test
217219
public void matchesWhenNoIdStringThenFalse() {
218220
assertThatIllegalArgumentException()
219221
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{}" + this.rawPassword))
220-
.withMessage("There is no PasswordEncoder mapped for the id \"\"");
222+
.withMessage(NO_PASSWORD_ENCODER);
221223
verifyNoMoreInteractions(this.bcrypt, this.noop);
222224
}
223225

@@ -226,7 +228,7 @@ public void matchesWhenPrefixInMiddleThenFalse() {
226228
assertThatIllegalArgumentException()
227229
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "invalid" + this.bcryptEncodedPassword))
228230
.isInstanceOf(IllegalArgumentException.class)
229-
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
231+
.withMessage(NO_PASSWORD_ENCODER);
230232
verifyNoMoreInteractions(this.bcrypt, this.noop);
231233
}
232234

@@ -236,7 +238,7 @@ public void matchesWhenIdIsNullThenFalse() {
236238
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
237239
assertThatIllegalArgumentException()
238240
.isThrownBy(() -> passwordEncoder.matches(this.rawPassword, this.rawPassword))
239-
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
241+
.withMessage(NO_PASSWORD_ENCODER);
240242
verifyNoMoreInteractions(this.bcrypt, this.noop);
241243
}
242244

@@ -289,4 +291,14 @@ public void upgradeEncodingWhenDifferentIdThenTrue() {
289291
verifyNoMoreInteractions(this.bcrypt);
290292
}
291293

294+
@Test
295+
void matchesShouldThrowIllegalArgumentExceptionWhenNoPasswordEncoderIsMappedForTheId() {
296+
assertThatIllegalArgumentException()
297+
.isThrownBy(() -> this.passwordEncoder.matches("rawPassword", "prefixEncodedPassword"))
298+
.isInstanceOf(IllegalArgumentException.class)
299+
.withMessage(NO_PASSWORD_ENCODER);
300+
verifyNoMoreInteractions(this.bcrypt, this.noop);
301+
302+
}
303+
292304
}

0 commit comments

Comments
 (0)