Skip to content

Commit 46cd94b

Browse files
committed
SpEL Propagates Authorization Exceptions
Closes gh-16697
1 parent acd2de4 commit 46cd94b

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

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

+16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.springframework.expression.EvaluationContext;
2020
import org.springframework.expression.EvaluationException;
2121
import org.springframework.expression.Expression;
22+
import org.springframework.security.authorization.AuthorizationDeniedException;
2223
import org.springframework.security.authorization.AuthorizationResult;
2324
import org.springframework.security.authorization.ExpressionAuthorizationDecision;
2425

@@ -43,9 +44,24 @@ static AuthorizationResult evaluate(Expression expr, EvaluationContext ctx) {
4344
"SpEL expression must return either a Boolean or an AuthorizationDecision");
4445
}
4546
catch (EvaluationException ex) {
47+
AuthorizationDeniedException denied = findAuthorizationException(ex);
48+
if (denied != null) {
49+
throw denied;
50+
}
4651
throw new IllegalArgumentException("Failed to evaluate expression '" + expr.getExpressionString() + "'",
4752
ex);
4853
}
4954
}
5055

56+
static AuthorizationDeniedException findAuthorizationException(EvaluationException ex) {
57+
Throwable cause = ex.getCause();
58+
while (cause != null) {
59+
if (cause instanceof AuthorizationDeniedException denied) {
60+
return denied;
61+
}
62+
cause = cause.getCause();
63+
}
64+
return null;
65+
}
66+
5167
}

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

+15
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import org.springframework.expression.spel.standard.SpelExpressionParser;
2323
import org.springframework.expression.spel.support.StandardEvaluationContext;
2424
import org.springframework.security.authorization.AuthorizationDecision;
25+
import org.springframework.security.authorization.AuthorizationDeniedException;
2526
import org.springframework.security.authorization.ExpressionAuthorizationDecision;
2627

2728
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2830

2931
public class ExpressionUtilsTests {
3032

@@ -48,10 +50,23 @@ public void evaluateWhenBooleanThenReturnsExpressionAuthorizationDecision() {
4850
assertThat(ExpressionUtils.evaluate(expression, context)).isInstanceOf(ExpressionAuthorizationDecision.class);
4951
}
5052

53+
@Test
54+
public void evaluateWhenExpressionThrowsAuthorizationDeniedExceptionThenPropagates() {
55+
SpelExpressionParser parser = new SpelExpressionParser();
56+
Expression expression = parser.parseExpression("#root.throwException()");
57+
StandardEvaluationContext context = new StandardEvaluationContext(this);
58+
assertThatExceptionOfType(AuthorizationDeniedException.class)
59+
.isThrownBy(() -> ExpressionUtils.evaluate(expression, context));
60+
}
61+
5162
public AuthorizationDecision returnDecision() {
5263
return new AuthorizationDecisionDetails(false, this.details);
5364
}
5465

66+
public Object throwException() {
67+
throw new AuthorizationDeniedException("denied");
68+
}
69+
5570
public boolean returnResult() {
5671
return false;
5772
}

0 commit comments

Comments
 (0)