Skip to content

Commit e014a6a

Browse files
Add AuthorizationResult Support to AuthorizationManager
Closes gh-14843
1 parent 3b20844 commit e014a6a

20 files changed

+181
-109
lines changed

config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java

Lines changed: 4 additions & 3 deletions
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.
@@ -34,6 +34,7 @@
3434
import org.springframework.security.authentication.TestingAuthenticationToken;
3535
import org.springframework.security.authorization.AuthorizationDecision;
3636
import org.springframework.security.authorization.AuthorizationManager;
37+
import org.springframework.security.authorization.AuthorizationResult;
3738
import org.springframework.security.core.Authentication;
3839
import org.springframework.security.web.DefaultSecurityFilterChain;
3940
import org.springframework.security.web.FilterChainProxy;
@@ -221,7 +222,7 @@ private boolean checkLoginPageIsPublic(List<Filter> filters, FilterInvocation lo
221222
AuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter
222223
.getAuthorizationManager();
223224
try {
224-
AuthorizationDecision decision = authorizationManager.check(() -> TEST, loginRequest.getHttpRequest());
225+
AuthorizationResult decision = authorizationManager.authorize(() -> TEST, loginRequest.getHttpRequest());
225226
return decision != null && decision.isGranted();
226227
}
227228
catch (Exception ex) {
@@ -252,7 +253,7 @@ private Supplier<Boolean> deriveAnonymousCheck(List<Filter> filters, FilterInvoc
252253
return () -> {
253254
AuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter
254255
.getAuthorizationManager();
255-
AuthorizationDecision decision = authorizationManager.check(() -> token, loginRequest.getHttpRequest());
256+
AuthorizationResult decision = authorizationManager.authorize(() -> token, loginRequest.getHttpRequest());
256257
return decision != null && decision.isGranted();
257258
};
258259
}

config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java

Lines changed: 3 additions & 3 deletions
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.
@@ -90,13 +90,13 @@ public void getWhenUsingAuthorizationManagerThenRedirectsToLogin() throws Except
9090
this.spring.configLocations(this.xml("AuthorizationManager")).autowire();
9191
AuthorizationManager<HttpServletRequest> authorizationManager = this.spring.getContext()
9292
.getBean(AuthorizationManager.class);
93-
given(authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
93+
given(authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(false));
9494
// @formatter:off
9595
this.mvc.perform(get("/"))
9696
.andExpect(status().isFound())
9797
.andExpect(redirectedUrl("http://localhost/login"));
9898
// @formatter:on
99-
verify(authorizationManager).check(any(), any());
99+
verify(authorizationManager).authorize(any(), any());
100100
}
101101

102102
@Test

core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java

Lines changed: 19 additions & 1 deletion
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.
@@ -42,8 +42,26 @@ public interface AuthorizationEventPublisher {
4242
* @param object the secured object
4343
* @param decision the decision about whether the user may access the secured object
4444
* @param <T> the secured object's type
45+
* @deprecated use {@link #publishAuthorizationEvent(Supplier, Object, AuthorizationResult)} instead
4546
*/
47+
@Deprecated
4648
<T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
4749
AuthorizationDecision decision);
4850

51+
/**
52+
* Publish the given details in the form of an event, typically
53+
* {@link AuthorizationGrantedEvent} or {@link AuthorizationDeniedEvent}.
54+
*
55+
* Note that success events can be very noisy if enabled by default. Because of this
56+
* implementations may choose to drop success events by default.
57+
* @param authentication a {@link Supplier} for the current user
58+
* @param object the secured object
59+
* @param decision {@link AuthorizationResult} the decision about whether the user may access the secured object
60+
* @param <T> the secured object's type
61+
* @since 6.4
62+
*/
63+
default <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
64+
AuthorizationResult decision) {
65+
publishAuthorizationEvent(authentication, object,(AuthorizationDecision) decision);
66+
}
4967
}

core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 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.
@@ -50,8 +50,20 @@ default void verify(Supplier<Authentication> authentication, T object) {
5050
* @param authentication the {@link Supplier} of the {@link Authentication} to check
5151
* @param object the {@link T} object to check
5252
* @return an {@link AuthorizationDecision} or null if no decision could be made
53+
* @deprecated please use {@link #authorize(Supplier, Object)} instead
5354
*/
5455
@Nullable
56+
@Deprecated
5557
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
5658

59+
/**
60+
* Determines if access is granted for a specific authentication and object.
61+
* @param authentication the {@link Supplier} of the {@link Authentication} to authorize
62+
* @param object the {@link T} object to authorize
63+
* @return an {@link AuthorizationResult}
64+
* @since 6.4
65+
*/
66+
default AuthorizationResult authorize(Supplier<Authentication> authentication, T object){
67+
return check(authentication, object);
68+
}
5769
}

core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java

Lines changed: 21 additions & 4 deletions
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.
@@ -33,7 +33,7 @@ public class AuthorizationObservationContext<T> extends Observation.Context {
3333

3434
private final T object;
3535

36-
private AuthorizationDecision decision;
36+
private AuthorizationResult authorizationResult;
3737

3838
public AuthorizationObservationContext(T object) {
3939
Assert.notNull(object, "object cannot be null");
@@ -73,15 +73,32 @@ public T getObject() {
7373
* @return the observed {@link AuthorizationDecision}
7474
*/
7575
public AuthorizationDecision getDecision() {
76-
return this.decision;
76+
return (AuthorizationDecision) this.authorizationResult;
7777
}
7878

7979
/**
8080
* Set the observed {@link AuthorizationDecision}
8181
* @param decision the observed {@link AuthorizationDecision}
8282
*/
8383
public void setDecision(AuthorizationDecision decision) {
84-
this.decision = decision;
84+
this.authorizationResult = decision;
8585
}
8686

87+
/**
88+
* Get the observed {@link AuthorizationResult}
89+
* @return the observed {@link AuthorizationResult}
90+
* @since 6.4
91+
*/
92+
public AuthorizationResult getAuthorizationResult() {
93+
return this.authorizationResult;
94+
}
95+
96+
/**
97+
* Set the observed {@link AuthorizationResult}
98+
* @param authorizationResult the observed {@link AuthorizationResult}
99+
* @since 6.4
100+
*/
101+
public void setAuthorizationResult(AuthorizationResult authorizationResult) {
102+
this.authorizationResult = authorizationResult;
103+
}
87104
}

core/src/main/java/org/springframework/security/authorization/AuthorizationObservationConvention.java

Lines changed: 5 additions & 5 deletions
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.
@@ -100,10 +100,10 @@ private String getObjectType(AuthorizationObservationContext<?> context) {
100100
}
101101

102102
private String getAuthorizationDecision(AuthorizationObservationContext<?> context) {
103-
if (context.getDecision() == null) {
103+
if (context.getAuthorizationResult() == null) {
104104
return "unknown";
105105
}
106-
return String.valueOf(context.getDecision().isGranted());
106+
return String.valueOf(context.getAuthorizationResult().isGranted());
107107
}
108108

109109
private String getAuthorities(AuthorizationObservationContext<?> context) {
@@ -114,10 +114,10 @@ private String getAuthorities(AuthorizationObservationContext<?> context) {
114114
}
115115

116116
private String getDecisionDetails(AuthorizationObservationContext<?> context) {
117-
if (context.getDecision() == null) {
117+
if (context.getAuthorizationResult() == null) {
118118
return "unknown";
119119
}
120-
AuthorizationDecision decision = context.getDecision();
120+
AuthorizationResult decision = context.getAuthorizationResult();
121121
return String.valueOf(decision);
122122
}
123123

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2002-2024 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.authorization;
18+
19+
import java.util.function.Supplier;
20+
21+
import org.springframework.security.core.Authentication;
22+
23+
/**
24+
* An {@link AuthorizationEventPublisher} implementation that does not authorization publish events
25+
*
26+
* @author Max Batischev
27+
* @since 6.4
28+
*/
29+
public final class NoopAuthorizationEventPublisher implements AuthorizationEventPublisher {
30+
31+
@Override
32+
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object, AuthorizationDecision decision) {
33+
34+
}
35+
36+
@Override
37+
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object, AuthorizationResult decision) {
38+
}
39+
}

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
import org.springframework.security.access.AccessDeniedException;
3030
import org.springframework.security.access.prepost.PostAuthorize;
3131
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
32-
import org.springframework.security.authorization.AuthorizationDecision;
3332
import org.springframework.security.authorization.AuthorizationDeniedException;
3433
import org.springframework.security.authorization.AuthorizationEventPublisher;
3534
import org.springframework.security.authorization.AuthorizationManager;
35+
import org.springframework.security.authorization.AuthorizationResult;
36+
import org.springframework.security.authorization.NoopAuthorizationEventPublisher;
3637
import org.springframework.security.core.Authentication;
3738
import org.springframework.security.core.context.SecurityContextHolder;
3839
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -60,7 +61,7 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
6061

6162
private int order;
6263

63-
private AuthorizationEventPublisher eventPublisher = AuthorizationManagerAfterMethodInterceptor::noPublish;
64+
private AuthorizationEventPublisher eventPublisher = new NoopAuthorizationEventPublisher();
6465

6566
/**
6667
* Creates an instance.
@@ -182,7 +183,7 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strat
182183
private Object attemptAuthorization(MethodInvocation mi, Object result) {
183184
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
184185
MethodInvocationResult object = new MethodInvocationResult(mi, result);
185-
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, object);
186+
AuthorizationResult decision = this.authorizationManager.authorize(this::getAuthentication, object);
186187
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, decision);
187188
if (decision != null && !decision.isGranted()) {
188189
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
@@ -193,7 +194,7 @@ private Object attemptAuthorization(MethodInvocation mi, Object result) {
193194
return result;
194195
}
195196

196-
private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationDecision decision) {
197+
private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationResult decision) {
197198
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler deniedHandler) {
198199
return deniedHandler.handleDeniedInvocationResult(mi, decision);
199200
}
@@ -209,9 +210,4 @@ private Authentication getAuthentication() {
209210
return authentication;
210211
}
211212

212-
private static <T> void noPublish(Supplier<Authentication> authentication, T object,
213-
AuthorizationDecision decision) {
214-
215-
}
216-
217213
}

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private boolean isMultiValue(Class<?> returnType, ReactiveAdapter adapter) {
164164

165165
private Mono<Object> postAuthorize(Mono<Authentication> authentication, MethodInvocation mi, Object result) {
166166
MethodInvocationResult invocationResult = new MethodInvocationResult(mi, result);
167-
return this.authorizationManager.check(authentication, invocationResult)
167+
return this.authorizationManager.authorize(authentication, invocationResult)
168168
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
169169
.flatMap((decision) -> postProcess(decision, invocationResult));
170170
}

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@
3333
import org.springframework.security.access.annotation.Secured;
3434
import org.springframework.security.access.prepost.PreAuthorize;
3535
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
36-
import org.springframework.security.authorization.AuthorizationDecision;
3736
import org.springframework.security.authorization.AuthorizationDeniedException;
3837
import org.springframework.security.authorization.AuthorizationEventPublisher;
3938
import org.springframework.security.authorization.AuthorizationManager;
4039
import org.springframework.security.authorization.AuthorizationResult;
40+
import org.springframework.security.authorization.NoopAuthorizationEventPublisher;
4141
import org.springframework.security.core.Authentication;
4242
import org.springframework.security.core.context.SecurityContextHolder;
4343
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -65,7 +65,7 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author
6565

6666
private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
6767

68-
private AuthorizationEventPublisher eventPublisher = AuthorizationManagerBeforeMethodInterceptor::noPublish;
68+
private AuthorizationEventPublisher eventPublisher = new NoopAuthorizationEventPublisher();
6969

7070
/**
7171
* Creates an instance.
@@ -247,9 +247,9 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur
247247

248248
private Object attemptAuthorization(MethodInvocation mi) throws Throwable {
249249
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
250-
AuthorizationDecision decision;
250+
AuthorizationResult decision;
251251
try {
252-
decision = this.authorizationManager.check(this::getAuthentication, mi);
252+
decision = this.authorizationManager.authorize(this::getAuthentication, mi);
253253
}
254254
catch (AuthorizationDeniedException denied) {
255255
return handle(mi, denied);
@@ -298,10 +298,4 @@ private Authentication getAuthentication() {
298298
}
299299
return authentication;
300300
}
301-
302-
private static <T> void noPublish(Supplier<Authentication> authentication, T object,
303-
AuthorizationDecision decision) {
304-
305-
}
306-
307301
}

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ private Flux<Object> preAuthorized(MethodInvocation mi, Flux<Object> mapping) {
153153

154154
private Mono<Object> preAuthorized(MethodInvocation mi, Mono<Object> mapping) {
155155
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
156-
return this.authorizationManager.check(authentication, mi)
156+
return this.authorizationManager.authorize(authentication, mi)
157157
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
158158
.flatMap((decision) -> {
159159
if (decision.isGranted()) {

core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 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,7 +77,7 @@ public void beforeWhenMockAuthorizationManagerThenCheckAndReturnedObject() throw
7777
Pointcut.TRUE, mockAuthorizationManager);
7878
Object returnedObject = advice.invoke(mockMethodInvocation);
7979
assertThat(returnedObject).isEqualTo(result.getResult());
80-
verify(mockAuthorizationManager).check(any(Supplier.class), any(MethodInvocationResult.class));
80+
verify(mockAuthorizationManager).authorize(any(Supplier.class), any(MethodInvocationResult.class));
8181
}
8282

8383
@Test
@@ -139,15 +139,15 @@ public void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable {
139139

140140
advice.invoke(mockMethodInvocation);
141141
verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocationResult.class),
142-
any(AuthorizationDecision.class));
142+
any(AuthorizationResult.class));
143143
}
144144

145145
@Test
146146
public void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Throwable {
147147
MethodInvocation mi = mock(MethodInvocation.class);
148148
given(mi.proceed()).willReturn("ok");
149149
AuthorizationManager<MethodInvocationResult> manager = mock(AuthorizationManager.class);
150-
given(manager.check(any(), any()))
150+
given(manager.authorize(any(), any()))
151151
.willThrow(new MyAuthzDeniedException("denied", new AuthorizationDecision(false)));
152152
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
153153
Pointcut.TRUE, manager);

0 commit comments

Comments
 (0)