Skip to content

Commit bbaaf4f

Browse files
committed
Split ChangePasswordAdvisor Methods
There are now two interfaces, ChangeExistingPasswordAdvisor and ChangeUpdatingPasswordAdvisor. I have a sense that more information may be wanted down the road for ChangeUpdatingPasswordAdvisor; so this would allow them to evolve independently.
1 parent ce01188 commit bbaaf4f

File tree

11 files changed

+92
-62
lines changed

11 files changed

+92
-62
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/PasswordManagementConfigurer.java

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers;
1818

19-
import java.util.ArrayList;
20-
import java.util.List;
21-
2219
import org.springframework.context.ApplicationContext;
2320
import org.springframework.context.ApplicationContextAware;
21+
import org.springframework.security.authentication.password.ChangeExistingPasswordAdvisor;
2422
import org.springframework.security.authentication.password.ChangePasswordAdvice;
25-
import org.springframework.security.authentication.password.ChangePasswordAdvisor;
2623
import org.springframework.security.authentication.password.ChangePasswordServiceAdvisor;
24+
import org.springframework.security.authentication.password.ChangeUpdatingPasswordAdvisor;
2725
import org.springframework.security.authentication.password.DelegatingChangePasswordAdvisor;
2826
import org.springframework.security.authentication.password.UserDetailsPasswordManager;
2927
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -67,7 +65,9 @@ public final class PasswordManagementConfigurer<B extends HttpSecurityBuilder<B>
6765

6866
private ChangePasswordAdviceRepository changePasswordAdviceRepository;
6967

70-
private ChangePasswordAdvisor changePasswordAdvisor;
68+
private ChangeExistingPasswordAdvisor changeExistingPasswordAdvisor;
69+
70+
private ChangeUpdatingPasswordAdvisor changeUpdatingPasswordAdvisor;
7171

7272
private ChangePasswordAdviceHandler changePasswordAdviceHandler;
7373

@@ -97,8 +97,15 @@ public PasswordManagementConfigurer<B> changePasswordAdviceRepository(
9797
return this;
9898
}
9999

100-
public PasswordManagementConfigurer<B> changePasswordAdvisor(ChangePasswordAdvisor changePasswordAdvisor) {
101-
this.changePasswordAdvisor = changePasswordAdvisor;
100+
public PasswordManagementConfigurer<B> changeExistingPasswordAdvisor(
101+
ChangeExistingPasswordAdvisor changePasswordAdvisor) {
102+
this.changeExistingPasswordAdvisor = changePasswordAdvisor;
103+
return this;
104+
}
105+
106+
public PasswordManagementConfigurer<B> changeUpdatingPasswordAdvisor(
107+
ChangeUpdatingPasswordAdvisor changePasswordAdvisor) {
108+
this.changeUpdatingPasswordAdvisor = changePasswordAdvisor;
102109
return this;
103110
}
104111

@@ -129,25 +136,26 @@ public void init(B http) throws Exception {
129136
: this.context.getBeanProvider(ChangePasswordAdviceRepository.class)
130137
.getIfUnique(HttpSessionChangePasswordAdviceRepository::new);
131138

132-
ChangePasswordAdvisor changePasswordAdvisor = (this.changePasswordAdvisor != null) ? this.changePasswordAdvisor
133-
: this.context.getBeanProvider(ChangePasswordAdvisor.class).getIfUnique(() -> {
134-
List<ChangePasswordAdvisor> advisors = new ArrayList<>();
135-
advisors.add(new ChangeCompromisedPasswordAdvisor());
136-
advisors.add(new ChangePasswordServiceAdvisor(passwordManager));
137-
return new DelegatingChangePasswordAdvisor(advisors);
138-
});
139+
ChangeExistingPasswordAdvisor changeExistingPasswordAdvisor = (this.changeExistingPasswordAdvisor != null)
140+
? this.changeExistingPasswordAdvisor
141+
: this.context.getBeanProvider(ChangeExistingPasswordAdvisor.class)
142+
.getIfUnique(() -> DelegatingChangePasswordAdvisor.forExisting(
143+
new ChangePasswordServiceAdvisor(passwordManager), new ChangeCompromisedPasswordAdvisor()));
144+
ChangeUpdatingPasswordAdvisor changeUpdatingPasswordAdvisor = (this.changeExistingPasswordAdvisor != null)
145+
? this.changeUpdatingPasswordAdvisor : this.context.getBeanProvider(ChangeUpdatingPasswordAdvisor.class)
146+
.getIfUnique(ChangeCompromisedPasswordAdvisor::new);
139147

140148
http.setSharedObject(ChangePasswordAdviceRepository.class, changePasswordAdviceRepository);
141149
http.setSharedObject(UserDetailsPasswordManager.class, passwordManager);
142-
http.setSharedObject(ChangePasswordAdvisor.class, changePasswordAdvisor);
150+
http.setSharedObject(ChangeUpdatingPasswordAdvisor.class, changeUpdatingPasswordAdvisor);
143151

144152
FormLoginConfigurer form = http.getConfigurer(FormLoginConfigurer.class);
145153
String passwordParameter = (form != null) ? form.getPasswordParameter() : "password";
146154
http.getConfigurer(SessionManagementConfigurer.class)
147155
.addSessionAuthenticationStrategy((authentication, request, response) -> {
148156
UserDetails user = (UserDetails) authentication.getPrincipal();
149157
String password = request.getParameter(passwordParameter);
150-
ChangePasswordAdvice advice = changePasswordAdvisor.advise(user, password);
158+
ChangePasswordAdvice advice = changeExistingPasswordAdvisor.advise(user, password);
151159
changePasswordAdviceRepository.savePasswordAdvice(request, response, advice);
152160
});
153161
}
@@ -181,7 +189,7 @@ public void configure(B http) throws Exception {
181189
http.getSharedObject(UserDetailsPasswordManager.class));
182190
processing
183191
.setRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher(this.changePasswordProcessingUrl));
184-
processing.setChangePasswordAdvisor(http.getSharedObject(ChangePasswordAdvisor.class));
192+
processing.setChangePasswordAdvisor(http.getSharedObject(ChangeUpdatingPasswordAdvisor.class));
185193
processing.setChangePasswordAdviceRepository(http.getSharedObject(ChangePasswordAdviceRepository.class));
186194
processing.setPasswordEncoder(passwordEncoder);
187195
processing.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());

core/src/main/java/org/springframework/security/authentication/password/ChangePasswordAdvisor.java renamed to core/src/main/java/org/springframework/security/authentication/password/ChangeExistingPasswordAdvisor.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@
1818

1919
import org.springframework.security.core.userdetails.UserDetails;
2020

21-
public interface ChangePasswordAdvisor {
21+
public interface ChangeExistingPasswordAdvisor {
2222

2323
ChangePasswordAdvice advise(UserDetails user, String password);
2424

25-
default ChangePasswordAdvice adviseForUpdate(UserDetails user, String password) {
26-
return advise(user, password);
27-
}
28-
2925
}

core/src/main/java/org/springframework/security/authentication/password/ChangeLengthPasswordAdvisor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import org.springframework.security.authentication.password.ChangePasswordAdvice.Action;
2020
import org.springframework.security.core.userdetails.UserDetails;
2121

22-
public class ChangeLengthPasswordAdvisor implements ChangePasswordAdvisor {
22+
public class ChangeLengthPasswordAdvisor implements ChangeExistingPasswordAdvisor, ChangeUpdatingPasswordAdvisor {
2323

2424
private final int minLength;
2525

core/src/main/java/org/springframework/security/authentication/password/ChangePasswordAdvice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public interface ChangePasswordAdvice {
2525
Collection<ChangePasswordReason> getReasons();
2626

2727
static ChangePasswordAdvice keep() {
28-
return new SimpleChangePasswordAdvice(Action.KEEP);
28+
return SimpleChangePasswordAdvice.KEEP;
2929
}
3030

3131
enum Action {

core/src/main/java/org/springframework/security/authentication/password/ChangePasswordServiceAdvisor.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import org.springframework.security.core.userdetails.UserDetails;
2020

21-
public final class ChangePasswordServiceAdvisor implements ChangePasswordAdvisor {
21+
public final class ChangePasswordServiceAdvisor implements ChangeExistingPasswordAdvisor {
2222

2323
private final UserDetailsPasswordManager passwordManager;
2424

@@ -31,9 +31,4 @@ public ChangePasswordAdvice advise(UserDetails user, String password) {
3131
return this.passwordManager.loadPasswordAdvice(user);
3232
}
3333

34-
@Override
35-
public ChangePasswordAdvice adviseForUpdate(UserDetails user, String password) {
36-
return null;
37-
}
38-
3934
}

core/src/main/java/org/springframework/security/authentication/password/ChangeRepeatedPasswordAdvisor.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.springframework.security.crypto.password.PasswordEncoder;
2424
import org.springframework.util.Assert;
2525

26-
public final class ChangeRepeatedPasswordAdvisor implements ChangePasswordAdvisor {
26+
public final class ChangeRepeatedPasswordAdvisor implements ChangeUpdatingPasswordAdvisor {
2727

2828
private final UserDetailsService userDetailsService;
2929

@@ -37,11 +37,6 @@ public ChangeRepeatedPasswordAdvisor(UserDetailsService userDetailsService) {
3737

3838
@Override
3939
public ChangePasswordAdvice advise(UserDetails user, String password) {
40-
return null;
41-
}
42-
43-
@Override
44-
public ChangePasswordAdvice adviseForUpdate(UserDetails user, String password) {
4540
UserDetails withPassword = this.userDetailsService.loadUserByUsername(user.getUsername());
4641
if (this.passwordEncoder.matches(password, withPassword.getPassword())) {
4742
return new SimpleChangePasswordAdvice(this.action, ChangePasswordReason.REPEATED);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.authentication.password;
18+
19+
import org.springframework.security.core.userdetails.UserDetails;
20+
21+
public interface ChangeUpdatingPasswordAdvisor {
22+
23+
ChangePasswordAdvice advise(UserDetails user, String password);
24+
25+
}

core/src/main/java/org/springframework/security/authentication/password/DelegatingChangePasswordAdvisor.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,39 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.Collection;
21+
import java.util.Collections;
2122
import java.util.List;
2223
import java.util.Objects;
24+
import java.util.function.BiFunction;
25+
import java.util.stream.Stream;
2326

2427
import org.springframework.security.core.userdetails.UserDetails;
2528

26-
public final class DelegatingChangePasswordAdvisor implements ChangePasswordAdvisor {
29+
public final class DelegatingChangePasswordAdvisor
30+
implements ChangeExistingPasswordAdvisor, ChangeUpdatingPasswordAdvisor {
2731

28-
private final List<ChangePasswordAdvisor> advisors;
32+
private final List<BiFunction<UserDetails, String, ChangePasswordAdvice>> advisors;
2933

30-
public DelegatingChangePasswordAdvisor(List<ChangePasswordAdvisor> advisors) {
31-
this.advisors = advisors;
34+
private DelegatingChangePasswordAdvisor(List<BiFunction<UserDetails, String, ChangePasswordAdvice>> advisors) {
35+
this.advisors = Collections.unmodifiableList(advisors);
3236
}
3337

34-
@Override
35-
public ChangePasswordAdvice advise(UserDetails user, String password) {
36-
Collection<ChangePasswordAdvice> advice = this.advisors.stream()
37-
.map((advisor) -> advisor.advise(user, password))
38-
.filter(Objects::nonNull)
39-
.toList();
40-
return new CompositeChangePasswordAdvice(advice);
38+
public static ChangeExistingPasswordAdvisor forExisting(ChangeExistingPasswordAdvisor... advisors) {
39+
return new DelegatingChangePasswordAdvisor(Stream.of(advisors)
40+
.map((advisor) -> (BiFunction<UserDetails, String, ChangePasswordAdvice>) advisor::advise)
41+
.toList());
42+
}
43+
44+
public static ChangeUpdatingPasswordAdvisor forUpdating(ChangeUpdatingPasswordAdvisor... advisors) {
45+
return new DelegatingChangePasswordAdvisor(Stream.of(advisors)
46+
.map((advisor) -> (BiFunction<UserDetails, String, ChangePasswordAdvice>) advisor::advise)
47+
.toList());
4148
}
4249

4350
@Override
44-
public ChangePasswordAdvice adviseForUpdate(UserDetails user, String password) {
51+
public ChangePasswordAdvice advise(UserDetails user, String password) {
4552
Collection<ChangePasswordAdvice> advice = this.advisors.stream()
46-
.map((advisor) -> advisor.adviseForUpdate(user, password))
53+
.map((advisor) -> advisor.apply(user, password))
4754
.filter(Objects::nonNull)
4855
.toList();
4956
return new CompositeChangePasswordAdvice(advice);

core/src/main/java/org/springframework/security/authentication/password/SimpleChangePasswordAdvice.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 the original author or authors.
2+
* Copyright 2002-2025 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,23 +16,25 @@
1616

1717
package org.springframework.security.authentication.password;
1818

19-
import java.util.ArrayList;
2019
import java.util.Collection;
2120
import java.util.List;
2221

23-
public final class SimpleChangePasswordAdvice implements ChangePasswordAdvice {
22+
public class SimpleChangePasswordAdvice implements ChangePasswordAdvice {
23+
24+
static final SimpleChangePasswordAdvice KEEP = new SimpleChangePasswordAdvice(Action.KEEP);
2425

2526
private final Action action;
2627

2728
private final Collection<ChangePasswordReason> reasons;
2829

29-
public SimpleChangePasswordAdvice(Action action, ChangePasswordReason... reasons) {
30-
this(action, List.of(reasons));
30+
public SimpleChangePasswordAdvice(Action action, Collection<ChangePasswordReason> reasons) {
31+
this.action = action;
32+
this.reasons = reasons;
3133
}
3234

33-
public SimpleChangePasswordAdvice(Action action, Collection<ChangePasswordReason> reasons) {
35+
public SimpleChangePasswordAdvice(Action action, ChangePasswordReason... reasons) {
3436
this.action = action;
35-
this.reasons = new ArrayList<>(reasons);
37+
this.reasons = List.of(reasons);
3638
}
3739

3840
@Override

web/src/main/java/org/springframework/security/web/authentication/password/ChangeCompromisedPasswordAdvisor.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@
1818

1919
import java.util.Collection;
2020

21+
import org.springframework.security.authentication.password.ChangeExistingPasswordAdvisor;
2122
import org.springframework.security.authentication.password.ChangePasswordAdvice;
2223
import org.springframework.security.authentication.password.ChangePasswordAdvice.Action;
23-
import org.springframework.security.authentication.password.ChangePasswordAdvisor;
2424
import org.springframework.security.authentication.password.ChangePasswordReason;
25+
import org.springframework.security.authentication.password.ChangeUpdatingPasswordAdvisor;
2526
import org.springframework.security.authentication.password.CompromisedPasswordChecker;
2627
import org.springframework.security.authentication.password.CompromisedPasswordDecision;
2728
import org.springframework.security.authentication.password.SimpleChangePasswordAdvice;
2829
import org.springframework.security.core.userdetails.UserDetails;
2930

30-
public final class ChangeCompromisedPasswordAdvisor implements ChangePasswordAdvisor {
31+
public final class ChangeCompromisedPasswordAdvisor
32+
implements ChangeExistingPasswordAdvisor, ChangeUpdatingPasswordAdvisor {
3133

3234
private final CompromisedPasswordChecker pwned = new HaveIBeenPwnedRestApiPasswordChecker();
3335

0 commit comments

Comments
 (0)