Skip to content

Commit

Permalink
Defer MethodSecurityExpressionHandler Resolution
Browse files Browse the repository at this point in the history
When using Spring Security ACL and compiling to Native, in order to create the '*AuthorizationMethodInterceptor' Proxy beans during build time, Spring tries to resolve the DataSource bean since the DataSource can be a dependency of some AclService implementations, and fails because some required data source properties are not available during build time.

This commit defers the initialization of the MethodSecurityExpressionHandler to the runtime.

Closes spring-projectsgh-12653
  • Loading branch information
marcusdacoregio committed Sep 13, 2023
1 parent db37bdf commit 8dbf354
Showing 1 changed file with 51 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@

package org.springframework.security.config.annotation.method.configuration;

import java.util.function.Supplier;

import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationEventPublisher;
Expand All @@ -36,7 +42,9 @@
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.function.SingletonSupplier;

/**
* Base {@link Configuration} for enabling Spring Security Method Security.
Expand All @@ -59,7 +67,7 @@ static MethodInterceptor preFilterAuthorizationMethodInterceptor(
PreFilterAuthorizationMethodInterceptor preFilter = new PreFilterAuthorizationMethodInterceptor();
strategyProvider.ifAvailable(preFilter::setSecurityContextHolderStrategy);
preFilter.setExpressionHandler(
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
return preFilter;
}

Expand All @@ -73,7 +81,7 @@ static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
manager.setExpressionHandler(
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor
.preAuthorize(manager(manager, registryProvider));
strategyProvider.ifAvailable(preAuthorize::setSecurityContextHolderStrategy);
Expand All @@ -91,7 +99,7 @@ static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
manager.setExpressionHandler(
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
AuthorizationManagerAfterMethodInterceptor postAuthorize = AuthorizationManagerAfterMethodInterceptor
.postAuthorize(manager(manager, registryProvider));
strategyProvider.ifAvailable(postAuthorize::setSecurityContextHolderStrategy);
Expand All @@ -108,7 +116,7 @@ static MethodInterceptor postFilterAuthorizationMethodInterceptor(
PostFilterAuthorizationMethodInterceptor postFilter = new PostFilterAuthorizationMethodInterceptor();
strategyProvider.ifAvailable(postFilter::setSecurityContextHolderStrategy);
postFilter.setExpressionHandler(
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
return postFilter;
}

Expand All @@ -125,4 +133,43 @@ static <T> AuthorizationManager<T> manager(AuthorizationManager<T> delegate,
return new DeferringObservationAuthorizationManager<>(registryProvider, delegate);
}

private static final class DeferringMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {

private final Supplier<MethodSecurityExpressionHandler> expressionHandler;

private DeferringMethodSecurityExpressionHandler(
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider, ApplicationContext applicationContext) {
this.expressionHandler = SingletonSupplier.of(() -> expressionHandlerProvider
.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, applicationContext)));
}

@Override
public ExpressionParser getExpressionParser() {
return this.expressionHandler.get().getExpressionParser();
}

@Override
public EvaluationContext createEvaluationContext(Authentication authentication, MethodInvocation invocation) {
return this.expressionHandler.get().createEvaluationContext(authentication, invocation);
}

@Override
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication,
MethodInvocation invocation) {
return this.expressionHandler.get().createEvaluationContext(authentication, invocation);
}

@Override
public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
return this.expressionHandler.get().filter(filterTarget, filterExpression, ctx);
}

@Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
this.expressionHandler.get().setReturnObject(returnObject, ctx);
}

}

}

0 comments on commit 8dbf354

Please sign in to comment.