Skip to content

Commit 9e2f34b

Browse files
ruabtmhruabtmh
ruabtmh
authored and
ruabtmh
committed
Add ContinueOnError Support For Failed Authentications
Closes gh-14521
1 parent e771267 commit 9e2f34b

File tree

2 files changed

+58
-8
lines changed

2 files changed

+58
-8
lines changed

core/src/main/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManager.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 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.
@@ -18,7 +18,10 @@
1818

1919
import java.util.Arrays;
2020
import java.util.List;
21+
import java.util.function.Function;
2122

23+
import org.apache.commons.logging.Log;
24+
import org.apache.commons.logging.LogFactory;
2225
import reactor.core.publisher.Flux;
2326
import reactor.core.publisher.Mono;
2427

@@ -28,7 +31,7 @@
2831
/**
2932
* A {@link ReactiveAuthenticationManager} that delegates to other
3033
* {@link ReactiveAuthenticationManager} instances using the result from the first non
31-
* empty result.
34+
* empty result. Errors from delegates will be ignored if continueOnError is true.
3235
*
3336
* @author Rob Winch
3437
* @since 5.1
@@ -37,6 +40,10 @@ public class DelegatingReactiveAuthenticationManager implements ReactiveAuthenti
3740

3841
private final List<ReactiveAuthenticationManager> delegates;
3942

43+
private boolean continueOnError = false;
44+
45+
private final Log logger = LogFactory.getLog(getClass());
46+
4047
public DelegatingReactiveAuthenticationManager(ReactiveAuthenticationManager... entryPoints) {
4148
this(Arrays.asList(entryPoints));
4249
}
@@ -48,11 +55,15 @@ public DelegatingReactiveAuthenticationManager(List<ReactiveAuthenticationManage
4855

4956
@Override
5057
public Mono<Authentication> authenticate(Authentication authentication) {
51-
// @formatter:off
52-
return Flux.fromIterable(this.delegates)
53-
.concatMap((m) -> m.authenticate(authentication))
54-
.next();
55-
// @formatter:on
58+
Flux<ReactiveAuthenticationManager> result = Flux.fromIterable(this.delegates);
59+
Function<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)
60+
.doOnError(logger::debug);
61+
62+
return ((this.continueOnError) ? result.concatMapDelayError(logging) : result.concatMap(logging)).next();
63+
}
64+
65+
public void setContinueOnError(boolean continueOnError) {
66+
this.continueOnError = continueOnError;
5667
}
5768

5869
}

core/src/test/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManagerTests.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 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.
@@ -77,4 +77,43 @@ public void authenticateWhenBadCredentialsThenDelegate2NotInvokedAndError() {
7777
.verify();
7878
}
7979

80+
@Test
81+
public void authenticateWhenContinueOnErrorAndFirstBadCredentialsThenTriesSecond() {
82+
given(this.delegate1.authenticate(any())).willReturn(Mono.error(new BadCredentialsException("Test")));
83+
given(this.delegate2.authenticate(any())).willReturn(Mono.just(this.authentication));
84+
85+
DelegatingReactiveAuthenticationManager manager = managerWithContinueOnError();
86+
87+
assertThat(manager.authenticate(this.authentication).block()).isEqualTo(this.authentication);
88+
}
89+
90+
@Test
91+
public void authenticateWhenContinueOnErrorAndBothDelegatesBadCredentialsThenError() {
92+
given(this.delegate1.authenticate(any())).willReturn(Mono.error(new BadCredentialsException("Test")));
93+
given(this.delegate2.authenticate(any())).willReturn(Mono.error(new BadCredentialsException("Test")));
94+
95+
DelegatingReactiveAuthenticationManager manager = managerWithContinueOnError();
96+
97+
StepVerifier.create(manager.authenticate(this.authentication))
98+
.expectError(BadCredentialsException.class)
99+
.verify();
100+
}
101+
102+
@Test
103+
public void authenticateWhenContinueOnErrorAndDelegate1NotEmptyThenReturnsNotEmpty() {
104+
given(this.delegate1.authenticate(any())).willReturn(Mono.just(this.authentication));
105+
106+
DelegatingReactiveAuthenticationManager manager = managerWithContinueOnError();
107+
108+
assertThat(manager.authenticate(this.authentication).block()).isEqualTo(this.authentication);
109+
}
110+
111+
private DelegatingReactiveAuthenticationManager managerWithContinueOnError() {
112+
DelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,
113+
this.delegate2);
114+
manager.setContinueOnError(true);
115+
116+
return manager;
117+
}
118+
80119
}

0 commit comments

Comments
 (0)