Merge branch '6.3.x' into 6.4.x

This commit is contained in:
Josh Cummings 2025-03-04 09:52:31 -07:00
commit 4ae0965b1c
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
2 changed files with 31 additions and 0 deletions

View File

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

View File

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