From 6af1ac08db98216ac7e6ded6f1adea2fdd10a81d Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 12 Feb 2018 16:28:12 -0600 Subject: [PATCH] GlobalMethodSecurityConfigurationTests groovy->java Issue: gh-4939 --- ...balMethodSecurityConfigurationTests.groovy | 518 ------------------ ...lobalMethodSecurityConfigurationTests.java | 484 ++++++++++++++++ 2 files changed, 484 insertions(+), 518 deletions(-) delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy create mode 100644 config/src/test/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.java diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy deleted file mode 100644 index 7414636058..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.config.annotation.method.configuration - - -import org.springframework.security.access.hierarchicalroles.RoleHierarchy; -import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl - -import java.lang.reflect.Proxy - -import org.springframework.beans.BeansException -import org.springframework.beans.factory.config.BeanPostProcessor -import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter -import org.springframework.security.config.annotation.method.configuration.NamespaceGlobalMethodSecurityTests.BaseMethodConfig -import org.springframework.security.config.core.GrantedAuthorityDefaults; - -import javax.sql.DataSource - -import org.aopalliance.intercept.MethodInterceptor -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.ApplicationContext -import org.springframework.context.ApplicationListener -import org.springframework.context.annotation.AdviceMode -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.security.access.AccessDeniedException -import org.springframework.security.access.PermissionEvaluator -import org.springframework.security.access.prepost.PreAuthorize -import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter -import org.springframework.security.authentication.AuthenticationManager -import org.springframework.security.authentication.AuthenticationTrustResolver -import org.springframework.security.authentication.DefaultAuthenticationEventPublisher -import org.springframework.security.authentication.TestingAuthenticationToken -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken -import org.springframework.security.authentication.event.AuthenticationSuccessEvent -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder -import org.springframework.security.config.method.TestPermissionEvaluator; -import org.springframework.security.core.Authentication -import org.springframework.security.core.authority.AuthorityUtils -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.transaction.annotation.EnableTransactionManagement; - -/** - * - * @author Rob Winch - */ -public class GlobalMethodSecurityConfigurationTests extends BaseSpringSpec { - def "messages set when using GlobalMethodSecurityConfiguration"() { - when: - loadConfig(InMemoryAuthWithGlobalMethodSecurityConfig) - then: - authenticationManager.messages.messageSource instanceof ApplicationContext - } - - def "AuthenticationEventPublisher is registered GlobalMethodSecurityConfiguration"() { - when: - loadConfig(InMemoryAuthWithGlobalMethodSecurityConfig) - then: - authenticationManager.eventPublisher instanceof DefaultAuthenticationEventPublisher - when: - Authentication auth = new UsernamePasswordAuthenticationToken("user",null,AuthorityUtils.createAuthorityList("ROLE_USER")) - authenticationManager.eventPublisher.publishAuthenticationSuccess(auth) - then: - InMemoryAuthWithGlobalMethodSecurityConfig.EVENT.authentication == auth - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - public static class InMemoryAuthWithGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration implements ApplicationListener { - static AuthenticationSuccessEvent EVENT - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Override - public void onApplicationEvent(AuthenticationSuccessEvent e) { - EVENT = e - } - } - - AuthenticationManager getAuthenticationManager() { - context.getBean(MethodInterceptor).authenticationManager - } - - def "AuthenticationTrustResolver autowires"() { - setup: - CustomTrustResolverConfig.TR = Mock(AuthenticationTrustResolver) - when: - loadConfig(CustomTrustResolverConfig) - def preAdviceVoter = context.getBean(MethodInterceptor).accessDecisionManager.decisionVoters.find { it instanceof PreInvocationAuthorizationAdviceVoter} - then: - preAdviceVoter.preAdvice.expressionHandler.trustResolver == CustomTrustResolverConfig.TR - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - static class CustomTrustResolverConfig extends GlobalMethodSecurityConfiguration { - static AuthenticationTrustResolver TR - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Bean - public AuthenticationTrustResolver tr() { - return TR - } - } - - def "SEC-2301: DefaultWebSecurityExpressionHandler has BeanResolver set"() { - setup: - SecurityContextHolder.getContext().setAuthentication( - new TestingAuthenticationToken("user", "password","ROLE_USER")) - loadConfig(ExpressionHandlerHasBeanResolverSetConfig) - def service = context.getBean(ServiceImpl) - when: "service with bean reference on PreAuthorize invoked" - service.message() - then: "properly throws AccessDeniedException" - thrown(AccessDeniedException) - when: "service with bean reference on PreAuthorize invoked" - context.getBean(CustomAuthzService).grantAccess = true - service.message() - then: "grants access too" - noExceptionThrown() - } - - @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) - static class ExpressionHandlerHasBeanResolverSetConfig extends GlobalMethodSecurityConfiguration { - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Bean - public ServiceImpl service() { - return new ServiceImpl() - } - - @Bean - public CustomAuthzService authz() { - return new CustomAuthzService() - } - } - - static class ServiceImpl { - @PreAuthorize("@authz.authorize()") - public String message() { - null - } - } - - static class CustomAuthzService { - boolean grantAccess - - public boolean authorize() { - grantAccess - } - } - - def "Method Security supports annotations on interface parameter names"() { - setup: - SecurityContextHolder.getContext().setAuthentication( - new TestingAuthenticationToken("user", "password","ROLE_USER")) - loadConfig(MethodSecurityServiceConfig) - MethodSecurityService service = context.getBean(MethodSecurityService) - when: "service with annotated argument" - service.postAnnotation('deny') - then: "properly throws AccessDeniedException" - thrown(AccessDeniedException) - when: "service with annotated argument" - service.postAnnotation('grant') - then: "properly throws AccessDeniedException" - noExceptionThrown() - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - static class MethodSecurityServiceConfig extends GlobalMethodSecurityConfiguration { - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Bean - public MethodSecurityService service() { - new MethodSecurityServiceImpl() - } - } - - def "GlobalMethodSecurityConfiguration autowires PermissionEvaluator"() { - setup: - SecurityContextHolder.getContext().setAuthentication( - new TestingAuthenticationToken("user", "password","ROLE_USER")) - PermissionEvaluator evaluator = Mock() - AutowirePermissionEvaluatorConfig.PE = evaluator - loadConfig(AutowirePermissionEvaluatorConfig) - MethodSecurityService service = context.getBean(MethodSecurityService) - when: - service.hasPermission("something") - then: - 1 * evaluator.hasPermission(_, "something", "read") >> true - when: - service.hasPermission("something") - then: - 1 * evaluator.hasPermission(_, "something", "read") >> false - thrown(AccessDeniedException) - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - public static class AutowirePermissionEvaluatorConfig extends GlobalMethodSecurityConfiguration { - static PermissionEvaluator PE - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Bean - public PermissionEvaluator pe() { - PE - } - - @Bean - public MethodSecurityService service() { - new MethodSecurityServiceImpl() - } - } - - def "GlobalMethodSecurityConfiguration does not failw with multiple PermissionEvaluator"() { - when: - loadConfig(MultiPermissionEvaluatorConfig) - then: - noExceptionThrown() - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - public static class MultiPermissionEvaluatorConfig extends GlobalMethodSecurityConfiguration { - static PermissionEvaluator PE = new TestPermissionEvaluator() - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Bean - public PermissionEvaluator pe() { - PE - } - - @Bean - public PermissionEvaluator pe2() { - PE - } - - @Bean - public MethodSecurityService service() { - new MethodSecurityServiceImpl() - } - } - - def "SEC-2425: EnableGlobalMethodSecurity works on superclass"() { - setup: - SecurityContextHolder.getContext().setAuthentication( - new TestingAuthenticationToken("user", "password","ROLE_USER")) - loadConfig(ParentConfig) - MethodSecurityService service = context.getBean(MethodSecurityService) - when: - service.preAuthorize() - then: - thrown(AccessDeniedException) - } - - static class ChildConfig extends ParentConfig {} - - @EnableGlobalMethodSecurity(prePostEnabled = true) - static class ParentConfig { - - @Autowired - protected void configurGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Bean - public MethodSecurityService service() { - new MethodSecurityServiceImpl() - } - } - - def "SEC-2479: Support AuthenticationManager in parent"() { - setup: - SecurityContextHolder.getContext().setAuthentication( - new TestingAuthenticationToken("user", "password","ROLE_USER")) - loadConfig(Sec2479ParentConfig) - def child = new AnnotationConfigApplicationContext() - child.register(Sec2479ChildConfig) - child.parent = context - child.refresh() - MethodSecurityService service = child.getBean(MethodSecurityService) - when: - service.preAuthorize() - then: - thrown(AccessDeniedException) - cleanup: - child?.close() - } - - @Configuration - static class Sec2479ParentConfig { - static AuthenticationManager AM - - @Bean - public AuthenticationManager am() { - AM - } - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - static class Sec2479ChildConfig { - @Bean - public MethodSecurityService service() { - new MethodSecurityServiceImpl() - } - } - - def "SEC-2815: @EnableGlobalMethodSecurity does not trigger eager initialization of Beans in GlobalAuthenticationConfigurer"() { - setup: - Sec2815Config.dataSource = Mock(DataSource) - when: 'load a Configuration that uses a Bean (DataSource) in a GlobalAuthenticationConfigurerAdapter' - loadConfig(Sec2815Config) - then: 'The Bean (DataSource) is still properly post processed with all BeanPostProcessor' - context.getBean(MockBeanPostProcessor).beforeInit['dataSource'] - context.getBean(MockBeanPostProcessor).afterInit['dataSource'] - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - static class Sec2815Config { - static DataSource dataSource; - - @Bean - public MethodSecurityService service() { - new MethodSecurityServiceImpl() - } - - @Bean - public MockBeanPostProcessor mockBeanPostProcessor() { - new MockBeanPostProcessor() - } - - @Bean - public DataSource dataSource() { - dataSource - } - - @Configuration - static class AuthConfig extends GlobalAuthenticationConfigurerAdapter { - @Autowired - DataSource dataSource - - @Override - void init(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication() - } - } - } - - - static class MockBeanPostProcessor implements BeanPostProcessor { - Map beforeInit = new HashMap() - Map afterInit = new HashMap() - - @Override - Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - beforeInit[beanName] = bean - bean - } - - @Override - Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - afterInit[beanName] = bean - bean - } - } - - def "SEC-3045: Global Security proxies security"() { - setup: - when: 'load a Configuration that uses a Bean (DataSource) in a GlobalAuthenticationConfigurerAdapter' - loadConfig(Sec3005Config) - MethodSecurityService service = context.getBean(MethodSecurityService) - then: 'The Bean (DataSource) is still properly post processed with all BeanPostProcessor' - !Proxy.isProxyClass(service.getClass()) - } - - @EnableGlobalMethodSecurity(prePostEnabled = true, mode= AdviceMode.ASPECTJ) - @EnableTransactionManagement - static class Sec3005Config { - static DataSource dataSource; - - @Bean - public MethodSecurityService service() { - new MethodSecurityServiceImpl() - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) { - auth.inMemoryAuthentication() - } - } - - // gh-3797 - def preAuthorizeBeanSpel() { - setup: - SecurityContextHolder.getContext().setAuthentication( - new TestingAuthenticationToken("user", "password","ROLE_USER")) - context = new AnnotationConfigApplicationContext(PreAuthorizeBeanSpelConfig) - BeanSpelService service = context.getBean(BeanSpelService) - when: - service.run(true) - then: - noExceptionThrown() - when: - service.run(false) - then: - thrown(AccessDeniedException) - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - @Configuration - public static class PreAuthorizeBeanSpelConfig extends BaseMethodConfig { - @Bean - BeanSpelService service() { - return new BeanSpelService(); - } - @Bean - BeanSpelSecurity security() { - return new BeanSpelSecurity(); - } - } - - static class BeanSpelService { - @PreAuthorize("@security.check(#arg)") - void run(boolean arg) {} - } - - static class BeanSpelSecurity { - public boolean check(boolean arg) { - return arg; - } - } - - // gh-3394 - def roleHierarchy() { - setup: - SecurityContextHolder.getContext().setAuthentication( - new TestingAuthenticationToken("user", "password","ROLE_USER")) - context = new AnnotationConfigApplicationContext(RoleHierarchyConfig) - MethodSecurityService service = context.getBean(MethodSecurityService) - when: - service.preAuthorizeAdmin() - then: - noExceptionThrown() - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - @Configuration - public static class RoleHierarchyConfig extends BaseMethodConfig { - @Bean - RoleHierarchy roleHierarchy() { - return new RoleHierarchyImpl(hierarchy:"ROLE_USER > ROLE_ADMIN") - } - } - - def "GrantedAuthorityDefaults autowires"() { - when: - loadConfig(CustomGrantedAuthorityConfig) - def preAdviceVoter = context.getBean(MethodInterceptor).accessDecisionManager.decisionVoters.find { it instanceof PreInvocationAuthorizationAdviceVoter} - then: - preAdviceVoter.preAdvice.expressionHandler.defaultRolePrefix == "ROLE:" - } - - @EnableGlobalMethodSecurity(prePostEnabled = true) - static class CustomGrantedAuthorityConfig extends GlobalMethodSecurityConfiguration { - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - - @Bean - public GrantedAuthorityDefaults ga() { - return new GrantedAuthorityDefaults("ROLE:") - } - } -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.java new file mode 100644 index 0000000000..42f3fa866d --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.java @@ -0,0 +1,484 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.config.annotation.method.configuration; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.AdviceMode; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.PermissionEvaluator; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; +import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.event.AbstractAuthenticationEvent; +import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent; +import org.springframework.security.config.MockEventListener; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter; +import org.springframework.security.config.core.GrantedAuthorityDefaults; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import javax.sql.DataSource; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * + * @author Rob Winch + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SecurityTestExecutionListeners +public class GlobalMethodSecurityConfigurationTests { + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired(required = false) + private MethodSecurityService service; + + private AuthenticationManager authenticationManager; + + @Autowired + public void setMethodInterceptor(MethodSecurityInterceptor interceptor) { + this.authenticationManager = interceptor.getAuthenticationManager(); + } + + @Autowired(required = false) + MockEventListener events; + + @Test + public void methodSecurityAuthenticationManagerPublishesEvent() { + this.spring.register(InMemoryAuthWithGlobalMethodSecurityConfig.class).autowire(); + + try { + this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("foo", "bar")); + } catch(AuthenticationException e) {} + + assertThat(this.events.getEvents()).extracting(Object::getClass).containsOnly((Class) AuthenticationFailureBadCredentialsEvent.class); + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + public static class InMemoryAuthWithGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration { + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication(); + } + + @Bean + public MockEventListener listener() { + return new MockEventListener() {}; + } + } + + @Test + @WithMockUser + public void methodSecurityWhenAuthenticationTrustResolverIsBeanThenAutowires() { + this.spring.register(CustomTrustResolverConfig.class).autowire(); + + AuthenticationTrustResolver trustResolver = this.spring.getContext().getBean(AuthenticationTrustResolver.class); + when(trustResolver.isAnonymous(any())).thenReturn(true, false); + + assertThatThrownBy(() -> this.service.preAuthorizeNotAnonymous()) + .isInstanceOf(AccessDeniedException.class); + + this.service.preAuthorizeNotAnonymous(); + + verify(trustResolver, atLeastOnce()).isAnonymous(any()); + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + static class CustomTrustResolverConfig { + + @Bean + public AuthenticationTrustResolver trustResolver() { + return mock(AuthenticationTrustResolver.class); + } + + @Bean + public MethodSecurityServiceImpl service() { + return new MethodSecurityServiceImpl(); + } + } + + // SEC-2301 + @Test + @WithMockUser + public void defaultWebSecurityExpressionHandlerHasBeanResolverSet() { + this.spring.register(ExpressionHandlerHasBeanResolverSetConfig.class).autowire(); + Authz authz = this.spring.getContext().getBean(Authz.class); + + assertThatThrownBy(() -> this.service.preAuthorizeBean(false)) + .isInstanceOf(AccessDeniedException.class); + + this.service.preAuthorizeBean(true); + } + + @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) + static class ExpressionHandlerHasBeanResolverSetConfig { + + @Bean + public MethodSecurityServiceImpl service() { + return new MethodSecurityServiceImpl(); + } + + @Bean + public Authz authz() { + return new Authz(); + } + } + + @Test + @WithMockUser + public void methodSecuritySupportsAnnotaitonsOnInterfaceParamerNames() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + + assertThatThrownBy(() -> this.service.postAnnotation("deny")) + .isInstanceOf(AccessDeniedException.class); + + this.service.postAnnotation("grant"); + // no exception + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + static class MethodSecurityServiceConfig { + + @Bean + public MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + } + + @Test + @WithMockUser + public void globalMethodSecurityConfigurationAutowiresPermissionEvaluator() { + this.spring.register(AutowirePermissionEvaluatorConfig.class).autowire(); + PermissionEvaluator permission = this.spring.getContext().getBean(PermissionEvaluator.class); + when(permission.hasPermission(any(), eq("something"), eq("read"))).thenReturn(true, false); + + this.service.hasPermission("something"); + // no exception + + assertThatThrownBy(() -> this.service.hasPermission("something")) + .isInstanceOf(AccessDeniedException.class); + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + public static class AutowirePermissionEvaluatorConfig { + + @Bean + public PermissionEvaluator permissionEvaluator() { + return mock(PermissionEvaluator.class); + } + + @Bean + public MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + } + + @Test + public void multiPermissionEvaluatorConfig() throws Exception { + this.spring.register(MultiPermissionEvaluatorConfig.class).autowire(); + + // no exception + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + public static class MultiPermissionEvaluatorConfig { + + @Bean + public PermissionEvaluator permissionEvaluator() { + return mock(PermissionEvaluator.class); + } + + @Bean + public PermissionEvaluator permissionEvaluator2() { + return mock(PermissionEvaluator.class); + } + } + + // SEC-2425 + @Test + @WithMockUser + public void enableGlobalMethodSecurityWorksOnSuperclass() { + this.spring.register(ChildConfig.class).autowire(); + + assertThatThrownBy(() -> this.service.preAuthorize()) + .isInstanceOf(AccessDeniedException.class); + } + + @Configuration + static class ChildConfig extends ParentConfig {} + + @EnableGlobalMethodSecurity(prePostEnabled = true) + static class ParentConfig { + + @Bean + public MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + } + + // SEC-2479 + @Test + @WithMockUser + public void supportAuthenticationManagerInParent() { + try (AnnotationConfigWebApplicationContext parent = new AnnotationConfigWebApplicationContext()) { + parent.register(Sec2479ParentConfig.class); + parent.refresh(); + try (AnnotationConfigWebApplicationContext child = new AnnotationConfigWebApplicationContext()) { + child.setParent(parent); + child.register(Sec2479ChildConfig.class); + child.refresh(); + this.spring.context(child).autowire(); + + assertThatThrownBy(() -> this.service.preAuthorize()) + .isInstanceOf(AccessDeniedException.class); + } + } + } + + @Configuration + static class Sec2479ParentConfig { + @Bean + public AuthenticationManager am() { + return mock(AuthenticationManager.class); + } + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + static class Sec2479ChildConfig { + @Bean + public MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + } + + @Test + public void enableGlobalMethodSecurityDoesNotTriggerEagerInitializationOfBeansInGlobalAuthenticationConfigurer() { + this.spring.register(Sec2815Config.class).autowire(); + + MockBeanPostProcessor pp = this.spring.getContext().getBean(MockBeanPostProcessor.class); + + assertThat(pp.beforeInit).containsKeys("dataSource"); + assertThat(pp.afterInit).containsKeys("dataSource"); + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + static class Sec2815Config { + @Bean + public MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + + @Bean + public MockBeanPostProcessor mockBeanPostProcessor() { + return new MockBeanPostProcessor(); + } + + @Bean + public DataSource dataSource() { + return mock(DataSource.class); + } + + @Configuration + static class AuthConfig extends GlobalAuthenticationConfigurerAdapter { + @Autowired + DataSource dataSource; + + @Override + public void init(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication(); + } + } + } + + static class MockBeanPostProcessor implements BeanPostProcessor { + Map beforeInit = new HashMap(); + Map afterInit = new HashMap(); + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws + BeansException { + this.beforeInit.put(beanName, bean); + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + this.afterInit.put(beanName, bean); + return bean; + } + } + + // SEC-3045 + @Test + public void globalSecurityProxiesSecurity() { + this.spring.register(Sec3005Config.class).autowire(); + + assertThat(this.service.getClass()).matches(c-> !Proxy.isProxyClass(c), "is not proxy class"); + } + + @EnableGlobalMethodSecurity(prePostEnabled = true, mode= AdviceMode.ASPECTJ) + @EnableTransactionManagement + static class Sec3005Config { + @Bean + public MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication(); + } + } + + // + // // gh-3797 + // def preAuthorizeBeanSpel() { + // setup: + // SecurityContextHolder.getContext().setAuthentication( + // new TestingAuthenticationToken("user", "password","ROLE_USER")) + // context = new AnnotationConfigApplicationContext(PreAuthorizeBeanSpelConfig) + // BeanSpelService service = context.getBean(BeanSpelService) + // when: + // service.run(true) + // then: + // noExceptionThrown() + // when: + // service.run(false) + // then: + // thrown(AccessDeniedException) + // } + // + + @Test + @WithMockUser + public void preAuthorizeBeanSpel() { + this.spring.register(PreAuthorizeBeanSpelConfig.class).autowire(); + + assertThatThrownBy(() -> this.service.preAuthorizeBean(false)) + .isInstanceOf(AccessDeniedException.class); + + this.service.preAuthorizeBean(true); + } + + @Configuration + @EnableGlobalMethodSecurity(prePostEnabled = true) + public static class PreAuthorizeBeanSpelConfig { + @Bean + MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + + @Bean + Authz authz() { + return new Authz(); + } + } + + // gh-3394 + @Test + @WithMockUser + public void roleHierarchy() { + this.spring.register(RoleHierarchyConfig.class).autowire(); + + assertThatThrownBy(() -> this.service.preAuthorize()) + .isInstanceOf(AccessDeniedException.class); + this.service.preAuthorizeAdmin(); + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + @Configuration + public static class RoleHierarchyConfig { + @Bean + MethodSecurityService service() { + return new MethodSecurityServiceImpl(); + } + + @Bean + RoleHierarchy roleHierarchy() { + RoleHierarchyImpl result = new RoleHierarchyImpl(); + result.setHierarchy("ROLE_USER > ROLE_ADMIN"); + return result; + } + } + + @Test + @WithMockUser(authorities = "ROLE:USER") + public void grantedAuthorityDefaultsAutowires() { + this.spring.register(CustomGrantedAuthorityConfig.class).autowire(); + + CustomGrantedAuthorityConfig.CustomAuthorityService customService = this.spring.getContext().getBean( + CustomGrantedAuthorityConfig.CustomAuthorityService.class); + + assertThatThrownBy(() -> this.service.preAuthorize()) + .isInstanceOf(AccessDeniedException.class); + + customService.customPrefixRoleUser(); + // no exception + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + static class CustomGrantedAuthorityConfig { + + @Bean + public GrantedAuthorityDefaults ga() { + return new GrantedAuthorityDefaults("ROLE:"); + } + + @Bean + public CustomAuthorityService service() { + return new CustomAuthorityService(); + } + + @Bean + public MethodSecurityServiceImpl methodSecurityService() { + return new MethodSecurityServiceImpl(); + } + + static class CustomAuthorityService { + @PreAuthorize("hasRole('ROLE:USER')") + public void customPrefixRoleUser() {} + } + } +}