Skip to content

Commit 526d87d

Browse files
Polish
- Resolve the post processor in the Expression Attribute Registry - Rename the default post processors - Create PostProcessableAuthorizationDecision#postProcess - Rename AuthorizationException to AuthorizationDeniedException
1 parent e0eff34 commit 526d87d

19 files changed

+143
-198
lines changed

config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationPostProcessorConfiguration.java

-37
This file was deleted.

config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,7 +57,6 @@ public String[] selectImports(@NonNull AnnotationMetadata importMetadata) {
5757
imports.add(Jsr250MethodSecurityConfiguration.class.getName());
5858
}
5959
imports.add(AuthorizationProxyConfiguration.class.getName());
60-
imports.add(AuthorizationPostProcessorConfiguration.class.getName());
6160
return imports.toArray(new String[0]);
6261
}
6362

config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
9898
ObjectProvider<ObservationRegistry> registryProvider, ObjectProvider<RoleHierarchy> roleHierarchyProvider,
9999
PrePostMethodSecurityConfiguration configuration, ApplicationContext context) {
100100
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
101+
manager.setApplicationContext(context);
101102
AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor
102103
.preAuthorize(manager(manager, registryProvider));
103104
preAuthorize.setOrder(preAuthorize.getOrder() + configuration.interceptorOrderOffset);
104-
preAuthorize.setApplicationContext(context);
105105
return new DeferringMethodInterceptor<>(preAuthorize, (f) -> {
106106
methodSecurityDefaultsProvider.ifAvailable(manager::setTemplateDefaults);
107107
manager.setExpressionHandler(expressionHandlerProvider
@@ -122,6 +122,7 @@ static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
122122
ObjectProvider<ObservationRegistry> registryProvider, ObjectProvider<RoleHierarchy> roleHierarchyProvider,
123123
PrePostMethodSecurityConfiguration configuration, ApplicationContext context) {
124124
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
125+
manager.setApplicationContext(context);
125126
AuthorizationManagerAfterMethodInterceptor postAuthorize = AuthorizationManagerAfterMethodInterceptor
126127
.postAuthorize(manager(manager, registryProvider));
127128
postAuthorize.setOrder(postAuthorize.getOrder() + configuration.interceptorOrderOffset);

core/src/main/java/org/springframework/security/access/prepost/PostAuthorize.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
import java.lang.annotation.Target;
2525

2626
import org.springframework.security.authorization.method.AuthorizationDeniedPostProcessor;
27-
import org.springframework.security.authorization.method.DefaultPostInvocationAuthorizationDeniedPostProcessor;
2827
import org.springframework.security.authorization.method.MethodInvocationResult;
28+
import org.springframework.security.authorization.method.PostInvocationThrowingAuthorizationDeniedPostProcessor;
2929

3030
/**
3131
* Annotation for specifying a method access-control expression which will be evaluated
@@ -46,6 +46,6 @@
4646
*/
4747
String value();
4848

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

5151
}

core/src/main/java/org/springframework/security/access/prepost/PreAuthorize.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import org.aopalliance.intercept.MethodInvocation;
2727

2828
import org.springframework.security.authorization.method.AuthorizationDeniedPostProcessor;
29-
import org.springframework.security.authorization.method.DefaultPreInvocationAuthorizationDeniedPostProcessor;
29+
import org.springframework.security.authorization.method.PreInvocationThrowingAuthorizationDeniedPostProcessor;
3030

3131
/**
3232
* Annotation for specifying a method access-control expression which will be evaluated to
@@ -47,6 +47,6 @@
4747
*/
4848
String value();
4949

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

5252
}

core/src/main/java/org/springframework/security/authorization/AuthorizationException.java renamed to core/src/main/java/org/springframework/security/authorization/AuthorizationDeniedException.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
import org.springframework.security.access.AccessDeniedException;
2020
import org.springframework.util.Assert;
2121

22-
public class AuthorizationException extends AccessDeniedException {
22+
public class AuthorizationDeniedException extends AccessDeniedException {
2323

2424
private final AuthorizationResult result;
2525

26-
public AuthorizationException(String msg, AuthorizationResult result) {
26+
public AuthorizationDeniedException(String msg, AuthorizationResult result) {
2727
super(msg);
2828
Assert.notNull(result, "decision cannot be null");
2929
Assert.state(!result.isGranted(), "Granted decisions are not supported");

core/src/main/java/org/springframework/security/authorization/method/AbstractExpressionAttributeRegistry.java

+11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import org.aopalliance.intercept.MethodInvocation;
2727

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

4647
private PrePostTemplateDefaults defaults;
4748

49+
private ApplicationContext applicationContext;
50+
4851
/**
4952
* Returns an {@link ExpressionAttribute} for the {@link MethodInvocation}.
5053
* @param mi the {@link MethodInvocation} to use
@@ -90,6 +93,14 @@ void setTemplateDefaults(PrePostTemplateDefaults defaults) {
9093
this.defaults = defaults;
9194
}
9295

96+
void setApplicationContext(ApplicationContext applicationContext) {
97+
this.applicationContext = applicationContext;
98+
}
99+
100+
ApplicationContext getApplicationContext() {
101+
return this.applicationContext;
102+
}
103+
93104
/**
94105
* Subclasses should implement this method to provide the non-null
95106
* {@link ExpressionAttribute} for the method and the target class.

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java

+6-37
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.security.authorization.method;
1818

19-
import java.util.Arrays;
2019
import java.util.function.Supplier;
2120

2221
import org.aopalliance.aop.Advice;
@@ -27,7 +26,6 @@
2726

2827
import org.springframework.aop.Pointcut;
2928
import org.springframework.context.ApplicationContext;
30-
import org.springframework.core.ResolvableType;
3129
import org.springframework.core.log.LogMessage;
3230
import org.springframework.security.access.AccessDeniedException;
3331
import org.springframework.security.access.prepost.PostAuthorize;
@@ -58,7 +56,7 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
5856

5957
private final AuthorizationManager<MethodInvocationResult> authorizationManager;
6058

61-
private final AuthorizationDeniedPostProcessor<MethodInvocationResult> defaultPostProcessor = new DefaultPostInvocationAuthorizationDeniedPostProcessor();
59+
private final AuthorizationDeniedPostProcessor<MethodInvocationResult> postProcessor = new PostInvocationThrowingAuthorizationDeniedPostProcessor();
6260

6361
private int order;
6462

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

196196
private Object postProcess(MethodInvocationResult mi, AuthorizationDecision decision) {
197-
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> postProcessorClass = getPostProcessorClass(
198-
decision);
199-
AuthorizationDeniedPostProcessor<MethodInvocationResult> postProcessor = resolvePostProcessor(
200-
postProcessorClass);
201-
return postProcessor.postProcessResult(mi, decision);
202-
}
203-
204-
@SuppressWarnings({ "unchecked", "DataFlowIssue" })
205-
private Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> getPostProcessorClass(
206-
AuthorizationDecision decision) {
207-
if (!(decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision)) {
208-
return null;
209-
}
210-
if (!MethodInvocationResult.class
211-
.isAssignableFrom(ResolvableType.forInstance(postProcessableDecision).resolveGeneric(0))) {
212-
return null;
213-
}
214-
return (Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>>) postProcessableDecision
215-
.getPostProcessorClass();
216-
}
217-
218-
private AuthorizationDeniedPostProcessor<MethodInvocationResult> resolvePostProcessor(
219-
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocationResult>> beanClass) {
220-
if (beanClass == null || this.context == null) {
221-
return this.defaultPostProcessor;
222-
}
223-
String[] beanNames = this.context.getBeanNamesForType(beanClass);
224-
if (beanNames.length == 0) {
225-
throw new IllegalStateException("Could not find a bean of type " + beanClass.getSimpleName());
226-
}
227-
if (beanNames.length > 1) {
228-
throw new IllegalStateException("Expected to find a single bean of type " + beanClass.getSimpleName()
229-
+ " but found " + Arrays.toString(beanNames));
197+
if (decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision) {
198+
return postProcessableDecision.postProcess();
230199
}
231-
return this.context.getBean(beanNames[0], beanClass);
200+
return this.postProcessor.postProcessResult(mi, decision);
232201
}
233202

234203
private Authentication getAuthentication() {

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java

+6-44
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.security.authorization.method;
1818

19-
import java.util.Arrays;
2019
import java.util.function.Supplier;
2120

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

3130
import org.springframework.aop.Pointcut;
32-
import org.springframework.context.ApplicationContext;
33-
import org.springframework.core.ResolvableType;
3431
import org.springframework.core.log.LogMessage;
3532
import org.springframework.security.access.AccessDeniedException;
3633
import org.springframework.security.access.annotation.Secured;
@@ -62,14 +59,12 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author
6259

6360
private final AuthorizationManager<MethodInvocation> authorizationManager;
6461

65-
private final AuthorizationDeniedPostProcessor<MethodInvocation> defaultPostProcessor = new DefaultPreInvocationAuthorizationDeniedPostProcessor();
62+
private final AuthorizationDeniedPostProcessor<MethodInvocation> postProcessor = new PreInvocationThrowingAuthorizationDeniedPostProcessor();
6663

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

6966
private AuthorizationEventPublisher eventPublisher = AuthorizationManagerBeforeMethodInterceptor::noPublish;
7067

71-
private ApplicationContext context;
72-
7368
/**
7469
* Creates an instance.
7570
* @param pointcut the {@link Pointcut} to use
@@ -193,6 +188,8 @@ public static AuthorizationManagerBeforeMethodInterceptor jsr250(
193188
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
194189
* using the configured {@link AuthorizationManager}.
195190
* @param mi the {@link MethodInvocation} to check
191+
* @return the result of the method invocation or a post-processed result if
192+
* authorization denied
196193
* @throws AccessDeniedException if access is not granted
197194
*/
198195
@Override
@@ -248,11 +245,6 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur
248245
this.securityContextHolderStrategy = () -> securityContextHolderStrategy;
249246
}
250247

251-
public void setApplicationContext(ApplicationContext applicationContext) {
252-
Assert.notNull(applicationContext, "applicationContext cannot be null");
253-
this.context = applicationContext;
254-
}
255-
256248
private Object attemptAuthorization(MethodInvocation mi) throws Throwable {
257249
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
258250
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, mi);
@@ -267,40 +259,10 @@ private Object attemptAuthorization(MethodInvocation mi) throws Throwable {
267259
}
268260

269261
private Object postProcess(MethodInvocation mi, AuthorizationDecision decision) {
270-
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> postProcessorClass = getPostProcessorClass(
271-
decision);
272-
AuthorizationDeniedPostProcessor<MethodInvocation> postProcessor = resolvePostProcessor(postProcessorClass);
273-
return postProcessor.postProcessResult(mi, decision);
274-
}
275-
276-
@SuppressWarnings({ "unchecked", "DataFlowIssue" })
277-
private Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> getPostProcessorClass(
278-
AuthorizationDecision decision) {
279-
if (!(decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision)) {
280-
return null;
281-
}
282-
Class<?> genericType = ResolvableType.forInstance(postProcessableDecision).resolveGeneric(0);
283-
if (!MethodInvocation.class.isAssignableFrom(genericType)) {
284-
return null;
285-
}
286-
return (Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>>) postProcessableDecision
287-
.getPostProcessorClass();
288-
}
289-
290-
private AuthorizationDeniedPostProcessor<MethodInvocation> resolvePostProcessor(
291-
Class<? extends AuthorizationDeniedPostProcessor<MethodInvocation>> beanClass) {
292-
if (beanClass == null || this.context == null) {
293-
return this.defaultPostProcessor;
294-
}
295-
String[] beanNames = this.context.getBeanNamesForType(beanClass);
296-
if (beanNames.length == 0) {
297-
throw new IllegalStateException("Could not find a bean of type " + beanClass.getSimpleName());
298-
}
299-
if (beanNames.length > 1) {
300-
throw new IllegalStateException("Expected to find a single bean of type " + beanClass.getSimpleName()
301-
+ " but found " + Arrays.toString(beanNames));
262+
if (decision instanceof PostProcessableAuthorizationDecision<?> postProcessableDecision) {
263+
return postProcessableDecision.postProcess();
302264
}
303-
return this.context.getBean(beanNames[0], beanClass);
265+
return this.postProcessor.postProcessResult(mi, decision);
304266
}
305267

306268
private Authentication getAuthentication() {

core/src/main/java/org/springframework/security/authorization/method/MethodAccessDeniedHandlerResolver.java

-29
This file was deleted.

core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.aopalliance.intercept.MethodInvocation;
2222

23+
import org.springframework.context.ApplicationContext;
2324
import org.springframework.expression.EvaluationContext;
2425
import org.springframework.security.access.expression.ExpressionUtils;
2526
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
@@ -60,6 +61,10 @@ public void setTemplateDefaults(PrePostTemplateDefaults defaults) {
6061
this.registry.setTemplateDefaults(defaults);
6162
}
6263

64+
public void setApplicationContext(ApplicationContext context) {
65+
this.registry.setApplicationContext(context);
66+
}
67+
6368
/**
6469
* Determine if an {@link Authentication} has access to the returned object by
6570
* evaluating the {@link PostAuthorize} annotation that the {@link MethodInvocation}
@@ -81,7 +86,7 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, Meth
8186
expressionHandler.setReturnObject(mi.getResult(), ctx);
8287
boolean granted = ExpressionUtils.evaluateAsBoolean(postAuthorizeAttribute.getExpression(), ctx);
8388
return new PostProcessableAuthorizationDecision<>(granted, postAuthorizeAttribute.getExpression(), mi,
84-
postAuthorizeAttribute.getPostProcessorClass());
89+
postAuthorizeAttribute.getPostProcessor());
8590
}
8691

8792
}

0 commit comments

Comments
 (0)