Polish Method Authorization Denied Handling

- Renamed @AuthorizationDeniedHandler to @HandleAuthorizationDenied
- Merged the post processor interface into MethodAuthorizationDeniedHandler , it now has two methods handleDeniedInvocation and handleDeniedInvocationResult
- @HandleAuthorizationDenied now handles AuthorizationDeniedException thrown from the method

Issue gh-14601
This commit is contained in:
Marcus Hert Da Coregio
2024-04-09 10:56:40 -03:00
parent 14da8f4fc8
commit 2fbbcc4bd0
31 changed files with 425 additions and 440 deletions

View File

@@ -28,10 +28,8 @@ import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.util.Assert;
@@ -42,8 +40,8 @@ import org.springframework.util.Assert;
* @author Josh Cummings
* @since 6.0
*/
public final class ObservationAuthorizationManager<T> implements AuthorizationManager<T>, MessageSourceAware,
MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
public final class ObservationAuthorizationManager<T>
implements AuthorizationManager<T>, MessageSourceAware, MethodAuthorizationDeniedHandler {
private final ObservationRegistry registry;
@@ -55,17 +53,12 @@ public final class ObservationAuthorizationManager<T> implements AuthorizationMa
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
public ObservationAuthorizationManager(ObservationRegistry registry, AuthorizationManager<T> delegate) {
this.registry = registry;
this.delegate = delegate;
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
this.postProcessor = p;
}
}
@Override
@@ -116,14 +109,14 @@ public final class ObservationAuthorizationManager<T> implements AuthorizationMa
}
@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handle(methodInvocation, authorizationResult);
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}

View File

@@ -25,10 +25,8 @@ import reactor.core.publisher.Mono;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
@@ -38,8 +36,8 @@ import org.springframework.util.Assert;
* @author Josh Cummings
* @since 6.0
*/
public final class ObservationReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T>,
MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
public final class ObservationReactiveAuthorizationManager<T>
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {
private final ObservationRegistry registry;
@@ -49,8 +47,6 @@ public final class ObservationReactiveAuthorizationManager<T> implements Reactiv
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
public ObservationReactiveAuthorizationManager(ObservationRegistry registry,
ReactiveAuthorizationManager<T> delegate) {
this.registry = registry;
@@ -58,9 +54,6 @@ public final class ObservationReactiveAuthorizationManager<T> implements Reactiv
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
this.postProcessor = p;
}
}
@Override
@@ -99,14 +92,14 @@ public final class ObservationReactiveAuthorizationManager<T> implements Reactiv
}
@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handle(methodInvocation, authorizationResult);
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}

View File

@@ -33,7 +33,6 @@ import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -57,7 +56,7 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
private final AuthorizationManager<MethodInvocationResult> authorizationManager;
private final MethodAuthorizationDeniedPostProcessor defaultPostProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
private int order;
@@ -119,7 +118,16 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object result = mi.proceed();
Object result;
try {
result = mi.proceed();
}
catch (AuthorizationDeniedException ex) {
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
return handler.handleDeniedInvocation(mi, ex);
}
return this.defaultHandler.handleDeniedInvocation(mi, ex);
}
return attemptAuthorization(mi, result);
}
@@ -174,28 +182,22 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
private Object attemptAuthorization(MethodInvocation mi, Object result) {
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
MethodInvocationResult object = new MethodInvocationResult(mi, result);
AuthorizationDecision decision;
try {
decision = this.authorizationManager.check(this::getAuthentication, object);
}
catch (AuthorizationDeniedException denied) {
return postProcess(object, denied);
}
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, object);
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, decision);
if (decision != null && !decision.isGranted()) {
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
+ this.authorizationManager + " and decision " + decision));
return postProcess(object, decision);
return handlePostInvocationDenied(object, decision);
}
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
return result;
}
private Object postProcess(MethodInvocationResult mi, AuthorizationResult decision) {
if (this.authorizationManager instanceof MethodAuthorizationDeniedPostProcessor postProcessableDecision) {
return postProcessableDecision.postProcessResult(mi, decision);
private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationDecision decision) {
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler deniedHandler) {
return deniedHandler.handleDeniedInvocationResult(mi, decision);
}
return this.defaultPostProcessor.postProcessResult(mi, decision);
return this.defaultHandler.handleDeniedInvocationResult(mi, decision);
}
private Authentication getAuthentication() {

View File

@@ -26,6 +26,7 @@ import org.aopalliance.intercept.MethodInvocation;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Signal;
import org.springframework.aop.Pointcut;
import org.springframework.core.KotlinDetector;
@@ -60,7 +61,7 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
private int order = AuthorizationInterceptorsOrder.LAST.getOrder();
private final MethodAuthorizationDeniedPostProcessor defaultPostProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
/**
* Creates an instance for the {@link PostAuthorize} annotation.
@@ -118,27 +119,39 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
+ "(for example, a Mono or Flux) or the function must be a Kotlin coroutine "
+ "in order to support Reactor Context");
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
Function<Object, Mono<?>> postAuthorize = (result) -> postAuthorize(authentication, mi, result);
Function<Signal<?>, Mono<?>> postAuthorize = (signal) -> {
if (signal.isOnComplete()) {
return Mono.empty();
}
if (!signal.hasError()) {
return postAuthorize(authentication, mi, signal.get());
}
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
return postProcess(denied, mi);
}
return Mono.error(signal.getThrowable());
};
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);
if (hasFlowReturnType) {
if (isSuspendingFunction) {
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
return Flux.from(publisher).flatMap(postAuthorize);
return Flux.from(publisher).materialize().flatMap(postAuthorize);
}
else {
Assert.state(adapter != null, () -> "The returnType " + type + " on " + method
+ " must have a org.springframework.core.ReactiveAdapter registered");
Flux<?> response = Flux.defer(() -> adapter.toPublisher(ReactiveMethodInvocationUtils.proceed(mi)))
.materialize()
.flatMap(postAuthorize);
return KotlinDelegate.asFlow(response);
}
}
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
if (isMultiValue(type, adapter)) {
Flux<?> flux = Flux.from(publisher).flatMap(postAuthorize);
Flux<?> flux = Flux.from(publisher).materialize().flatMap(postAuthorize);
return (adapter != null) ? adapter.fromPublisher(flux) : flux;
}
Mono<?> mono = Mono.from(publisher).flatMap(postAuthorize);
Mono<?> mono = Mono.from(publisher).materialize().flatMap(postAuthorize);
return (adapter != null) ? adapter.fromPublisher(mono) : mono;
}
@@ -153,17 +166,7 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
MethodInvocationResult invocationResult = new MethodInvocationResult(mi, result);
return this.authorizationManager.check(authentication, invocationResult)
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
.materialize()
.flatMap((signal) -> {
if (!signal.hasError()) {
AuthorizationDecision decision = signal.get();
return postProcess(decision, invocationResult);
}
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
return postProcess(denied, invocationResult);
}
return Mono.error(signal.getThrowable());
});
.flatMap((decision) -> postProcess(decision, invocationResult));
}
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocationResult methodInvocationResult) {
@@ -171,10 +174,24 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
return Mono.just(methodInvocationResult.getResult());
}
return Mono.fromSupplier(() -> {
if (this.authorizationManager instanceof MethodAuthorizationDeniedPostProcessor postProcessableDecision) {
return postProcessableDecision.postProcessResult(methodInvocationResult, decision);
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
return handler.handleDeniedInvocationResult(methodInvocationResult, decision);
}
return this.defaultPostProcessor.postProcessResult(methodInvocationResult, decision);
return this.defaultHandler.handleDeniedInvocationResult(methodInvocationResult, decision);
}).flatMap((processedResult) -> {
if (Mono.class.isAssignableFrom(processedResult.getClass())) {
return (Mono<?>) processedResult;
}
return Mono.justOrEmpty(processedResult);
});
}
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocation methodInvocation) {
return Mono.fromSupplier(() -> {
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
return handler.handleDeniedInvocation(methodInvocation, decision);
}
return this.defaultHandler.handleDeniedInvocation(methodInvocation, decision);
}).flatMap((processedResult) -> {
if (Mono.class.isAssignableFrom(processedResult.getClass())) {
return (Mono<?>) processedResult;

View File

@@ -261,14 +261,33 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author
return handle(mi, decision);
}
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
return mi.proceed();
return proceed(mi);
}
private Object proceed(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (AuthorizationDeniedException ex) {
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
return handler.handleDeniedInvocation(mi, ex);
}
return this.defaultHandler.handleDeniedInvocation(mi, ex);
}
}
private Object handle(MethodInvocation mi, AuthorizationDeniedException denied) {
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
return handler.handleDeniedInvocation(mi, denied);
}
return this.defaultHandler.handleDeniedInvocation(mi, denied);
}
private Object handle(MethodInvocation mi, AuthorizationResult decision) {
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
return handler.handle(mi, decision);
return handler.handleDeniedInvocation(mi, decision);
}
return this.defaultHandler.handle(mi, decision);
return this.defaultHandler.handleDeniedInvocation(mi, decision);
}
private Authentication getAuthentication() {

View File

@@ -142,19 +142,12 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
return this.authorizationManager.check(authentication, mi)
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
.materialize()
.flatMapMany((signal) -> {
if (!signal.hasError()) {
AuthorizationDecision decision = signal.get();
if (decision.isGranted()) {
return mapping;
}
return postProcess(decision, mi);
.flatMapMany((decision) -> {
if (decision.isGranted()) {
return mapping.onErrorResume(AuthorizationDeniedException.class,
(deniedEx) -> postProcess(deniedEx, mi));
}
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
return postProcess(denied, mi);
}
return Mono.error(signal.getThrowable());
return postProcess(decision, mi);
});
}
@@ -162,28 +155,21 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
return this.authorizationManager.check(authentication, mi)
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
.materialize()
.flatMap((signal) -> {
if (!signal.hasError()) {
AuthorizationDecision decision = signal.get();
if (decision.isGranted()) {
return mapping;
}
return postProcess(decision, mi);
.flatMap((decision) -> {
if (decision.isGranted()) {
return mapping.onErrorResume(AuthorizationDeniedException.class,
(deniedEx) -> postProcess(deniedEx, mi));
}
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
return postProcess(denied, mi);
}
return Mono.error(signal.getThrowable());
return postProcess(decision, mi);
});
}
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocation mi) {
return Mono.fromSupplier(() -> {
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
return handler.handle(mi, decision);
return handler.handleDeniedInvocation(mi, decision);
}
return this.defaultHandler.handle(mi, decision);
return this.defaultHandler.handleDeniedInvocation(mi, decision);
}).flatMap((result) -> {
if (Mono.class.isAssignableFrom(result.getClass())) {
return (Mono<?>) result;

View File

@@ -25,32 +25,26 @@ import java.lang.annotation.Target;
/**
* Annotation for specifying handling behavior when an authorization denied happens in
* method security
* method security or an
* {@link org.springframework.security.authorization.AuthorizationDeniedException} is
* thrown during method invocation
*
* @author Marcus da Coregio
* @since 6.3
* @see org.springframework.security.access.prepost.PreAuthorize
* @see org.springframework.security.access.prepost.PostAuthorize
* @see AuthorizationManagerAfterMethodInterceptor
* @see AuthorizationManagerBeforeMethodInterceptor
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface AuthorizationDeniedHandler {
public @interface HandleAuthorizationDenied {
/**
* The {@link MethodAuthorizationDeniedHandler} used to handle denied authorizations
* from {@link org.springframework.security.access.prepost.PreAuthorize}
* The {@link MethodAuthorizationDeniedHandler} used to handle denied authorization
* results
* @return
*/
Class<? extends MethodAuthorizationDeniedHandler> handlerClass() default ThrowingMethodAuthorizationDeniedHandler.class;
/**
* The {@link MethodAuthorizationDeniedPostProcessor} used to post process denied
* authorizations from
* {@link org.springframework.security.access.prepost.PostAuthorize}
* @return
*/
Class<? extends MethodAuthorizationDeniedPostProcessor> postProcessorClass() default ThrowingMethodAuthorizationDeniedPostProcessor.class;
}

View File

@@ -27,13 +27,14 @@ import org.springframework.security.authorization.AuthorizationResult;
* @author Marcus da Coregio
* @since 6.3
* @see org.springframework.security.access.prepost.PreAuthorize
* @see org.springframework.security.access.prepost.PostAuthorize
*/
public interface MethodAuthorizationDeniedHandler {
/**
* Handle denied method invocations, implementations might either throw an
* {@link org.springframework.security.access.AccessDeniedException} or a replacement
* result instead of invoking the method, e.g. a masked value.
* {@link org.springframework.security.authorization.AuthorizationDeniedException} or
* a replacement result instead of invoking the method, e.g. a masked value.
* @param methodInvocation the {@link MethodInvocation} related to the authorization
* denied
* @param authorizationResult the authorization denied result
@@ -41,6 +42,24 @@ public interface MethodAuthorizationDeniedHandler {
* {@link reactor.core.publisher.Mono} for reactive applications
*/
@Nullable
Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult);
Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult);
/**
* Handle denied method invocations, implementations might either throw an
* {@link org.springframework.security.authorization.AuthorizationDeniedException} or
* a replacement result instead of invoking the method, e.g. a masked value. By
* default, this method invokes
* {@link #handleDeniedInvocation(MethodInvocation, AuthorizationResult)}.
* @param methodInvocationResult the object containing the {@link MethodInvocation}
* and the result produced
* @param authorizationResult the authorization denied result
* @return a replacement result for the denied method invocation, or null, or a
* {@link reactor.core.publisher.Mono} for reactive applications
*/
@Nullable
default Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return handleDeniedInvocation(methodInvocationResult.getMethodInvocation(), authorizationResult);
}
}

View File

@@ -1,46 +0,0 @@
/*
* 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.springframework.lang.Nullable;
import org.springframework.security.authorization.AuthorizationResult;
/**
* An interface to define a strategy to handle denied method invocation results
*
* @author Marcus da Coregio
* @since 6.3
* @see org.springframework.security.access.prepost.PostAuthorize
*/
public interface MethodAuthorizationDeniedPostProcessor {
/**
* Post-process the denied result produced by a method invocation, implementations
* might either throw an
* {@link org.springframework.security.access.AccessDeniedException} or return a
* replacement result instead of the denied result, e.g. a masked value.
* @param methodInvocationResult the object containing the method invocation and the
* result produced
* @param authorizationResult the {@link AuthorizationResult} containing the
* authorization denied details
* @return a replacement result for the denied result, or null, or a
* {@link reactor.core.publisher.Mono} for reactive applications
*/
@Nullable
Object postProcessResult(MethodInvocationResult methodInvocationResult, AuthorizationResult authorizationResult);
}

View File

@@ -38,7 +38,7 @@ import org.springframework.security.core.Authentication;
* @since 5.6
*/
public final class PostAuthorizeAuthorizationManager
implements AuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedPostProcessor {
implements AuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {
private PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
@@ -96,11 +96,19 @@ public final class PostAuthorizeAuthorizationManager
}
@Override
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
return postAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
return postAuthorizeAttribute.getPostProcessor().postProcessResult(methodInvocationResult, authorizationResult);
return postAuthorizeAttribute.getHandler()
.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}

View File

@@ -27,16 +27,16 @@ import org.springframework.util.Assert;
*/
class PostAuthorizeExpressionAttribute extends ExpressionAttribute {
private final MethodAuthorizationDeniedPostProcessor postProcessor;
private final MethodAuthorizationDeniedHandler handler;
PostAuthorizeExpressionAttribute(Expression expression, MethodAuthorizationDeniedPostProcessor postProcessor) {
PostAuthorizeExpressionAttribute(Expression expression, MethodAuthorizationDeniedHandler handler) {
super(expression);
Assert.notNull(postProcessor, "postProcessor cannot be null");
this.postProcessor = postProcessor;
Assert.notNull(handler, "handler cannot be null");
this.handler = handler;
}
MethodAuthorizationDeniedPostProcessor getPostProcessor() {
return this.postProcessor;
MethodAuthorizationDeniedHandler getHandler() {
return this.handler;
}
}

View File

@@ -38,12 +38,12 @@ import org.springframework.util.Assert;
*/
final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
private final MethodAuthorizationDeniedPostProcessor defaultPostProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
private Function<Class<? extends MethodAuthorizationDeniedPostProcessor>, MethodAuthorizationDeniedPostProcessor> postProcessorResolver;
private Function<Class<? extends MethodAuthorizationDeniedHandler>, MethodAuthorizationDeniedHandler> handlerResolver;
PostAuthorizeExpressionAttributeRegistry() {
this.postProcessorResolver = (clazz) -> this.defaultPostProcessor;
this.handlerResolver = (clazz) -> this.defaultHandler;
}
@NonNull
@@ -55,22 +55,22 @@ final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionA
return ExpressionAttribute.NULL_ATTRIBUTE;
}
Expression expression = getExpressionHandler().getExpressionParser().parseExpression(postAuthorize.value());
MethodAuthorizationDeniedPostProcessor postProcessor = resolvePostProcessor(method, targetClass);
return new PostAuthorizeExpressionAttribute(expression, postProcessor);
MethodAuthorizationDeniedHandler deniedHandler = resolveHandler(method, targetClass);
return new PostAuthorizeExpressionAttribute(expression, deniedHandler);
}
private MethodAuthorizationDeniedPostProcessor resolvePostProcessor(Method method, Class<?> targetClass) {
Function<AnnotatedElement, AuthorizationDeniedHandler> lookup = AuthorizationAnnotationUtils
.withDefaults(AuthorizationDeniedHandler.class);
AuthorizationDeniedHandler deniedHandler = lookup.apply(method);
private MethodAuthorizationDeniedHandler resolveHandler(Method method, Class<?> targetClass) {
Function<AnnotatedElement, HandleAuthorizationDenied> lookup = AuthorizationAnnotationUtils
.withDefaults(HandleAuthorizationDenied.class);
HandleAuthorizationDenied deniedHandler = lookup.apply(method);
if (deniedHandler != null) {
return this.postProcessorResolver.apply(deniedHandler.postProcessorClass());
return this.handlerResolver.apply(deniedHandler.handlerClass());
}
deniedHandler = lookup.apply(targetClass(method, targetClass));
if (deniedHandler != null) {
return this.postProcessorResolver.apply(deniedHandler.postProcessorClass());
return this.handlerResolver.apply(deniedHandler.handlerClass());
}
return this.defaultPostProcessor;
return this.defaultHandler;
}
private PostAuthorize findPostAuthorizeAnnotation(Method method, Class<?> targetClass) {
@@ -86,23 +86,23 @@ final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionA
*/
void setApplicationContext(ApplicationContext context) {
Assert.notNull(context, "context cannot be null");
this.postProcessorResolver = (postProcessorClass) -> resolvePostProcessor(context, postProcessorClass);
this.handlerResolver = (clazz) -> resolveHandler(context, clazz);
}
private MethodAuthorizationDeniedPostProcessor resolvePostProcessor(ApplicationContext context,
Class<? extends MethodAuthorizationDeniedPostProcessor> postProcessorClass) {
if (postProcessorClass == this.defaultPostProcessor.getClass()) {
return this.defaultPostProcessor;
private MethodAuthorizationDeniedHandler resolveHandler(ApplicationContext context,
Class<? extends MethodAuthorizationDeniedHandler> handlerClass) {
if (handlerClass == this.defaultHandler.getClass()) {
return this.defaultHandler;
}
String[] beanNames = context.getBeanNamesForType(postProcessorClass);
String[] beanNames = context.getBeanNamesForType(handlerClass);
if (beanNames.length == 0) {
throw new IllegalStateException("Could not find a bean of type " + postProcessorClass.getName());
throw new IllegalStateException("Could not find a bean of type " + handlerClass.getName());
}
if (beanNames.length > 1) {
throw new IllegalStateException("Expected to find a single bean of type " + postProcessorClass.getName()
throw new IllegalStateException("Expected to find a single bean of type " + handlerClass.getName()
+ " but found " + Arrays.toString(beanNames));
}
return context.getBean(beanNames[0], postProcessorClass);
return context.getBean(beanNames[0], handlerClass);
}
}

View File

@@ -38,7 +38,7 @@ import org.springframework.util.Assert;
* @since 5.8
*/
public final class PostAuthorizeReactiveAuthorizationManager
implements ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedPostProcessor {
implements ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
@@ -95,11 +95,19 @@ public final class PostAuthorizeReactiveAuthorizationManager
}
@Override
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
return postAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
return postAuthorizeAttribute.getPostProcessor().postProcessResult(methodInvocationResult, authorizationResult);
return postAuthorizeAttribute.getHandler()
.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}

View File

@@ -86,10 +86,10 @@ public final class PreAuthorizeAuthorizationManager
}
@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
PreAuthorizeExpressionAttribute postAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;
return postAuthorizeAttribute.getHandler().handle(methodInvocation, authorizationResult);
PreAuthorizeExpressionAttribute preAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;
return preAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
}
}

View File

@@ -60,9 +60,9 @@ final class PreAuthorizeExpressionAttributeRegistry extends AbstractExpressionAt
}
private MethodAuthorizationDeniedHandler resolveHandler(Method method, Class<?> targetClass) {
Function<AnnotatedElement, AuthorizationDeniedHandler> lookup = AuthorizationAnnotationUtils
.withDefaults(AuthorizationDeniedHandler.class);
AuthorizationDeniedHandler deniedHandler = lookup.apply(method);
Function<AnnotatedElement, HandleAuthorizationDenied> lookup = AuthorizationAnnotationUtils
.withDefaults(HandleAuthorizationDenied.class);
HandleAuthorizationDenied deniedHandler = lookup.apply(method);
if (deniedHandler != null) {
return this.handlerResolver.apply(deniedHandler.handlerClass());
}

View File

@@ -90,10 +90,10 @@ public final class PreAuthorizeReactiveAuthorizationManager
}
@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
PreAuthorizeExpressionAttribute preAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;
return preAuthorizeAttribute.getHandler().handle(methodInvocation, authorizationResult);
return preAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
}
}

View File

@@ -31,11 +31,20 @@ import org.springframework.security.authorization.AuthorizationResult;
public final class ThrowingMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {
@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
if (result instanceof AuthorizationDeniedException denied) {
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
if (authorizationResult instanceof AuthorizationDeniedException denied) {
throw denied;
}
throw new AuthorizationDeniedException("Access Denied", result);
throw new AuthorizationDeniedException("Access Denied", authorizationResult);
}
@Override
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
if (authorizationResult instanceof AuthorizationDeniedException denied) {
throw denied;
}
throw new AuthorizationDeniedException("Access Denied", authorizationResult);
}
}

View File

@@ -1,39 +0,0 @@
/*
* 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.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.authorization.AuthorizationResult;
/**
* An implementation of {@link MethodAuthorizationDeniedPostProcessor} that throws
* {@link AuthorizationDeniedException}
*
* @author Marcus da Coregio
* @since 6.3
*/
public final class ThrowingMethodAuthorizationDeniedPostProcessor implements MethodAuthorizationDeniedPostProcessor {
@Override
public Object postProcessResult(MethodInvocationResult methodInvocationResult, AuthorizationResult result) {
if (result instanceof AuthorizationDeniedException denied) {
throw denied;
}
throw new AuthorizationDeniedException("Access Denied", result);
}
}

View File

@@ -127,7 +127,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
.willAnswer(this::masking);
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
@@ -147,7 +147,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
.willAnswer((invocation) -> {
MethodInvocationResult argument = invocation.getArgument(0);
if (!"john".equals(argument.getResult())) {
@@ -173,7 +173,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
.willAnswer(this::masking);
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
@@ -192,7 +192,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
.willAnswer(this::monoMasking);
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
@@ -211,7 +211,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
.willReturn(null);
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
@@ -266,7 +266,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
}
interface HandlingReactiveAuthorizationManager
extends ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedPostProcessor {
extends ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {
}

View File

@@ -127,7 +127,8 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
given(mockReactiveAuthorizationManager.handle(any(), any(AuthorizationResult.class))).willReturn("***");
given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))
.willReturn("***");
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
Pointcut.TRUE, mockReactiveAuthorizationManager);
Object result = interceptor.invoke(mockMethodInvocation);
@@ -145,7 +146,7 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
given(mockReactiveAuthorizationManager.handle(any(), any(AuthorizationResult.class)))
given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))
.willReturn(Mono.just("***"));
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
Pointcut.TRUE, mockReactiveAuthorizationManager);
@@ -164,7 +165,7 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
HandlingReactiveAuthorizationManager.class);
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
given(mockReactiveAuthorizationManager.handle(any(), any(AuthorizationResult.class)))
given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))
.willReturn(Mono.just("***"));
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
Pointcut.TRUE, mockReactiveAuthorizationManager);