Skip to content

Commit e764492

Browse files
franticticktickjzheaux
authored andcommitted
Add AuthorizationResult support for AuthorizationManager
Closes gh-14843
1 parent 702538e commit e764492

File tree

25 files changed

+134
-34
lines changed

25 files changed

+134
-34
lines changed

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

Lines changed: 6 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.
@@ -32,8 +32,8 @@
3232
import org.springframework.security.access.ConfigAttribute;
3333
import org.springframework.security.authentication.AnonymousAuthenticationToken;
3434
import org.springframework.security.authentication.TestingAuthenticationToken;
35-
import org.springframework.security.authorization.AuthorizationDecision;
3635
import org.springframework.security.authorization.AuthorizationManager;
36+
import org.springframework.security.authorization.AuthorizationResult;
3737
import org.springframework.security.core.Authentication;
3838
import org.springframework.security.web.DefaultSecurityFilterChain;
3939
import org.springframework.security.web.FilterChainProxy;
@@ -221,7 +221,8 @@ private boolean checkLoginPageIsPublic(List<Filter> filters, FilterInvocation lo
221221
AuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter
222222
.getAuthorizationManager();
223223
try {
224-
AuthorizationDecision decision = authorizationManager.check(() -> TEST, loginRequest.getHttpRequest());
224+
AuthorizationResult decision = authorizationManager.authorize(() -> TEST,
225+
loginRequest.getHttpRequest());
225226
return decision != null && decision.isGranted();
226227
}
227228
catch (Exception ex) {
@@ -252,7 +253,8 @@ 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,
257+
loginRequest.getHttpRequest());
256258
return decision != null && decision.isGranted();
257259
};
258260
}

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

Lines changed: 2 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.
@@ -113,6 +113,7 @@ public void validateCheckLoginPageIsntProtectedThrowsIllegalArgumentException()
113113
@Test
114114
public void validateCheckLoginPageAllowsAnonymous() {
115115
given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
116+
given(this.authorizationManager.authorize(any(), any())).willCallRealMethod();
116117
this.validator.validate(this.chainAuthorizationFilter);
117118
verify(this.logger).warn("Anonymous access to the login page doesn't appear to be enabled. "
118119
+ "This is almost certainly an error. Please check your configuration allows unauthenticated "

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

Lines changed: 2 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.
@@ -91,6 +91,7 @@ public void getWhenUsingAuthorizationManagerThenRedirectsToLogin() throws Except
9191
AuthorizationManager<HttpServletRequest> authorizationManager = this.spring.getContext()
9292
.getBean(AuthorizationManager.class);
9393
given(authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
94+
given(authorizationManager.authorize(any(), any())).willCallRealMethod();
9495
// @formatter:off
9596
this.mvc.perform(get("/"))
9697
.andExpect(status().isFound())

config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ public void sendWhenCustomAuthorizationManagerThenAuthorizesAccordingly() {
514514
AuthorizationManager<Message<?>> authorizationManager = this.spring.getContext()
515515
.getBean(AuthorizationManager.class);
516516
given(authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
517+
given(authorizationManager.authorize(any(), any())).willCallRealMethod();
517518
Message<?> message = message("/any");
518519
assertThatExceptionOfType(Exception.class).isThrownBy(send(message))
519520
.withCauseInstanceOf(AccessDeniedException.class);

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

Lines changed: 16 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,23 @@ 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
62+
* authorize
63+
* @param object the {@link T} object to authorize
64+
* @return an {@link AuthorizationResult}
65+
* @since 6.4
66+
*/
67+
@Nullable
68+
default AuthorizationResult authorize(Supplier<Authentication> authentication, T object) {
69+
return check(authentication, object);
70+
}
71+
5772
}

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

Lines changed: 30 additions & 2 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.
@@ -35,6 +35,8 @@ public class AuthorizationObservationContext<T> extends Observation.Context {
3535

3636
private AuthorizationDecision decision;
3737

38+
private AuthorizationResult authorizationResult;
39+
3840
public AuthorizationObservationContext(T object) {
3941
Assert.notNull(object, "object cannot be null");
4042
this.object = object;
@@ -71,17 +73,43 @@ public T getObject() {
7173
/**
7274
* Get the observed {@link AuthorizationDecision}
7375
* @return the observed {@link AuthorizationDecision}
76+
* @deprecated please use {@link #getAuthorizationResult()} instead
7477
*/
78+
@Deprecated
7579
public AuthorizationDecision getDecision() {
76-
return this.decision;
80+
Assert.isInstanceOf(AuthorizationDecision.class, this.authorizationResult,
81+
"Please call getAuthorizationResult instead. If you must call getDecision, please ensure that the result you provide is of type AuthorizationDecision");
82+
return (AuthorizationDecision) this.authorizationResult;
7783
}
7884

7985
/**
8086
* Set the observed {@link AuthorizationDecision}
8187
* @param decision the observed {@link AuthorizationDecision}
88+
* @deprecated please use {@link #setAuthorizationResult(AuthorizationResult)} instead
8289
*/
90+
@Deprecated
8391
public void setDecision(AuthorizationDecision decision) {
92+
Assert.isInstanceOf(AuthorizationDecision.class, decision,
93+
"Please call setAuthorizationResult instead. If you must call getDecision, please ensure that the result you provide is of type AuthorizationDecision");
8494
this.decision = decision;
8595
}
8696

97+
/**
98+
* Get the observed {@link AuthorizationResult}
99+
* @return the observed {@link AuthorizationResult}
100+
* @since 6.4
101+
*/
102+
public AuthorizationResult getAuthorizationResult() {
103+
return this.authorizationResult;
104+
}
105+
106+
/**
107+
* Set the observed {@link AuthorizationResult}
108+
* @param authorizationResult the observed {@link AuthorizationResult}
109+
* @since 6.4
110+
*/
111+
public void setAuthorizationResult(AuthorizationResult authorizationResult) {
112+
this.authorizationResult = authorizationResult;
113+
}
114+
87115
}

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

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

Lines changed: 2 additions & 2 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.
@@ -71,7 +71,7 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, T ob
7171
Observation observation = Observation.createNotStarted(this.convention, () -> context, this.registry).start();
7272
try (Observation.Scope scope = observation.openScope()) {
7373
AuthorizationDecision decision = this.delegate.check(wrapped, object);
74-
context.setDecision(decision);
74+
context.setAuthorizationResult(decision);
7575
if (decision != null && !decision.isGranted()) {
7676
observation.error(new AccessDeniedException(
7777
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access Denied")));

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

Lines changed: 2 additions & 2 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.
@@ -68,7 +68,7 @@ public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T
6868
.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null))
6969
.start();
7070
return this.delegate.check(wrapped, object).doOnSuccess((decision) -> {
71-
context.setDecision(decision);
71+
context.setAuthorizationResult(decision);
7272
if (decision == null || !decision.isGranted()) {
7373
observation.error(new AccessDeniedException("Access Denied"));
7474
}

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 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.
@@ -36,7 +36,9 @@ public interface ReactiveAuthorizationManager<T> {
3636
* @param authentication the Authentication to check
3737
* @param object the object to check
3838
* @return an decision or empty Mono if no decision could be made.
39+
* @deprecated please use {@link #authorize(Mono, Object)} instead
3940
*/
41+
@Deprecated
4042
Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object);
4143

4244
/**
@@ -55,4 +57,15 @@ default Mono<Void> verify(Mono<Authentication> authentication, T object) {
5557
// @formatter:on
5658
}
5759

60+
/**
61+
* Determines if access is granted for a specific authentication and object.
62+
* @param authentication the Authentication to authorize
63+
* @param object the object to check
64+
* @return an decision or empty Mono if no decision could be made.
65+
* @since 6.4
66+
*/
67+
default Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, T object) {
68+
return check(authentication, object).cast(AuthorizationResult.class);
69+
}
70+
5871
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
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;
3636
import org.springframework.security.core.Authentication;
3737
import org.springframework.security.core.context.SecurityContextHolder;
3838
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -182,7 +182,7 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strat
182182
private Object attemptAuthorization(MethodInvocation mi, Object result) {
183183
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
184184
MethodInvocationResult object = new MethodInvocationResult(mi, result);
185-
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, object);
185+
AuthorizationResult decision = this.authorizationManager.authorize(this::getAuthentication, object);
186186
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, decision);
187187
if (decision != null && !decision.isGranted()) {
188188
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
@@ -193,7 +193,7 @@ private Object attemptAuthorization(MethodInvocation mi, Object result) {
193193
return result;
194194
}
195195

196-
private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationDecision decision) {
196+
private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationResult decision) {
197197
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler deniedHandler) {
198198
return deniedHandler.handleDeniedInvocationResult(mi, decision);
199199
}

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: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
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;
@@ -247,9 +246,9 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur
247246

248247
private Object attemptAuthorization(MethodInvocation mi) throws Throwable {
249248
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
250-
AuthorizationDecision decision;
249+
AuthorizationResult decision;
251250
try {
252-
decision = this.authorizationManager.check(this::getAuthentication, mi);
251+
decision = this.authorizationManager.authorize(this::getAuthentication, mi);
253252
}
254253
catch (AuthorizationDeniedException denied) {
255254
return handle(mi, denied);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public Object invoke(MethodInvocation mi) throws Throwable {
140140

141141
private Flux<Object> preAuthorized(MethodInvocation mi, Flux<Object> mapping) {
142142
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
143-
return this.authorizationManager.check(authentication, mi)
143+
return this.authorizationManager.authorize(authentication, mi)
144144
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
145145
.flatMapMany((decision) -> {
146146
if (decision.isGranted()) {
@@ -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/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public <T> void publishAuthorizationEvent(Supplier<Authentication> authenticatio
3939
@Override
4040
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
4141
AuthorizationResult result) {
42+
4243
}
4344

4445
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public void beforeWhenMockAuthorizationManagerThenCheckAndReturnedObject() throw
7474
MethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());
7575
given(mockMethodInvocation.proceed()).willReturn(result.getResult());
7676
AuthorizationManager<MethodInvocationResult> mockAuthorizationManager = mock(AuthorizationManager.class);
77+
given(mockAuthorizationManager.authorize(any(), any())).willCallRealMethod();
7778
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
7879
Pointcut.TRUE, mockAuthorizationManager);
7980
Object returnedObject = advice.invoke(mockMethodInvocation);
@@ -152,6 +153,7 @@ public void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Thro
152153
AuthorizationManager<MethodInvocationResult> manager = mock(AuthorizationManager.class);
153154
given(manager.check(any(), any()))
154155
.willThrow(new MyAuthzDeniedException("denied", new AuthorizationDecision(false)));
156+
given(manager.authorize(any(), any())).willCallRealMethod();
155157
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
156158
Pointcut.TRUE, manager);
157159
assertThatExceptionOfType(MyAuthzDeniedException.class).isThrownBy(() -> advice.invoke(mi));

0 commit comments

Comments
 (0)