Skip to content

Commit 5714d57

Browse files
jzheauxAyush Kohli
authored andcommitted
Add AfterMethodAuthorizationManager
- Removes the need to keep MethodAuthorizationContext#returnObject in sync with other method parameters - Restores MethodAuthorizationContext's immutability Closes spring-projectsgh-9591
1 parent 74a3365 commit 5714d57

12 files changed

+176
-74
lines changed

config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> createDefaul
141141
if (jsr250Enabled()) {
142142
beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice());
143143
}
144-
return new DelegatingAuthorizationMethodBeforeAdvice(beforeAdvices);
144+
return new DelegatingAuthorizationMethodBeforeAdvice<>(beforeAdvices);
145145
}
146146

147147
private PreFilterAuthorizationMethodBeforeAdvice getPreFilterAuthorizationMethodBeforeAdvice() {
@@ -192,7 +192,7 @@ private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> createDefault
192192
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>();
193193
afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice());
194194
afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice());
195-
return new DelegatingAuthorizationMethodAfterAdvice(afterAdvices);
195+
return new DelegatingAuthorizationMethodAfterAdvice<>(afterAdvices);
196196
}
197197

198198
private PostFilterAuthorizationMethodAfterAdvice getPostFilterAuthorizationMethodAfterAdvice() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2002-2021 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.method;
18+
19+
import java.util.function.Supplier;
20+
21+
import org.aopalliance.intercept.MethodInvocation;
22+
23+
import org.springframework.lang.Nullable;
24+
import org.springframework.security.access.AccessDeniedException;
25+
import org.springframework.security.authorization.AuthorizationDecision;
26+
import org.springframework.security.core.Authentication;
27+
28+
/**
29+
* An Authorization manager which can determine if an {@link Authentication} has access to
30+
* a specific object and associated return object. Intended for use specifically to
31+
* evaluate the returning state of a method invocation.
32+
*
33+
* @param <T> the type of object that the authorization check is being done one.
34+
* @author Josh Cummings
35+
* @author Evgeniy Cheban
36+
* @since 5.5
37+
*/
38+
public interface AfterMethodAuthorizationManager<T> {
39+
40+
/**
41+
* Determines if access should be granted for a specific authentication and
42+
* returnedObject.
43+
* @param authentication the {@link Supplier} of the {@link Authentication} to check
44+
* @param object the {@code T} object to check, typically a {@link MethodInvocation}
45+
* @param returnedObject the returnedObject from the method invocation to check
46+
* @throws AccessDeniedException if access is not granted
47+
*/
48+
default void verify(Supplier<Authentication> authentication, T object, Object returnedObject) {
49+
AuthorizationDecision decision = check(authentication, object, returnedObject);
50+
if (decision != null && !decision.isGranted()) {
51+
throw new AccessDeniedException("Access Denied");
52+
}
53+
}
54+
55+
/**
56+
* Determines if access is granted for a specific authentication and returnedObject.
57+
* @param authentication the {@link Supplier} of the {@link Authentication} to check
58+
* @param object the {@code T} object to check, typically a {@link MethodInvocation}
59+
* @param returnedObject the returned object from the method invocation to check
60+
* @return an {@link AuthorizationDecision} or null if no decision could be made
61+
*/
62+
@Nullable
63+
AuthorizationDecision check(Supplier<Authentication> authentication, T object, Object returnedObject);
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2002-2021 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.method;
18+
19+
import java.util.function.Supplier;
20+
21+
import org.aopalliance.intercept.MethodInvocation;
22+
23+
import org.springframework.security.authorization.AuthorizationDecision;
24+
import org.springframework.security.authorization.AuthorizationManager;
25+
import org.springframework.security.core.Authentication;
26+
27+
/**
28+
* Adapts an {@link AuthorizationManager} into an {@link AfterMethodAuthorizationManager}
29+
*
30+
* @param <T> the {@code T} object to authorize, typically a {@link MethodInvocation}
31+
* @author Josh Cummings
32+
* @since 5.5
33+
*/
34+
public final class AfterMethodAuthorizationManagerAdapter<T> implements AfterMethodAuthorizationManager<T> {
35+
36+
private final AuthorizationManager<T> authorizationManager;
37+
38+
/**
39+
* Construct a {@link AfterMethodAuthorizationManagerAdapter} with the provided
40+
* parameters
41+
* @param authorizationManager the {@link AuthorizationManager} to adapt
42+
*/
43+
public AfterMethodAuthorizationManagerAdapter(AuthorizationManager<T> authorizationManager) {
44+
this.authorizationManager = authorizationManager;
45+
}
46+
47+
/**
48+
* Determine if access is granted for a specific authentication and {@code T} object.
49+
*
50+
* Note that the {@code returnedObject} parameter is ignored
51+
* @param authentication the {@link Supplier} of the {@link Authentication} to check
52+
* @param object the {@code T} object to check, typically a {@link MethodInvocation}
53+
* @param returnedObject the returned object from the method invocation, ignored in
54+
* this implementation
55+
* @return an {@link AuthorizationDecision} or null if no decision could be made
56+
*/
57+
@Override
58+
public AuthorizationDecision check(Supplier<Authentication> authentication, T object, Object returnedObject) {
59+
return this.authorizationManager.check(authentication, object);
60+
}
61+
62+
}

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@ public final class AuthorizationManagerMethodAfterAdvice<T> implements Authoriza
3939

4040
private final Pointcut pointcut;
4141

42-
private final AuthorizationManager<T> authorizationManager;
42+
private final AfterMethodAuthorizationManager<T> authorizationManager;
4343

4444
/**
4545
* Creates an instance.
4646
* @param pointcut the {@link Pointcut} to use
4747
* @param authorizationManager the {@link AuthorizationManager} to use
4848
*/
49-
public AuthorizationManagerMethodAfterAdvice(Pointcut pointcut, AuthorizationManager<T> authorizationManager) {
49+
public AuthorizationManagerMethodAfterAdvice(Pointcut pointcut,
50+
AfterMethodAuthorizationManager<T> authorizationManager) {
5051
Assert.notNull(pointcut, "pointcut cannot be null");
5152
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
5253
this.pointcut = pointcut;
@@ -57,13 +58,14 @@ public AuthorizationManagerMethodAfterAdvice(Pointcut pointcut, AuthorizationMan
5758
* Determines if an {@link Authentication} has access to the {@link T} object using
5859
* the {@link AuthorizationManager}.
5960
* @param authentication the {@link Supplier} of the {@link Authentication} to check
60-
* @param object the {@link T} object to check
61+
* @param object the {@link T} object to check - note that {@code T} should contain
62+
* the returned object
6163
* @throws AccessDeniedException if access is not granted
6264
*/
6365
@Override
64-
public Object after(Supplier<Authentication> authentication, T object, Object returnedObject) {
65-
this.authorizationManager.verify(authentication, object);
66-
return returnedObject;
66+
public Object after(Supplier<Authentication> authentication, T context, Object object) {
67+
this.authorizationManager.verify(authentication, context, object);
68+
return object;
6769
}
6870

6971
/**

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
import org.springframework.security.core.Authentication;
2828

2929
/**
30-
* An {@link Advice} which can determine if an {@link Authentication} has
31-
* access to the returned object from the {@link MethodInvocation}. {@link #getPointcut()}
32-
* describes when the advice applies for the method.
30+
* An {@link Advice} which can determine if an {@link Authentication} has access to the
31+
* returned object from the {@link MethodInvocation}. {@link #getPointcut()} describes
32+
* when the advice applies for the method.
3333
*
3434
* @param <T> the type of object that the authorization check is being done one.
3535
* @author Evgeniy Cheban

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,19 @@
3838
* @author Josh Cummings
3939
* @since 5.5
4040
*/
41-
public final class DelegatingAuthorizationMethodAfterAdvice
42-
implements AuthorizationMethodAfterAdvice<MethodAuthorizationContext> {
41+
public final class DelegatingAuthorizationMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> {
4342

4443
private final Log logger = LogFactory.getLog(getClass());
4544

4645
private final Pointcut pointcut;
4746

48-
private final List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates;
47+
private final List<AuthorizationMethodAfterAdvice<T>> delegates;
4948

5049
/**
5150
* Creates an instance.
5251
* @param delegates the {@link AuthorizationMethodAfterAdvice}s to use
5352
*/
54-
public DelegatingAuthorizationMethodAfterAdvice(
55-
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates) {
53+
public DelegatingAuthorizationMethodAfterAdvice(List<AuthorizationMethodAfterAdvice<T>> delegates) {
5654
Assert.notEmpty(delegates, "delegates cannot be empty");
5755
this.delegates = delegates;
5856
ComposablePointcut pointcut = null;
@@ -79,26 +77,24 @@ public Pointcut getPointcut() {
7977
* Delegates to specific {@link AuthorizationMethodAfterAdvice}s and returns the
8078
* <code>returnedObject</code> (possibly modified) from the method argument.
8179
* @param authentication the {@link Supplier} of the {@link Authentication} to check
82-
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
80+
* @param object the {@link MethodAuthorizationContext} to check
8381
* @param returnedObject the returned object from the {@link MethodInvocation} to
8482
* check
8583
* @return the <code>returnedObject</code> (possibly modified) from the method
8684
* argument
8785
*/
8886
@Override
89-
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext,
90-
Object returnedObject) {
87+
public Object after(Supplier<Authentication> authentication, T object, Object returnedObject) {
9188
if (this.logger.isTraceEnabled()) {
92-
this.logger.trace(
93-
LogMessage.format("Post Authorizing %s from %s", returnedObject, methodAuthorizationContext));
89+
this.logger.trace(LogMessage.format("Post Authorizing %s from %s", returnedObject, object));
9490
}
9591
Object result = returnedObject;
96-
for (AuthorizationMethodAfterAdvice<MethodAuthorizationContext> delegate : this.delegates) {
92+
for (AuthorizationMethodAfterAdvice<T> delegate : this.delegates) {
9793
if (this.logger.isTraceEnabled()) {
98-
this.logger.trace(LogMessage.format("Checking authorization on %s from %s using %s", result,
99-
methodAuthorizationContext, delegate));
94+
this.logger.trace(
95+
LogMessage.format("Checking authorization on %s from %s using %s", result, object, delegate));
10096
}
101-
result = delegate.after(authentication, methodAuthorizationContext, result);
97+
result = delegate.after(authentication, object, result);
10298
}
10399
return result;
104100
}

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

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,19 @@
3838
* @author Josh Cummings
3939
* @since 5.5
4040
*/
41-
public final class DelegatingAuthorizationMethodBeforeAdvice
42-
implements AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> {
41+
public final class DelegatingAuthorizationMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> {
4342

4443
private final Log logger = LogFactory.getLog(getClass());
4544

4645
private final Pointcut pointcut;
4746

48-
private final List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates;
47+
private final List<AuthorizationMethodBeforeAdvice<T>> delegates;
4948

5049
/**
5150
* Creates an instance.
5251
* @param delegates the {@link AuthorizationMethodBeforeAdvice}s to use
5352
*/
54-
public DelegatingAuthorizationMethodBeforeAdvice(
55-
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates) {
53+
public DelegatingAuthorizationMethodBeforeAdvice(List<AuthorizationMethodBeforeAdvice<T>> delegates) {
5654
Assert.notEmpty(delegates, "delegates cannot be empty");
5755
this.delegates = delegates;
5856
ComposablePointcut pointcut = null;
@@ -80,19 +78,18 @@ public Pointcut getPointcut() {
8078
* if all {@link AuthorizationMethodBeforeAdvice}s granted or abstained. Denies only
8179
* if one of the {@link AuthorizationMethodBeforeAdvice}s denied.
8280
* @param authentication the {@link Supplier} of the {@link Authentication} to check
83-
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
81+
* @param object the {@link MethodAuthorizationContext} to check
8482
*/
8583
@Override
86-
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext) {
84+
public void before(Supplier<Authentication> authentication, T object) {
8785
if (this.logger.isTraceEnabled()) {
88-
this.logger.trace(LogMessage.format("Pre Authorizing %s", methodAuthorizationContext));
86+
this.logger.trace(LogMessage.format("Pre Authorizing %s", object));
8987
}
90-
for (AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> delegate : this.delegates) {
88+
for (AuthorizationMethodBeforeAdvice<T> delegate : this.delegates) {
9189
if (this.logger.isTraceEnabled()) {
92-
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", methodAuthorizationContext,
93-
delegate));
90+
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", object, delegate));
9491
}
95-
delegate.before(authentication, methodAuthorizationContext);
92+
delegate.before(authentication, object);
9693
}
9794
}
9895

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

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ public final class MethodAuthorizationContext {
3131

3232
private final Class<?> targetClass;
3333

34-
private Object returnObject;
35-
3634
/**
3735
* Creates an instance.
3836
* @param methodInvocation the {@link MethodInvocation} to use
@@ -59,26 +57,10 @@ public Class<?> getTargetClass() {
5957
return this.targetClass;
6058
}
6159

62-
/**
63-
* Returns the returned object from the {@link MethodInvocation}.
64-
* @return the returned object from the {@link MethodInvocation} to use
65-
*/
66-
public Object getReturnObject() {
67-
return this.returnObject;
68-
}
69-
70-
/**
71-
* Sets the returned object from the {@link MethodInvocation}.
72-
* @param returnObject the returned object from the {@link MethodInvocation} to use
73-
*/
74-
public void setReturnObject(Object returnObject) {
75-
this.returnObject = returnObject;
76-
}
77-
7860
@Override
7961
public String toString() {
8062
return "MethodAuthorizationContext[methodInvocation=" + this.methodInvocation + ", targetClass="
81-
+ this.targetClass + ", returnObject=" + this.returnObject + ']';
63+
+ this.targetClass + ']';
8264
}
8365

8466
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
* @author Evgeniy Cheban
4444
* @since 5.5
4545
*/
46-
public final class PostAuthorizeAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
46+
public final class PostAuthorizeAuthorizationManager
47+
implements AfterMethodAuthorizationManager<MethodAuthorizationContext> {
4748

4849
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
4950

@@ -68,14 +69,14 @@ public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandl
6869
*/
6970
@Override
7071
public AuthorizationDecision check(Supplier<Authentication> authentication,
71-
MethodAuthorizationContext methodAuthorizationContext) {
72+
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) {
7273
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
7374
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
7475
return null;
7576
}
7677
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
7778
methodAuthorizationContext.getMethodInvocation());
78-
this.expressionHandler.setReturnObject(methodAuthorizationContext.getReturnObject(), ctx);
79+
this.expressionHandler.setReturnObject(returnedObject, ctx);
7980
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
8081
return new AuthorizationDecision(granted);
8182
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ public Pointcut getPointcut() {
8282
* evaluating an expression from the {@link PostFilter} annotation.
8383
* @param authentication the {@link Supplier} of the {@link Authentication} to check
8484
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
85-
* @param returnedObject the returned object from the {@link MethodInvocation} to
8685
* check
8786
* @return filtered <code>returnedObject</code> from the {@link MethodInvocation}
8887
*/
@@ -98,9 +97,7 @@ public Object after(Supplier<Authentication> authentication, MethodAuthorization
9897
}
9998
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
10099
methodAuthorizationContext.getMethodInvocation());
101-
Object result = this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
102-
methodAuthorizationContext.setReturnObject(result);
103-
return result;
100+
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
104101
}
105102

106103
private final class PostFilterExpressionAttributeRegistry

0 commit comments

Comments
 (0)