Skip to content

Commit

Permalink
Add support expressions in MethodAuthorizationDeniedHandler
Browse files Browse the repository at this point in the history
Closes gh-14857
  • Loading branch information
Max Batischev authored and Max Batischev committed Apr 15, 2024
1 parent bf478d9 commit 905324f
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.security.authorization.method;

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.util.Assert;

/**
* {@link MethodAuthorizationDeniedHandler} implementation, that return authorization
* result, based on SpEL expression.
*
* @author Max Batischev
* @since 6.3
*/
final class ExpressionMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {

private final String expression;

private final ExpressionParser expressionParser;

ExpressionMethodAuthorizationDeniedHandler(String expression, ExpressionParser expressionParser) {
Assert.notNull(expressionParser, "expressionParser cannot be null");
Assert.notNull(expression, "expression cannot be null");
this.expressionParser = expressionParser;
this.expression = expression;
}

@Override
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
Expression expression = this.expressionParser.parseExpression(this.expression);
return expression.getValue();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,9 @@
*/
Class<? extends MethodAuthorizationDeniedHandler> handlerClass() default ThrowingMethodAuthorizationDeniedHandler.class;

/**
* @return SpEL expression to be evaluated when handling denied authorization
*/
String handlerExpression() default "";

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.springframework.expression.Expression;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
* For internal use only, as this contract is likely to change.
Expand Down Expand Up @@ -64,6 +65,10 @@ private MethodAuthorizationDeniedHandler resolveHandler(Method method, Class<?>
.withDefaults(HandleAuthorizationDenied.class);
HandleAuthorizationDenied deniedHandler = lookup.apply(method);
if (deniedHandler != null) {
if (StringUtils.hasText(deniedHandler.handlerExpression())) {
return new ExpressionMethodAuthorizationDeniedHandler(deniedHandler.handlerExpression(),
getExpressionHandler().getExpressionParser());
}
return this.handlerResolver.apply(deniedHandler.handlerClass());
}
deniedHandler = lookup.apply(targetClass(method, targetClass));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import org.springframework.aop.TargetClassAware;
import org.springframework.core.annotation.AnnotationConfigurationException;
Expand All @@ -31,6 +32,7 @@
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.core.Authentication;

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

@Test
public void handleDeniedInvocationWhenHandlerExpressionIsPresentThenReturnEvaluatedValue() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" });
AuthorizationResult authorizationResult = Mockito.mock(AuthorizationResult.class);
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
Object decision = manager.handleDeniedInvocation(methodInvocation, authorizationResult);
assertThat(decision).isEqualTo("deny");
}

@PreAuthorize("hasRole('USER')")
public static class PreAuthorizeClass extends ParentClass {

Expand All @@ -176,6 +188,7 @@ public void doSomething() {
}

@PreAuthorize("#s == 'grant'")
@HandleAuthorizationDenied(handlerExpression = "'deny'")
public String doSomethingString(String s) {
return s;
}
Expand Down

0 comments on commit 905324f

Please sign in to comment.