Add check for custom advice

- Because publishing an advice bean replaces Spring Security
defaults, the code should error if both a custom bean and
either secureEnabled or prePostEnabled are specified

Issue gh-9289
This commit is contained in:
Josh Cummings 2021-04-08 14:33:30 -06:00
parent 45376b359b
commit 68cf74468c
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
2 changed files with 42 additions and 13 deletions

View File

@ -36,6 +36,7 @@ import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.Pointcuts; import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.StaticMethodMatcher; import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -45,26 +46,27 @@ import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.annotation.Secured;
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.method.AuthorizationMethodInterceptor; import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authorization.method.AuthorizationManagerMethodAfterAdvice; import org.springframework.security.authorization.method.AuthorizationManagerMethodAfterAdvice;
import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice; import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice; import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodBeforeAdvice; import org.springframework.security.authorization.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodAfterAdvice; import org.springframework.security.authorization.method.DelegatingAuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodBeforeAdvice; import org.springframework.security.authorization.method.DelegatingAuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
import org.springframework.security.authorization.method.MethodAuthorizationContext; import org.springframework.security.authorization.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager; import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodAfterAdvice; import org.springframework.security.authorization.method.PostFilterAuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager; import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodBeforeAdvice; import org.springframework.security.authorization.method.PreFilterAuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.util.Assert;
/** /**
* Base {@link Configuration} for enabling Spring Security Method Security. * Base {@link Configuration} for enabling Spring Security Method Security.
@ -75,7 +77,7 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class MethodSecurityConfiguration implements ImportAware { final class MethodSecurityConfiguration implements ImportAware, InitializingBean {
private MethodSecurityExpressionHandler methodSecurityExpressionHandler; private MethodSecurityExpressionHandler methodSecurityExpressionHandler;
@ -214,6 +216,15 @@ final class MethodSecurityConfiguration implements ImportAware {
this.enableMethodSecurity = AnnotationAttributes.fromMap(attributes); this.enableMethodSecurity = AnnotationAttributes.fromMap(attributes);
} }
@Override
public void afterPropertiesSet() throws Exception {
if (!securedEnabled() && !jsr250Enabled()) {
return;
}
Assert.isNull(this.authorizationMethodBeforeAdvice,
"You have specified your own advice, meaning that the annotation attributes securedEnabled and jsr250Enabled will be ignored. Please choose one or the other.");
}
private boolean securedEnabled() { private boolean securedEnabled() {
return this.enableMethodSecurity.getBoolean("securedEnabled"); return this.enableMethodSecurity.getBoolean("securedEnabled");
} }

View File

@ -27,6 +27,7 @@ import org.junit.runner.RunWith;
import org.springframework.aop.MethodMatcher; import org.springframework.aop.MethodMatcher;
import org.springframework.aop.support.JdkRegexpMethodPointcut; import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
@ -35,12 +36,12 @@ import org.springframework.security.access.annotation.BusinessService;
import org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl; import org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice; import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice; import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodBeforeAdvice; import org.springframework.security.authorization.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.MethodAuthorizationContext; import org.springframework.security.authorization.method.MethodAuthorizationContext;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
@ -103,7 +104,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser @WithMockUser
@Test @Test
public void securedWhenRoleUserThenAccessDeniedException() { public void securedWhenRoleUserThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire(); this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured) assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)
.withMessage("Access Denied"); .withMessage("Access Denied");
} }
@ -119,7 +120,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser(roles = "ADMIN") @WithMockUser(roles = "ADMIN")
@Test @Test
public void securedUserWhenRoleAdminThenAccessDeniedException() { public void securedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire(); this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser) assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied"); .withMessage("Access Denied");
} }
@ -244,7 +245,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser(roles = "ADMIN") @WithMockUser(roles = "ADMIN")
@Test @Test
public void jsr250WhenRoleAdminThenAccessDeniedException() { public void jsr250WhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire(); this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250) assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)
.withMessage("Access Denied"); .withMessage("Access Denied");
} }
@ -252,7 +253,7 @@ public class MethodSecurityConfigurationTests {
@WithAnonymousUser @WithAnonymousUser
@Test @Test
public void jsr250PermitAllWhenRoleAnonymousThenPasses() { public void jsr250PermitAllWhenRoleAnonymousThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire(); this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
String result = this.methodSecurityService.jsr250PermitAll(); String result = this.methodSecurityService.jsr250PermitAll();
assertThat(result).isNull(); assertThat(result).isNull();
} }
@ -272,7 +273,14 @@ public class MethodSecurityConfigurationTests {
this.businessService.rolesAllowedUser(); this.businessService.rolesAllowedUser();
} }
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true) @Test
public void configureWhenCustomAdviceAndSecureEnabledThenException() {
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring
.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceEnabledConfig.class)
.autowire());
}
@EnableMethodSecurity
static class MethodSecurityServiceConfig { static class MethodSecurityServiceConfig {
@Bean @Bean
@ -292,6 +300,16 @@ public class MethodSecurityConfigurationTests {
} }
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
static class MethodSecurityServiceEnabledConfig {
@Bean
MethodSecurityService methodSecurityService() {
return new MethodSecurityServiceImpl();
}
}
@EnableMethodSecurity @EnableMethodSecurity
static class CustomPermissionEvaluatorConfig { static class CustomPermissionEvaluatorConfig {