29
29
import org .springframework .context .annotation .Configuration ;
30
30
import org .springframework .http .ResponseEntity ;
31
31
import org .springframework .mock .web .MockHttpSession ;
32
- import org .springframework .security .authentication .password .ChangePasswordAdvice ;
33
- import org .springframework .security .authentication .password .ChangePasswordAdvisor ;
34
32
import org .springframework .security .authentication .password .PasswordAction ;
35
- import org .springframework .security .authentication .password .UserDetailsPasswordManager ;
33
+ import org .springframework .security .authentication .password .PasswordAdvice ;
34
+ import org .springframework .security .authentication .password .UpdatePasswordAdvisor ;
36
35
import org .springframework .security .config .Customizer ;
37
36
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
38
37
import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
39
38
import org .springframework .security .config .test .SpringTestContext ;
40
39
import org .springframework .security .config .test .SpringTestContextExtension ;
41
40
import org .springframework .security .core .annotation .AuthenticationPrincipal ;
42
41
import org .springframework .security .core .userdetails .PasswordEncodedUser ;
42
+ import org .springframework .security .core .userdetails .User ;
43
43
import org .springframework .security .core .userdetails .UserDetails ;
44
44
import org .springframework .security .core .userdetails .UserDetailsService ;
45
45
import org .springframework .security .crypto .factory .PasswordEncoderFactories ;
46
46
import org .springframework .security .crypto .password .PasswordEncoder ;
47
47
import org .springframework .security .provisioning .InMemoryUserDetailsManager ;
48
+ import org .springframework .security .provisioning .UserDetailsManager ;
48
49
import org .springframework .security .web .SecurityFilterChain ;
49
- import org .springframework .security .web .authentication .password .ChangeCompromisedPasswordAdvisor ;
50
- import org .springframework .security .web .authentication .password .ChangePasswordAdviceRepository ;
51
- import org .springframework .security .web .authentication .password .HttpSessionChangePasswordAdviceRepository ;
50
+ import org .springframework .security .web .authentication .password .CompromisedPasswordAdvisor ;
51
+ import org .springframework .security .web .authentication .password .HttpSessionPasswordAdviceRepository ;
52
+ import org .springframework .security .web .authentication .password .PasswordAdviceRepository ;
52
53
import org .springframework .test .web .servlet .MockMvc ;
53
54
import org .springframework .test .web .servlet .MvcResult ;
54
55
import org .springframework .web .bind .annotation .GetMapping ;
@@ -155,7 +156,7 @@ void whenAdminSetsExpiredAdviceThenUserLoginRedirectsToResetPassword() throws Ex
155
156
}
156
157
157
158
@ Test
158
- void whenCompromisedThenUserLoginAllowed () throws Exception {
159
+ void whenShouldChangeThenUserLoginAllowed () throws Exception {
159
160
this .spring .register (PasswordManagementConfig .class , AdminController .class , HomeController .class ).autowire ();
160
161
MvcResult result = this .mvc
161
162
.perform (post ("/login" ).with (csrf ()).param ("username" , "user" ).param ("password" , "password" ))
@@ -165,7 +166,7 @@ void whenCompromisedThenUserLoginAllowed() throws Exception {
165
166
MockHttpSession session = (MockHttpSession ) result .getRequest ().getSession ();
166
167
this .mvc .perform (get ("/" ).session (session ))
167
168
.andExpect (status ().isOk ())
168
- .andExpect (content ().string (containsString ("compromised " )));
169
+ .andExpect (content ().string (containsString ("SHOULD_CHANGE " )));
169
170
}
170
171
171
172
@ Configuration
@@ -206,27 +207,26 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
206
207
static class PasswordManagementConfig {
207
208
208
209
@ Bean
209
- SecurityFilterChain securityFilterChain (HttpSecurity http ) throws Exception {
210
+ SecurityFilterChain securityFilterChain (HttpSecurity http , UserDetailsService users ) throws Exception {
210
211
// @formatter:off
211
212
http
212
- .authorizeHttpRequests ((authz ) -> authz
213
- .requestMatchers ("/admin/**" ).hasRole ("ADMIN" )
214
- .anyRequest ().authenticated ()
215
- )
216
- .formLogin (Customizer .withDefaults ())
217
- .passwordManagement (Customizer .withDefaults ());
213
+ .authorizeHttpRequests ((authz ) -> authz
214
+ .requestMatchers ("/admin/**" ).hasRole ("ADMIN" )
215
+ .anyRequest ().authenticated ()
216
+ )
217
+ .formLogin (Customizer .withDefaults ())
218
+ .passwordManagement (Customizer .withDefaults ());
218
219
// @formatter:on
219
220
return http .build ();
220
221
}
221
222
222
223
@ Bean
223
- UserDetailsService users () {
224
- String adminPassword = UUID .randomUUID ().toString ();
225
- UserDetails compromised = PasswordEncodedUser .user ();
226
- UserDetails admin = PasswordEncodedUser .withUserDetails (PasswordEncodedUser .admin ())
227
- .password (adminPassword )
224
+ InMemoryUserDetailsManager users () {
225
+ UserDetails shouldChange = User .withUserDetails (PasswordEncodedUser .user ())
226
+ .passwordAction (PasswordAction .SHOULD_CHANGE )
228
227
.build ();
229
- return new InMemoryUserDetailsManager (compromised , admin );
228
+ UserDetails admin = PasswordEncodedUser .admin ();
229
+ return new InMemoryUserDetailsManager (shouldChange , admin );
230
230
}
231
231
232
232
}
@@ -235,13 +235,10 @@ UserDetailsService users() {
235
235
@ RestController
236
236
static class AdminController {
237
237
238
- private final UserDetailsService users ;
239
-
240
- private final UserDetailsPasswordManager passwords ;
238
+ private final UserDetailsManager users ;
241
239
242
- AdminController (UserDetailsService users , UserDetailsPasswordManager passwords ) {
240
+ AdminController (InMemoryUserDetailsManager users ) {
243
241
this .users = users ;
244
- this .passwords = passwords ;
245
242
}
246
243
247
244
@ GetMapping ("/advice/{username}" )
@@ -259,7 +256,8 @@ ResponseEntity<PasswordAction> expirePassword(@PathVariable("username") String u
259
256
if (user == null ) {
260
257
return ResponseEntity .notFound ().build ();
261
258
}
262
- this .passwords .savePasswordAction (user , PasswordAction .MUST_CHANGE );
259
+ UserDetails mustChange = User .withUserDetails (user ).passwordAction (PasswordAction .MUST_CHANGE ).build ();
260
+ this .users .updateUser (mustChange );
263
261
URI uri = URI .create ("/admin/passwords/advice/" + username );
264
262
return ResponseEntity .created (uri ).body (PasswordAction .MUST_CHANGE );
265
263
}
@@ -269,32 +267,32 @@ ResponseEntity<PasswordAction> expirePassword(@PathVariable("username") String u
269
267
@ RestController
270
268
static class HomeController {
271
269
272
- private final UserDetailsPasswordManager passwords ;
270
+ private final InMemoryUserDetailsManager passwords ;
273
271
274
- private final ChangePasswordAdvisor changePasswordAdvisor = new ChangeCompromisedPasswordAdvisor ();
272
+ private final UpdatePasswordAdvisor passwordAdvisor = new CompromisedPasswordAdvisor ();
275
273
276
- private final ChangePasswordAdviceRepository changePasswordAdviceRepository = new HttpSessionChangePasswordAdviceRepository ();
274
+ private final PasswordAdviceRepository passwordAdviceRepository = new HttpSessionPasswordAdviceRepository ();
277
275
278
276
private final PasswordEncoder encoder = PasswordEncoderFactories .createDelegatingPasswordEncoder ();
279
277
280
- HomeController (UserDetailsPasswordManager passwords ) {
278
+ HomeController (InMemoryUserDetailsManager passwords ) {
281
279
this .passwords = passwords ;
282
280
}
283
281
284
282
@ GetMapping
285
- ChangePasswordAdvice index (ChangePasswordAdvice advice ) {
283
+ PasswordAdvice index (PasswordAdvice advice ) {
286
284
return advice ;
287
285
}
288
286
289
287
@ PostMapping ("/change-password" )
290
288
ResponseEntity <?> changePassword (@ AuthenticationPrincipal UserDetails user ,
291
289
@ RequestParam ("password" ) String password , HttpServletRequest request , HttpServletResponse response ) {
292
- ChangePasswordAdvice advice = this .changePasswordAdvisor .advise (user , password );
290
+ PasswordAdvice advice = this .passwordAdvisor .advise (user , null , password );
293
291
if (advice .getAction () != PasswordAction .ABSTAIN ) {
294
292
return ResponseEntity .badRequest ().body (advice );
295
293
}
296
- this .passwords .updatePassword ( user , this .encoder .encode (password ));
297
- this .changePasswordAdviceRepository .removePasswordAdvice (request , response );
294
+ this .passwords .changePassword ( null , this .encoder .encode (password ));
295
+ this .passwordAdviceRepository .removePasswordAdvice (request , response );
298
296
return ResponseEntity .ok ().build ();
299
297
}
300
298
0 commit comments