Browse Source

SpEL Propagates Authorization Exceptions

Closes gh-16697
Josh Cummings 5 months ago
parent
commit
46cd94b5f4

+ 16 - 0
core/src/main/java/org/springframework/security/authorization/method/ExpressionUtils.java

@@ -19,6 +19,7 @@ package org.springframework.security.authorization.method;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.EvaluationException;
 import org.springframework.expression.Expression;
+import org.springframework.security.authorization.AuthorizationDeniedException;
 import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.authorization.ExpressionAuthorizationDecision;
 
@@ -43,9 +44,24 @@ final class ExpressionUtils {
 					"SpEL expression must return either a Boolean or an AuthorizationDecision");
 		}
 		catch (EvaluationException ex) {
+			AuthorizationDeniedException denied = findAuthorizationException(ex);
+			if (denied != null) {
+				throw denied;
+			}
 			throw new IllegalArgumentException("Failed to evaluate expression '" + expr.getExpressionString() + "'",
 					ex);
 		}
 	}
 
+	static AuthorizationDeniedException findAuthorizationException(EvaluationException ex) {
+		Throwable cause = ex.getCause();
+		while (cause != null) {
+			if (cause instanceof AuthorizationDeniedException denied) {
+				return denied;
+			}
+			cause = cause.getCause();
+		}
+		return null;
+	}
+
 }

+ 15 - 0
core/src/test/java/org/springframework/security/authorization/method/ExpressionUtilsTests.java

@@ -22,9 +22,11 @@ import org.springframework.expression.Expression;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationDeniedException;
 import org.springframework.security.authorization.ExpressionAuthorizationDecision;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 
 public class ExpressionUtilsTests {
 
@@ -48,10 +50,23 @@ public class ExpressionUtilsTests {
 		assertThat(ExpressionUtils.evaluate(expression, context)).isInstanceOf(ExpressionAuthorizationDecision.class);
 	}
 
+	@Test
+	public void evaluateWhenExpressionThrowsAuthorizationDeniedExceptionThenPropagates() {
+		SpelExpressionParser parser = new SpelExpressionParser();
+		Expression expression = parser.parseExpression("#root.throwException()");
+		StandardEvaluationContext context = new StandardEvaluationContext(this);
+		assertThatExceptionOfType(AuthorizationDeniedException.class)
+			.isThrownBy(() -> ExpressionUtils.evaluate(expression, context));
+	}
+
 	public AuthorizationDecision returnDecision() {
 		return new AuthorizationDecisionDetails(false, this.details);
 	}
 
+	public Object throwException() {
+		throw new AuthorizationDeniedException("denied");
+	}
+
 	public boolean returnResult() {
 		return false;
 	}