Skip to content

Commit

Permalink
Deprecate PrePostTemplateDefaults
Browse files Browse the repository at this point in the history
Since there is nothing specific to configuring pre/post
annotations, there is no need for the extra class.

If a need like this does arise in the future,
either AnnotationTemplateExpressionDefaults can be sub-
classed, or it can have introduced a Map field holding
custom properties.

Issue spring-projectsgh-15286
  • Loading branch information
jzheaux committed Aug 9, 2024
1 parent 3e5419d commit 9a93174
Showing 13 changed files with 173 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.function.SingletonSupplier;

@@ -72,6 +73,7 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor preFilterAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<AnnotationTemplateExpressionDefaults> templateExpressionDefaultsProvider,
ObjectProvider<PrePostTemplateDefaults> methodSecurityDefaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
@@ -80,6 +82,7 @@ static MethodInterceptor preFilterAuthorizationMethodInterceptor(
PreFilterAuthorizationMethodInterceptor preFilter = new PreFilterAuthorizationMethodInterceptor();
preFilter.setOrder(preFilter.getOrder() + configuration.interceptorOrderOffset);
return new DeferringMethodInterceptor<>(preFilter, (f) -> {
templateExpressionDefaultsProvider.ifAvailable(f::setTemplateDefaults);
methodSecurityDefaultsProvider.ifAvailable(f::setTemplateDefaults);
f.setExpressionHandler(expressionHandlerProvider
.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context)));
@@ -91,6 +94,7 @@ static MethodInterceptor preFilterAuthorizationMethodInterceptor(
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<AnnotationTemplateExpressionDefaults> templateExpressionDefaultsProvider,
ObjectProvider<PrePostTemplateDefaults> methodSecurityDefaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
@@ -103,6 +107,7 @@ static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
.preAuthorize(manager(manager, registryProvider));
preAuthorize.setOrder(preAuthorize.getOrder() + configuration.interceptorOrderOffset);
return new DeferringMethodInterceptor<>(preAuthorize, (f) -> {
templateExpressionDefaultsProvider.ifAvailable(manager::setTemplateDefaults);
methodSecurityDefaultsProvider.ifAvailable(manager::setTemplateDefaults);
manager.setExpressionHandler(expressionHandlerProvider
.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context)));
@@ -115,6 +120,7 @@ static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<AnnotationTemplateExpressionDefaults> templateExpressionDefaultsProvider,
ObjectProvider<PrePostTemplateDefaults> methodSecurityDefaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
@@ -127,6 +133,7 @@ static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
.postAuthorize(manager(manager, registryProvider));
postAuthorize.setOrder(postAuthorize.getOrder() + configuration.interceptorOrderOffset);
return new DeferringMethodInterceptor<>(postAuthorize, (f) -> {
templateExpressionDefaultsProvider.ifAvailable(manager::setTemplateDefaults);
methodSecurityDefaultsProvider.ifAvailable(manager::setTemplateDefaults);
manager.setExpressionHandler(expressionHandlerProvider
.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context)));
@@ -139,6 +146,7 @@ static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor postFilterAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<AnnotationTemplateExpressionDefaults> templateExpressionDefaultsProvider,
ObjectProvider<PrePostTemplateDefaults> methodSecurityDefaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
@@ -147,6 +155,7 @@ static MethodInterceptor postFilterAuthorizationMethodInterceptor(
PostFilterAuthorizationMethodInterceptor postFilter = new PostFilterAuthorizationMethodInterceptor();
postFilter.setOrder(postFilter.getOrder() + configuration.interceptorOrderOffset);
return new DeferringMethodInterceptor<>(postFilter, (f) -> {
templateExpressionDefaultsProvider.ifAvailable(f::setTemplateDefaults);
methodSecurityDefaultsProvider.ifAvailable(f::setTemplateDefaults);
f.setExpressionHandler(expressionHandlerProvider
.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context)));
Original file line number Diff line number Diff line change
@@ -32,6 +32,8 @@
import org.aopalliance.intercept.MethodInvocation;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import org.springframework.aop.Advisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
@@ -76,6 +78,7 @@
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.test.SpringTestParentApplicationContextExecutionListener;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
@@ -605,69 +608,77 @@ public void allAnnotationsWhenAdviceAfterAllOffsetThenReturnsFilteredList() {
assertThat(filtered).containsExactly("DoNotDrop");
}

@Test
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
@WithMockUser
public void methodeWhenParameterizedPreAuthorizeMetaAnnotationThenPasses() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
public void methodeWhenParameterizedPreAuthorizeMetaAnnotationThenPasses(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
assertThat(service.hasRole("USER")).isTrue();
}

@Test
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
@WithMockUser
public void methodRoleWhenPreAuthorizeMetaAnnotationHardcodedParameterThenPasses() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
public void methodRoleWhenPreAuthorizeMetaAnnotationHardcodedParameterThenPasses(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
assertThat(service.hasUserRole()).isTrue();
}

@Test
public void methodWhenParameterizedAnnotationThenFails() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
public void methodWhenParameterizedAnnotationThenFails(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(service::placeholdersOnlyResolvedByMetaAnnotations);
}

@Test
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
@WithMockUser(authorities = "SCOPE_message:read")
public void methodWhenMultiplePlaceholdersHasAuthorityThenPasses() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
public void methodWhenMultiplePlaceholdersHasAuthorityThenPasses(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
assertThat(service.readMessage()).isEqualTo("message");
}

@Test
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
@WithMockUser(roles = "ADMIN")
public void methodWhenMultiplePlaceholdersHasRoleThenPasses() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
public void methodWhenMultiplePlaceholdersHasRoleThenPasses(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
assertThat(service.readMessage()).isEqualTo("message");
}

@Test
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
@WithMockUser
public void methodWhenPostAuthorizeMetaAnnotationThenAuthorizes() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
public void methodWhenPostAuthorizeMetaAnnotationThenAuthorizes(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
service.startsWithDave("daveMatthews");
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> service.startsWithDave("jenniferHarper"));
}

@Test
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
@WithMockUser
public void methodWhenPreFilterMetaAnnotationThenFilters() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
public void methodWhenPreFilterMetaAnnotationThenFilters(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
assertThat(service.parametersContainDave(new ArrayList<>(List.of("dave", "carla", "vanessa", "paul"))))
.containsExactly("dave");
}

@Test
@ParameterizedTest
@ValueSource(classes = { LegacyMetaAnnotationPlaceholderConfig.class, MetaAnnotationPlaceholderConfig.class })
@WithMockUser
public void methodWhenPostFilterMetaAnnotationThenFilters() {
this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
public void methodWhenPostFilterMetaAnnotationThenFilters(Class<?> config) {
this.spring.register(config).autowire();
MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);
assertThat(service.resultsContainDave(new ArrayList<>(List.of("dave", "carla", "vanessa", "paul"))))
.containsExactly("dave");
@@ -825,7 +836,7 @@ void preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationInClassThenHandlerCanUs
@WithMockUser
void postAuthorizeWhenNullDeniedMetaAnnotationThanWorks() {
this.spring
.register(MethodSecurityServiceEnabledConfig.class, MetaAnnotationPlaceholderConfig.class,
.register(MethodSecurityServiceEnabledConfig.class, LegacyMetaAnnotationPlaceholderConfig.class,
MethodSecurityService.NullPostProcessor.class)
.autowire();
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
@@ -1259,7 +1270,7 @@ Authz authz() {

@Configuration
@EnableMethodSecurity
static class MetaAnnotationPlaceholderConfig {
static class LegacyMetaAnnotationPlaceholderConfig {

@Bean
PrePostTemplateDefaults methodSecurityDefaults() {
@@ -1273,6 +1284,22 @@ MetaAnnotationService metaAnnotationService() {

}

@Configuration
@EnableMethodSecurity
static class MetaAnnotationPlaceholderConfig {

@Bean
AnnotationTemplateExpressionDefaults methodSecurityDefaults() {
return new AnnotationTemplateExpressionDefaults();
}

@Bean
MetaAnnotationService metaAnnotationService() {
return new MetaAnnotationService();
}

}

static class MetaAnnotationService {

@RequireRole(role = "#role")
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
import org.springframework.lang.NonNull;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.util.Assert;

/**
@@ -76,6 +77,15 @@ void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.expressionHandler = expressionHandler;
}

@Deprecated
void setTemplateDefaults(PrePostTemplateDefaults defaults) {
AnnotationTemplateExpressionDefaults adapter = new AnnotationTemplateExpressionDefaults();
adapter.setIgnoreUnknown(defaults.isIgnoreUnknown());
setTemplateDefaults(adapter);
}

abstract void setTemplateDefaults(AnnotationTemplateExpressionDefaults adapter);

/**
* Subclasses should implement this method to provide the non-null
* {@link ExpressionAttribute} for the method and the target class.
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;

/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
@@ -57,11 +58,26 @@ public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandl
* not be resolved.
* @param defaults - whether to resolve pre/post-authorization templates parameters
* @since 6.3
* @deprecated Please use
* {@link #setTemplateDefaults(AnnotationTemplateExpressionDefaults)} instead
*/
@Deprecated
public void setTemplateDefaults(PrePostTemplateDefaults defaults) {
this.registry.setTemplateDefaults(defaults);
}

/**
* Configure pre/post-authorization template resolution
* <p>
* By default, this value is <code>null</code>, which indicates that templates should
* not be resolved.
* @param defaults - whether to resolve pre/post-authorization templates parameters
* @since 6.4
*/
public void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {
this.registry.setTemplateDefaults(defaults);
}

/**
* Invokes
* {@link PostAuthorizeExpressionAttributeRegistry#setApplicationContext(ApplicationContext)}
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.core.annotation.AnnotationSynthesizer;
import org.springframework.security.core.annotation.AnnotationSynthesizers;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.util.Assert;

/**
@@ -91,7 +92,7 @@ void setApplicationContext(ApplicationContext context) {
this.handlerResolver = (clazz) -> resolveHandler(context, clazz);
}

void setTemplateDefaults(PrePostTemplateDefaults templateDefaults) {
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
this.postAuthorizeSynthesizer = AnnotationSynthesizers.requireUnique(PostAuthorize.class, templateDefaults);
}

Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;

@@ -73,11 +74,25 @@ public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandl
* not be resolved.
* @param defaults - whether to resolve pre/post-authorization templates parameters
* @since 6.3
* @deprecated Please use {@link AnnotationTemplateExpressionDefaults} instead
*/
@Deprecated
public void setTemplateDefaults(PrePostTemplateDefaults defaults) {
this.registry.setTemplateDefaults(defaults);
}

/**
* Configure pre/post-authorization template resolution
* <p>
* By default, this value is <code>null</code>, which indicates that templates should
* not be resolved.
* @param defaults - whether to resolve pre/post-authorization templates parameters
* @since 6.4
*/
public void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {
this.registry.setTemplateDefaults(defaults);
}

/**
* {@inheritDoc}
*/
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.core.annotation.AnnotationSynthesizer;
import org.springframework.security.core.annotation.AnnotationSynthesizers;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;

/**
* For internal use only, as this contract is likely to change.
@@ -49,7 +50,7 @@ ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
return new ExpressionAttribute(postFilterExpression);
}

void setTemplateDefaults(PrePostTemplateDefaults defaults) {
void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {
this.synthesizer = AnnotationSynthesizers.requireUnique(PostFilter.class, defaults);
}

Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;

/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
@@ -57,11 +58,26 @@ public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandl
* not be resolved.
* @param defaults - whether to resolve pre/post-authorization templates parameters
* @since 6.3
* @deprecated Please use
* {@link #setTemplateDefaults(AnnotationTemplateExpressionDefaults)} instead
*/
@Deprecated
public void setTemplateDefaults(PrePostTemplateDefaults defaults) {
this.registry.setTemplateDefaults(defaults);
}

/**
* Configure pre/post-authorization template resolution
* <p>
* By default, this value is <code>null</code>, which indicates that templates should
* not be resolved.
* @param defaults - whether to resolve pre/post-authorization templates parameters
* @since 6.4
*/
public void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {
this.registry.setTemplateDefaults(defaults);
}

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

0 comments on commit 9a93174

Please sign in to comment.