Skip to content

Commit 0ee71ad

Browse files
TestServerOneTimeTokenGenerationSuccessHandler.lastToken to non-static variable
Signed-off-by: Max Batischev <[email protected]>
1 parent f4484d8 commit 0ee71ad

File tree

2 files changed

+150
-68
lines changed

2 files changed

+150
-68
lines changed

config/src/test/java/org/springframework/security/config/web/server/OneTimeTokenLoginSpecTests.java

+105-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 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.
@@ -21,6 +21,8 @@
2121

2222
import org.junit.jupiter.api.Test;
2323
import org.junit.jupiter.api.extension.ExtendWith;
24+
import org.mockito.ArgumentMatchers;
25+
import org.mockito.Mockito;
2426
import reactor.core.publisher.Mono;
2527

2628
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,6 +42,8 @@
4042
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers;
4143
import org.springframework.security.web.server.SecurityWebFilterChain;
4244
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
45+
import org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver;
46+
import org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver;
4347
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
4448
import org.springframework.security.web.server.authentication.ott.ServerRedirectOneTimeTokenGenerationSuccessHandler;
4549
import org.springframework.test.web.reactive.server.WebTestClient;
@@ -49,6 +53,8 @@
4953

5054
import static org.assertj.core.api.Assertions.assertThat;
5155
import static org.assertj.core.api.Assertions.assertThatException;
56+
import static org.mockito.Mockito.times;
57+
import static org.mockito.Mockito.verify;
5258

5359
/**
5460
* Tests for {@link ServerHttpSecurity.OneTimeTokenLoginSpec}
@@ -107,7 +113,7 @@ void oneTimeTokenWhenCorrectTokenThenCanAuthenticate() {
107113
.expectHeader().valueEquals("Location", "/login/ott");
108114
// @formatter:on
109115

110-
String token = TestServerOneTimeTokenGenerationSuccessHandler.lastToken.getTokenValue();
116+
String token = getLastToken().getTokenValue();
111117

112118
// @formatter:off
113119
this.client.mutateWith(SecurityMockServerConfigurers.csrf())
@@ -143,7 +149,7 @@ void oneTimeTokenWhenDifferentAuthenticationUrlsThenCanAuthenticate() {
143149
.expectHeader().valueEquals("Location", "/redirected");
144150
// @formatter:on
145151

146-
String token = TestServerOneTimeTokenGenerationSuccessHandler.lastToken.getTokenValue();
152+
String token = getLastToken().getTokenValue();
147153

148154
// @formatter:off
149155
this.client.mutateWith(SecurityMockServerConfigurers.csrf())
@@ -179,7 +185,7 @@ void oneTimeTokenWhenCorrectTokenUsedTwiceThenSecondTimeFails() {
179185
.expectHeader().valueEquals("Location", "/login/ott");
180186
// @formatter:on
181187

182-
String token = TestServerOneTimeTokenGenerationSuccessHandler.lastToken.getTokenValue();
188+
String token = getLastToken().getTokenValue();
183189

184190
// @formatter:off
185191
this.client.mutateWith(SecurityMockServerConfigurers.csrf())
@@ -268,39 +274,76 @@ void oneTimeTokenWhenFormLoginConfiguredThenRendersRequestTokenForm() {
268274
assertThat(response.contains(GENERATE_OTT_PART)).isTrue();
269275
}
270276

277+
private OneTimeToken getLastToken() {
278+
OneTimeToken lastToken = this.spring.getContext()
279+
.getBean(TestServerOneTimeTokenGenerationSuccessHandler.class).lastToken;
280+
return lastToken;
281+
}
282+
271283
@Test
272284
void oneTimeTokenWhenNoOneTimeTokenGenerationSuccessHandlerThenException() {
273285
assertThatException()
274-
.isThrownBy(() -> this.spring.register(OneTimeTokenNotGeneratedOttHandlerConfig.class).autowire())
275-
.havingRootCause()
276-
.isInstanceOf(IllegalStateException.class)
277-
.withMessage("""
286+
.isThrownBy(() -> this.spring.register(OneTimeTokenNotGeneratedOttHandlerConfig.class).autowire())
287+
.havingRootCause()
288+
.isInstanceOf(IllegalStateException.class)
289+
.withMessage("""
278290
A ServerOneTimeTokenGenerationSuccessHandler is required to enable oneTimeTokenLogin().
279291
Please provide it as a bean or pass it to the oneTimeTokenLogin() DSL.
280292
""");
281293
}
282294

295+
@Test
296+
void oneTimeTokenWhenCustomRequestResolverSetThenCustomResolverUse() {
297+
this.spring.register(OneTimeTokenConfigWithCustomRequestResolver.class).autowire();
298+
299+
// @formatter:off
300+
this.client.mutateWith(SecurityMockServerConfigurers.csrf())
301+
.post()
302+
.uri((uriBuilder) -> uriBuilder
303+
.path("/ott/generate")
304+
.build()
305+
)
306+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
307+
.body(BodyInserters.fromFormData("username", "user"))
308+
.exchange()
309+
.expectStatus()
310+
.is3xxRedirection()
311+
.expectHeader().valueEquals("Location", "/login/ott");
312+
// @formatter:on
313+
314+
ServerGenerateOneTimeTokenRequestResolver resolver = this.spring.getContext()
315+
.getBean(ServerGenerateOneTimeTokenRequestResolver.class);
316+
317+
verify(resolver, times(1)).resolve(ArgumentMatchers.any(ServerWebExchange.class));
318+
}
319+
283320
@Configuration(proxyBeanMethods = false)
284321
@EnableWebFlux
285322
@EnableWebFluxSecurity
286323
@Import(UserDetailsServiceConfig.class)
287324
static class OneTimeTokenDefaultConfig {
288325

289326
@Bean
290-
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
327+
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,
328+
ServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {
291329
// @formatter:off
292330
http
293331
.authorizeExchange((authorize) -> authorize
294332
.anyExchange()
295333
.authenticated()
296334
)
297335
.oneTimeTokenLogin((ott) -> ott
298-
.tokenGenerationSuccessHandler(new TestServerOneTimeTokenGenerationSuccessHandler())
336+
.tokenGenerationSuccessHandler(ottSuccessHandler)
299337
);
300338
// @formatter:on
301339
return http.build();
302340
}
303341

342+
@Bean
343+
TestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {
344+
return new TestServerOneTimeTokenGenerationSuccessHandler();
345+
}
346+
304347
}
305348

306349
@Configuration(proxyBeanMethods = false)
@@ -310,7 +353,8 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
310353
static class OneTimeTokenDifferentUrlsConfig {
311354

312355
@Bean
313-
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
356+
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http,
357+
ServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {
314358
// @formatter:off
315359
http
316360
.authorizeExchange((authorize) -> authorize
@@ -319,14 +363,19 @@ SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
319363
)
320364
.oneTimeTokenLogin((ott) -> ott
321365
.tokenGeneratingUrl("/generateurl")
322-
.tokenGenerationSuccessHandler(new TestServerOneTimeTokenGenerationSuccessHandler("/redirected"))
366+
.tokenGenerationSuccessHandler(ottSuccessHandler)
323367
.loginProcessingUrl("/loginprocessingurl")
324368
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/authenticated"))
325369
);
326370
// @formatter:on
327371
return http.build();
328372
}
329373

374+
@Bean
375+
TestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {
376+
return new TestServerOneTimeTokenGenerationSuccessHandler("/redirected");
377+
}
378+
330379
}
331380

332381
@Configuration(proxyBeanMethods = false)
@@ -336,7 +385,8 @@ SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
336385
static class OneTimeTokenFormLoginConfig {
337386

338387
@Bean
339-
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
388+
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http,
389+
ServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {
340390
// @formatter:off
341391
http
342392
.authorizeExchange((authorize) -> authorize
@@ -345,12 +395,17 @@ SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
345395
)
346396
.formLogin(Customizer.withDefaults())
347397
.oneTimeTokenLogin((ott) -> ott
348-
.tokenGenerationSuccessHandler(new TestServerOneTimeTokenGenerationSuccessHandler())
398+
.tokenGenerationSuccessHandler(ottSuccessHandler)
349399
);
350400
// @formatter:on
351401
return http.build();
352402
}
353403

404+
@Bean
405+
TestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {
406+
return new TestServerOneTimeTokenGenerationSuccessHandler();
407+
}
408+
354409
}
355410

356411
@Configuration(proxyBeanMethods = false)
@@ -385,10 +440,44 @@ ReactiveUserDetailsService userDetailsService() {
385440

386441
}
387442

443+
@Configuration(proxyBeanMethods = false)
444+
@EnableWebFlux
445+
@EnableWebFluxSecurity
446+
@Import(UserDetailsServiceConfig.class)
447+
static class OneTimeTokenConfigWithCustomRequestResolver {
448+
449+
@Bean
450+
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,
451+
ServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {
452+
// @formatter:off
453+
http
454+
.authorizeExchange((authorize) -> authorize
455+
.anyExchange()
456+
.authenticated()
457+
)
458+
.oneTimeTokenLogin((ott) -> ott
459+
.tokenGenerationSuccessHandler(ottSuccessHandler)
460+
);
461+
// @formatter:on
462+
return http.build();
463+
}
464+
465+
@Bean
466+
ServerGenerateOneTimeTokenRequestResolver resolver() {
467+
return Mockito.spy(new DefaultServerGenerateOneTimeTokenRequestResolver());
468+
}
469+
470+
@Bean
471+
TestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {
472+
return new TestServerOneTimeTokenGenerationSuccessHandler();
473+
}
474+
475+
}
476+
388477
private static class TestServerOneTimeTokenGenerationSuccessHandler
389478
implements ServerOneTimeTokenGenerationSuccessHandler {
390479

391-
private static OneTimeToken lastToken;
480+
private OneTimeToken lastToken;
392481

393482
private final ServerOneTimeTokenGenerationSuccessHandler delegate;
394483

@@ -402,7 +491,7 @@ private static class TestServerOneTimeTokenGenerationSuccessHandler
402491

403492
@Override
404493
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
405-
lastToken = oneTimeToken;
494+
this.lastToken = oneTimeToken;
406495
return this.delegate.handle(exchange, oneTimeToken);
407496
}
408497

0 commit comments

Comments
 (0)