Skip to content

Commit 905324f

Browse files
Max BatischevMax Batischev
Max Batischev
authored and
Max Batischev
committed
Add support expressions in MethodAuthorizationDeniedHandler
Closes gh-14857
1 parent bf478d9 commit 905324f

File tree

4 files changed

+75
-0
lines changed

4 files changed

+75
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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.method;
18+
19+
import org.aopalliance.intercept.MethodInvocation;
20+
21+
import org.springframework.expression.Expression;
22+
import org.springframework.expression.ExpressionParser;
23+
import org.springframework.security.authorization.AuthorizationResult;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* {@link MethodAuthorizationDeniedHandler} implementation, that return authorization
28+
* result, based on SpEL expression.
29+
*
30+
* @author Max Batischev
31+
* @since 6.3
32+
*/
33+
final class ExpressionMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {
34+
35+
private final String expression;
36+
37+
private final ExpressionParser expressionParser;
38+
39+
ExpressionMethodAuthorizationDeniedHandler(String expression, ExpressionParser expressionParser) {
40+
Assert.notNull(expressionParser, "expressionParser cannot be null");
41+
Assert.notNull(expression, "expression cannot be null");
42+
this.expressionParser = expressionParser;
43+
this.expression = expression;
44+
}
45+
46+
@Override
47+
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
48+
Expression expression = this.expressionParser.parseExpression(this.expression);
49+
return expression.getValue();
50+
}
51+
52+
}

Diff for: core/src/main/java/org/springframework/security/authorization/method/HandleAuthorizationDenied.java

+5
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,9 @@
4747
*/
4848
Class<? extends MethodAuthorizationDeniedHandler> handlerClass() default ThrowingMethodAuthorizationDeniedHandler.class;
4949

50+
/**
51+
* @return SpEL expression to be evaluated when handling denied authorization
52+
*/
53+
String handlerExpression() default "";
54+
5055
}

Diff for: core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeExpressionAttributeRegistry.java

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.expression.Expression;
2929
import org.springframework.security.access.prepost.PreAuthorize;
3030
import org.springframework.util.Assert;
31+
import org.springframework.util.StringUtils;
3132

3233
/**
3334
* For internal use only, as this contract is likely to change.
@@ -64,6 +65,10 @@ private MethodAuthorizationDeniedHandler resolveHandler(Method method, Class<?>
6465
.withDefaults(HandleAuthorizationDenied.class);
6566
HandleAuthorizationDenied deniedHandler = lookup.apply(method);
6667
if (deniedHandler != null) {
68+
if (StringUtils.hasText(deniedHandler.handlerExpression())) {
69+
return new ExpressionMethodAuthorizationDeniedHandler(deniedHandler.handlerExpression(),
70+
getExpressionHandler().getExpressionParser());
71+
}
6772
return this.handlerResolver.apply(deniedHandler.handlerClass());
6873
}
6974
deniedHandler = lookup.apply(targetClass(method, targetClass));

Diff for: core/src/test/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManagerTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.function.Supplier;
2222

2323
import org.junit.jupiter.api.Test;
24+
import org.mockito.Mockito;
2425

2526
import org.springframework.aop.TargetClassAware;
2627
import org.springframework.core.annotation.AnnotationConfigurationException;
@@ -31,6 +32,7 @@
3132
import org.springframework.security.authentication.TestAuthentication;
3233
import org.springframework.security.authentication.TestingAuthenticationToken;
3334
import org.springframework.security.authorization.AuthorizationDecision;
35+
import org.springframework.security.authorization.AuthorizationResult;
3436
import org.springframework.security.core.Authentication;
3537

3638
import static org.assertj.core.api.Assertions.assertThat;
@@ -156,6 +158,16 @@ public void checkRequiresUserWhenMethodsFromInheritThenApplies() throws Exceptio
156158
assertThat(decision.isGranted()).isTrue();
157159
}
158160

161+
@Test
162+
public void handleDeniedInvocationWhenHandlerExpressionIsPresentThenReturnEvaluatedValue() throws Exception {
163+
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
164+
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" });
165+
AuthorizationResult authorizationResult = Mockito.mock(AuthorizationResult.class);
166+
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
167+
Object decision = manager.handleDeniedInvocation(methodInvocation, authorizationResult);
168+
assertThat(decision).isEqualTo("deny");
169+
}
170+
159171
@PreAuthorize("hasRole('USER')")
160172
public static class PreAuthorizeClass extends ParentClass {
161173

@@ -176,6 +188,7 @@ public void doSomething() {
176188
}
177189

178190
@PreAuthorize("#s == 'grant'")
191+
@HandleAuthorizationDenied(handlerExpression = "'deny'")
179192
public String doSomethingString(String s) {
180193
return s;
181194
}

0 commit comments

Comments
 (0)