1
0
mirror of synced 2026-05-22 13:23:17 +00:00

SpEL Expressions Support Returning AuthorizationManager

Closes gh-17936
This commit is contained in:
Josh Cummings
2025-09-19 10:02:06 -06:00
parent 25e413127c
commit 765bdf1ed0
14 changed files with 84 additions and 4 deletions
@@ -16,14 +16,19 @@
package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
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.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.ExpressionAuthorizationDecision;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
final class ExpressionUtils {
@@ -31,8 +36,18 @@ final class ExpressionUtils {
}
static @Nullable AuthorizationResult evaluate(Expression expr, EvaluationContext ctx) {
return evaluate(expr, ctx, () -> null, null);
}
static <T> @Nullable AuthorizationResult evaluate(Expression expr, EvaluationContext ctx,
Supplier<? extends @Nullable Authentication> authentication, @Nullable T context) {
try {
Object result = expr.getValue(ctx);
if (result instanceof AuthorizationManager<?> manager) {
Assert.notNull(authentication, "authentication supplier cannot be null");
Assert.notNull(context, "context cannot be null");
return ((AuthorizationManager<T>) manager).authorize(authentication, context);
}
if (result instanceof AuthorizationResult decision) {
return decision;
}
@@ -95,7 +95,7 @@ public final class PostAuthorizeAuthorizationManager
MethodSecurityExpressionHandler expressionHandler = this.registry.getExpressionHandler();
EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication, mi.getMethodInvocation());
expressionHandler.setReturnObject(mi.getResult(), ctx);
return ExpressionUtils.evaluate(attribute.getExpression(), ctx);
return ExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, mi);
}
@Override
@@ -91,7 +91,7 @@ public final class PostAuthorizeReactiveAuthorizationManager
return authentication
.map((auth) -> expressionHandler.createEvaluationContext(auth, mi))
.doOnNext((ctx) -> expressionHandler.setReturnObject(result.getResult(), ctx))
.flatMap((ctx) -> ReactiveExpressionUtils.evaluate(attribute.getExpression(), ctx))
.flatMap((ctx) -> ReactiveExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, result))
.cast(AuthorizationResult.class);
// @formatter:on
}
@@ -85,7 +85,7 @@ public final class PreAuthorizeAuthorizationManager
return null;
}
EvaluationContext ctx = this.registry.getExpressionHandler().createEvaluationContext(authentication, mi);
return ExpressionUtils.evaluate(attribute.getExpression(), ctx);
return ExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, mi);
}
@Override
@@ -85,7 +85,7 @@ public final class PreAuthorizeReactiveAuthorizationManager
// @formatter:off
return authentication
.map((auth) -> this.registry.getExpressionHandler().createEvaluationContext(auth, mi))
.flatMap((ctx) -> ReactiveExpressionUtils.evaluate(attribute.getExpression(), ctx))
.flatMap((ctx) -> ReactiveExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, mi))
.cast(AuthorizationResult.class);
// @formatter:on
}
@@ -24,6 +24,9 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.ExpressionAuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* For internal use only, as this contract is likely to change.
@@ -34,6 +37,11 @@ import org.springframework.security.authorization.ExpressionAuthorizationDecisio
final class ReactiveExpressionUtils {
static Mono<AuthorizationResult> evaluate(Expression expr, EvaluationContext ctx) {
return evaluate(expr, ctx, Mono.empty(), null);
}
static <T> Mono<AuthorizationResult> evaluate(Expression expr, EvaluationContext ctx,
Mono<Authentication> authentication, @Nullable T context) {
return Mono.defer(() -> {
Object value;
try {
@@ -43,6 +51,10 @@ final class ReactiveExpressionUtils {
return Mono.error(() -> new IllegalArgumentException(
"Failed to evaluate expression '" + expr.getExpressionString() + "'", ex));
}
if (value instanceof ReactiveAuthorizationManager<?> manager) {
Assert.notNull(context, "context cannot be null");
return ((ReactiveAuthorizationManager<T>) manager).authorize(authentication, context);
}
if (value instanceof Mono<?> mono) {
return mono.flatMap((data) -> adapt(expr, data));
}