Deprecate PrePostTemplateDefaults

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 gh-15286
This commit is contained in:
Josh Cummings
2024-08-09 13:28:36 -06:00
parent 2c02d8aec7
commit e40c98e6d7
13 changed files with 173 additions and 35 deletions

View File

@@ -51,6 +51,7 @@ import org.springframework.security.authorization.method.PreAuthorizeAuthorizati
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 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
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 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
@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 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
.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 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
@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 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
.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 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
@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 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfras
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)));

View File

@@ -33,6 +33,8 @@ import org.aopalliance.intercept.MethodInterceptor;
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;
@@ -78,6 +80,7 @@ import org.springframework.security.config.test.SpringTestContext;
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;
@@ -607,69 +610,77 @@ public class PrePostMethodSecurityConfigurationTests {
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");
@@ -827,7 +838,7 @@ public class PrePostMethodSecurityConfigurationTests {
@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);
@@ -1268,7 +1279,7 @@ public class PrePostMethodSecurityConfigurationTests {
@Configuration
@EnableMethodSecurity
static class MetaAnnotationPlaceholderConfig {
static class LegacyMetaAnnotationPlaceholderConfig {
@Bean
PrePostTemplateDefaults methodSecurityDefaults() {
@@ -1282,6 +1293,22 @@ public class PrePostMethodSecurityConfigurationTests {
}
@Configuration
@EnableMethodSecurity
static class MetaAnnotationPlaceholderConfig {
@Bean
AnnotationTemplateExpressionDefaults methodSecurityDefaults() {
return new AnnotationTemplateExpressionDefaults();
}
@Bean
MetaAnnotationService metaAnnotationService() {
return new MetaAnnotationService();
}
}
static class MetaAnnotationService {
@RequireRole(role = "#role")