Skip to content

Commit

Permalink
Polish
Browse files Browse the repository at this point in the history
- Resolve the post processor in the Expression Attribute Registry
- Rename the default post processors
- Create PostProcessableAuthorizationDecision#postProcess
- Rename AuthorizationException to AuthorizationDeniedException
  • Loading branch information
marcusdacoregio committed Mar 26, 2024
1 parent e0eff34 commit 526d87d
Show file tree
Hide file tree
Showing 19 changed files with 143 additions and 198 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* 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.
Expand Down Expand Up @@ -57,7 +57,6 @@ public String[] selectImports(@NonNull AnnotationMetadata importMetadata) {
imports.add(Jsr250MethodSecurityConfiguration.class.getName());
}
imports.add(AuthorizationProxyConfiguration.class.getName());
imports.add(AuthorizationPostProcessorConfiguration.class.getName());
return imports.toArray(new String[0]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<ObservationRegistry> registryProvider, ObjectProvider<RoleHierarchy> roleHierarchyProvider,
PrePostMethodSecurityConfiguration configuration, ApplicationContext context) {
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
manager.setApplicationContext(context);
AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor
.preAuthorize(manager(manager, registryProvider));
preAuthorize.setOrder(preAuthorize.getOrder() + configuration.interceptorOrderOffset);
preAuthorize.setApplicationContext(context);
return new DeferringMethodInterceptor<>(preAuthorize, (f) -> {
methodSecurityDefaultsProvider.ifAvailable(manager::setTemplateDefaults);
manager.setExpressionHandler(expressionHandlerProvider
Expand All @@ -122,6 +122,7 @@ static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<ObservationRegistry> registryProvider, ObjectProvider<RoleHierarchy> roleHierarchyProvider,
PrePostMethodSecurityConfiguration configuration, ApplicationContext context) {
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
manager.setApplicationContext(context);
AuthorizationManagerAfterMethodInterceptor postAuthorize = AuthorizationManagerAfterMethodInterceptor
.postAuthorize(manager(manager, registryProvider));
postAuthorize.setOrder(postAuthorize.getOrder() + configuration.interceptorOrderOffset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import java.lang.annotation.Target;

import org.springframework.security.authorization.method.AuthorizationDeniedPostProcessor;
import org.springframework.security.authorization.method.DefaultPostInvocationAuthorizationDeniedPostProcessor;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.PostInvocationThrowingAuthorizationDeniedPostProcessor;

/**
* Annotation for specifying a method access-control expression which will be evaluated
Expand All @@ -46,6 +46,6 @@
*/
String value();

Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> postProcessorClass() default DefaultPostInvocationAuthorizationDeniedPostProcessor.class;
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> postProcessorClass() default PostInvocationThrowingAuthorizationDeniedPostProcessor.class;

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.security.authorization.method.AuthorizationDeniedPostProcessor;
import org.springframework.security.authorization.method.DefaultPreInvocationAuthorizationDeniedPostProcessor;
import org.springframework.security.authorization.method.PreInvocationThrowingAuthorizationDeniedPostProcessor;

/**
* Annotation for specifying a method access-control expression which will be evaluated to
Expand All @@ -47,6 +47,6 @@
*/
String value();

Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> postProcessorClass() default DefaultPreInvocationAuthorizationDeniedPostProcessor.class;
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> postProcessorClass() default PreInvocationThrowingAuthorizationDeniedPostProcessor.class;

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import org.springframework.security.access.AccessDeniedException;
import org.springframework.util.Assert;

public class AuthorizationException extends AccessDeniedException {
public class AuthorizationDeniedException extends AccessDeniedException {

private final AuthorizationResult result;

public AuthorizationException(String msg, AuthorizationResult result) {
public AuthorizationDeniedException(String msg, AuthorizationResult result) {
super(msg);
Assert.notNull(result, "decision cannot be null");
Assert.state(!result.isGranted(), "Granted decisions are not supported");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.NonNull;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
Expand All @@ -45,6 +46,8 @@ abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute

private PrePostTemplateDefaults defaults;

private ApplicationContext applicationContext;

/**
* Returns an {@link ExpressionAttribute} for the {@link MethodInvocation}.
* @param mi the {@link MethodInvocation} to use
Expand Down Expand Up @@ -90,6 +93,14 @@ void setTemplateDefaults(PrePostTemplateDefaults defaults) {
this.defaults = defaults;
}

void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

ApplicationContext getApplicationContext() {
return this.applicationContext;
}

/**
* Subclasses should implement this method to provide the non-null
* {@link ExpressionAttribute} for the method and the target class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.springframework.security.authorization.method;

import java.util.Arrays;
import java.util.function.Supplier;

import org.aopalliance.aop.Advice;
Expand All @@ -27,7 +26,6 @@

import org.springframework.aop.Pointcut;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.log.LogMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PostAuthorize;
Expand Down Expand Up @@ -58,7 +56,7 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori

private final AuthorizationManager<MethodInvocationResult> authorizationManager;

private final AuthorizationDeniedPostProcessor<MethodInvocationResult> defaultPostProcessor = new DefaultPostInvocationAuthorizationDeniedPostProcessor();
private final AuthorizationDeniedPostProcessor<MethodInvocationResult> postProcessor = new PostInvocationThrowingAuthorizationDeniedPostProcessor();

private int order;

Expand Down Expand Up @@ -118,6 +116,8 @@ public static AuthorizationManagerAfterMethodInterceptor postAuthorize(
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the {@link AuthorizationManager}.
* @param mi the {@link MethodInvocation} to check
* @return the result of the method invocation or a post-processed result if
* authorization denied
* @throws AccessDeniedException if access is not granted
*/
@Override
Expand Down Expand Up @@ -194,41 +194,10 @@ private Object attemptAuthorization(MethodInvocation mi, Object result) {
}

private Object postProcess(MethodInvocationResult mi, AuthorizationDecision decision) {
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> postProcessorClass = getPostProcessorClass(
decision);
AuthorizationDeniedPostProcessor<MethodInvocationResult> postProcessor = resolvePostProcessor(
postProcessorClass);
return postProcessor.postProcessResult(mi, decision);
}

@SuppressWarnings({ "unchecked", "DataFlowIssue" })
private Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> getPostProcessorClass(
AuthorizationDecision decision) {
if (!(decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision)) {
return null;
}
if (!MethodInvocationResult.class
.isAssignableFrom(ResolvableType.forInstance(postProcessableDecision).resolveGeneric(0))) {
return null;
}
return (Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>>) postProcessableDecision
.getPostProcessorClass();
}

private AuthorizationDeniedPostProcessor<MethodInvocationResult> resolvePostProcessor(
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> beanClass) {
if (beanClass == null || this.context == null) {
return this.defaultPostProcessor;
}
String[] beanNames = this.context.getBeanNamesForType(beanClass);
if (beanNames.length == 0) {
throw new IllegalStateException("Could not find a bean of type " + beanClass.getSimpleName());
}
if (beanNames.length > 1) {
throw new IllegalStateException("Expected to find a single bean of type " + beanClass.getSimpleName()
+ " but found " + Arrays.toString(beanNames));
if (decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision) {
return postProcessableDecision.postProcess();
}
return this.context.getBean(beanNames[0], beanClass);
return this.postProcessor.postProcessResult(mi, decision);
}

private Authentication getAuthentication() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.springframework.security.authorization.method;

import java.util.Arrays;
import java.util.function.Supplier;

import jakarta.annotation.security.DenyAll;
Expand All @@ -29,8 +28,6 @@
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.Pointcut;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.log.LogMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.annotation.Secured;
Expand Down Expand Up @@ -62,14 +59,12 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author

private final AuthorizationManager<MethodInvocation> authorizationManager;

private final AuthorizationDeniedPostProcessor<MethodInvocation> defaultPostProcessor = new DefaultPreInvocationAuthorizationDeniedPostProcessor();
private final AuthorizationDeniedPostProcessor<MethodInvocation> postProcessor = new PreInvocationThrowingAuthorizationDeniedPostProcessor();

private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();

private AuthorizationEventPublisher eventPublisher = AuthorizationManagerBeforeMethodInterceptor::noPublish;

private ApplicationContext context;

/**
* Creates an instance.
* @param pointcut the {@link Pointcut} to use
Expand Down Expand Up @@ -193,6 +188,8 @@ public static AuthorizationManagerBeforeMethodInterceptor jsr250(
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the configured {@link AuthorizationManager}.
* @param mi the {@link MethodInvocation} to check
* @return the result of the method invocation or a post-processed result if
* authorization denied
* @throws AccessDeniedException if access is not granted
*/
@Override
Expand Down Expand Up @@ -248,11 +245,6 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur
this.securityContextHolderStrategy = () -> securityContextHolderStrategy;
}

public void setApplicationContext(ApplicationContext applicationContext) {
Assert.notNull(applicationContext, "applicationContext cannot be null");
this.context = applicationContext;
}

private Object attemptAuthorization(MethodInvocation mi) throws Throwable {
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, mi);
Expand All @@ -267,40 +259,10 @@ private Object attemptAuthorization(MethodInvocation mi) throws Throwable {
}

private Object postProcess(MethodInvocation mi, AuthorizationDecision decision) {
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> postProcessorClass = getPostProcessorClass(
decision);
AuthorizationDeniedPostProcessor<MethodInvocation> postProcessor = resolvePostProcessor(postProcessorClass);
return postProcessor.postProcessResult(mi, decision);
}

@SuppressWarnings({ "unchecked", "DataFlowIssue" })
private Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> getPostProcessorClass(
AuthorizationDecision decision) {
if (!(decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision)) {
return null;
}
Class<?> genericType = ResolvableType.forInstance(postProcessableDecision).resolveGeneric(0);
if (!MethodInvocation.class.isAssignableFrom(genericType)) {
return null;
}
return (Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>>) postProcessableDecision
.getPostProcessorClass();
}

private AuthorizationDeniedPostProcessor<MethodInvocation> resolvePostProcessor(
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> beanClass) {
if (beanClass == null || this.context == null) {
return this.defaultPostProcessor;
}
String[] beanNames = this.context.getBeanNamesForType(beanClass);
if (beanNames.length == 0) {
throw new IllegalStateException("Could not find a bean of type " + beanClass.getSimpleName());
}
if (beanNames.length > 1) {
throw new IllegalStateException("Expected to find a single bean of type " + beanClass.getSimpleName()
+ " but found " + Arrays.toString(beanNames));
if (decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision) {
return postProcessableDecision.postProcess();
}
return this.context.getBean(beanNames[0], beanClass);
return this.postProcessor.postProcessResult(mi, decision);
}

private Authentication getAuthentication() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.context.ApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
Expand Down Expand Up @@ -60,6 +61,10 @@ public void setTemplateDefaults(PrePostTemplateDefaults defaults) {
this.registry.setTemplateDefaults(defaults);
}

public void setApplicationContext(ApplicationContext context) {
this.registry.setApplicationContext(context);
}

/**
* Determine if an {@link Authentication} has access to the returned object by
* evaluating the {@link PostAuthorize} annotation that the {@link MethodInvocation}
Expand All @@ -81,7 +86,7 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, Meth
expressionHandler.setReturnObject(mi.getResult(), ctx);
boolean granted = ExpressionUtils.evaluateAsBoolean(postAuthorizeAttribute.getExpression(), ctx);
return new PostProcessableAuthorizationDecision<>(granted, postAuthorizeAttribute.getExpression(), mi,
postAuthorizeAttribute.getPostProcessorClass());
postAuthorizeAttribute.getPostProcessor());
}

}
Loading

0 comments on commit 526d87d

Please sign in to comment.