diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AuthorizedRequestsWithPostProcessorConfig.java b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AuthorizedRequestsWithPostProcessorConfig.java deleted file mode 100644 index 39b84812b2..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AuthorizedRequestsWithPostProcessorConfig.java +++ /dev/null @@ -1,52 +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 - * - * https://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.web.configurers; - -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.Bean; -import org.springframework.security.access.event.AuthorizedEvent; -import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; - -@EnableWebSecurity -public class AuthorizedRequestsWithPostProcessorConfig extends - WebSecurityConfigurerAdapter { - static ApplicationListener AL; - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().permitAll() - .withObjectPostProcessor(new ObjectPostProcessor() { - public O postProcess( - O fsi) { - fsi.setPublishAuthorizationSuccess(true); - return fsi; - } - }); - } - // @formatter:on - - @Bean - public ApplicationListener applicationListener() { - return AL; - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerConfigs.java b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerConfigs.java deleted file mode 100644 index 1aa08f78ac..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerConfigs.java +++ /dev/null @@ -1,162 +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 - * - * https://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.web.configurers; - -import java.util.Arrays; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.security.access.AccessDecisionVoter; -import org.springframework.security.access.expression.SecurityExpressionHandler; -import org.springframework.security.access.expression.SecurityExpressionOperations; -import org.springframework.security.access.vote.AffirmativeBased; -import org.springframework.security.authentication.AuthenticationTrustResolverImpl; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.FilterInvocation; -import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; -import org.springframework.security.web.access.expression.WebExpressionVoter; -import org.springframework.security.web.access.expression.WebSecurityExpressionRoot; - -/** - * - * @author Rob Winch - * - */ -public class ExpressionUrlAuthorizationConfigurerConfigs { - - /** - * Ensure that All additional properties properly compile and chain properly - */ - @EnableWebSecurity - static class AllPropertiesWorkConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @SuppressWarnings("rawtypes") - @Override - protected void configure(HttpSecurity http) throws Exception { - SecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler(); - WebExpressionVoter expressionVoter = new WebExpressionVoter(); - AffirmativeBased adm = new AffirmativeBased(Arrays.>asList(expressionVoter)); - http - .authorizeRequests() - .expressionHandler(handler) - .accessDecisionManager(adm) - .filterSecurityInterceptorOncePerRequest(true) - .antMatchers("/a", "/b").hasRole("ADMIN") - .anyRequest().permitAll() - .and() - .formLogin(); - } - // @formatter:on - } - - @EnableWebSecurity - static class UseBeansInExpressions extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER"); - } - // @formatter:on - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/admin/**").hasRole("ADMIN") - .antMatchers("/user/**").hasRole("USER") - .antMatchers("/allow/**").access("@permission.check(authentication,'user')") - .anyRequest().access("@permission.check(authentication,'admin')"); - } - // @formatter:on - - @Bean - public Checker permission() { - return new Checker(); - } - - static class Checker { - public boolean check(Authentication authentication, String customArg) { - return authentication.getName().contains(customArg); - } - } - } - - @EnableWebSecurity - static class CustomExpressionRootConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER"); - } - // @formatter:on - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .expressionHandler(expressionHandler()) - .antMatchers("/admin/**").hasRole("ADMIN") - .antMatchers("/user/**").hasRole("USER") - .antMatchers("/allow/**").access("check('user')") - .anyRequest().access("check('admin')"); - } - // @formatter:on - - @Bean - public CustomExpressionHandler expressionHandler() { - return new CustomExpressionHandler(); - } - - static class CustomExpressionHandler extends DefaultWebSecurityExpressionHandler { - - @Override - protected SecurityExpressionOperations createSecurityExpressionRoot( - Authentication authentication, FilterInvocation fi) { - WebSecurityExpressionRoot root = new CustomExpressionRoot(authentication, - fi); - root.setPermissionEvaluator(getPermissionEvaluator()); - root.setTrustResolver(new AuthenticationTrustResolverImpl()); - root.setRoleHierarchy(getRoleHierarchy()); - return root; - } - } - - static class CustomExpressionRoot extends WebSecurityExpressionRoot { - - public CustomExpressionRoot(Authentication a, FilterInvocation fi) { - super(a, fi); - } - - public boolean check(String customArg) { - Authentication auth = this.getAuthentication(); - return auth.getName().contains(customArg); - } - } - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy deleted file mode 100644 index 1d74beabff..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright 2002-2016 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 - * - * https://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.web.configurers - -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; - -import static org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurerConfigs.* - -import javax.servlet.http.HttpServletResponse - -import org.springframework.beans.BeansException -import org.springframework.beans.factory.BeanCreationException -import org.springframework.beans.factory.config.BeanPostProcessor -import org.springframework.context.ApplicationListener -import org.springframework.context.annotation.Bean -import org.springframework.security.access.AccessDecisionManager; -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.event.AuthorizedEvent -import org.springframework.security.access.vote.AffirmativeBased -import org.springframework.security.authentication.RememberMeAuthenticationToken -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.SecurityExpressions.* -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurerConfigs.CustomExpressionRootConfig -import org.springframework.security.core.Authentication -import org.springframework.security.core.authority.AuthorityUtils -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor - -public class ExpressionUrlAuthorizationConfigurerTests extends BaseSpringSpec { - - def "hasAnyAuthority('ROLE_USER')"() { - when: - def expression = ExpressionUrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER") - then: - expression == "hasAnyAuthority('ROLE_USER')" - } - - def "hasAnyAuthority('ROLE_USER','ROLE_ADMIN')"() { - when: - def expression = ExpressionUrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER","ROLE_ADMIN") - then: - expression == "hasAnyAuthority('ROLE_USER','ROLE_ADMIN')" - } - - def "hasAnyRole('USER')"() { - when: - def expression = ExpressionUrlAuthorizationConfigurer.hasAnyRole("USER") - then: - expression == "hasAnyRole('ROLE_USER')" - } - - def "hasAnyRole('USER','ADMIN')"() { - when: - def expression = ExpressionUrlAuthorizationConfigurer.hasAnyRole("USER","ADMIN") - then: - expression == "hasAnyRole('ROLE_USER','ROLE_ADMIN')" - } - - def "hasRole('ROLE_USER') is rejected due to starting with ROLE_"() { - when: - def expression = ExpressionUrlAuthorizationConfigurer.hasRole("ROLE_USER") - then: - IllegalArgumentException e = thrown() - e.message == "role should not start with 'ROLE_' since it is automatically inserted. Got 'ROLE_USER'" - } - - def "authorizeRequests() uses AffirmativeBased AccessDecisionManager"() { - when: "Load Config with no specific AccessDecisionManager" - loadConfig(NoSpecificAccessDecessionManagerConfig) - then: "AccessDecessionManager matches the HttpSecurityBuilder's default" - findFilter(FilterSecurityInterceptor).accessDecisionManager.class == AffirmativeBased - } - - @EnableWebSecurity - static class NoSpecificAccessDecessionManagerConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - } - } - - def "authorizeRequests() no requests"() { - when: "Load Config with no requests" - loadConfig(NoRequestsConfig) - then: "A meaningful exception is thrown" - BeanCreationException success = thrown() - success.message.contains "At least one mapping is required (i.e. authorizeRequests().anyRequest().authenticated())" - } - - @EnableWebSecurity - static class NoRequestsConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - } - } - - def "authorizeRequests() incomplete mapping"() { - when: "Load Config with incomplete mapping" - loadConfig(IncompleteMappingConfig) - then: "A meaningful exception is thrown" - BeanCreationException success = thrown() - success.message.contains "An incomplete mapping was found for " - } - - @EnableWebSecurity - static class IncompleteMappingConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/a").authenticated() - .anyRequest() - } - } - - def "authorizeRequests() hasAuthority"() { - setup: - loadConfig(HasAuthorityConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_UNAUTHORIZED - when: - super.setup() - login() - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - when: - super.setup() - login("user","ROLE_INVALID") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_FORBIDDEN - } - - @EnableWebSecurity - static class HasAuthorityConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().hasAuthority("ROLE_USER") - } - } - - def "authorizeRequests() hasAnyAuthority"() { - setup: - loadConfig(HasAnyAuthorityConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_UNAUTHORIZED - when: - super.setup() - login("user","ROLE_ADMIN") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - when: - super.setup() - login("user","ROLE_DBA") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - when: - super.setup() - login("user","ROLE_INVALID") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_FORBIDDEN - } - - @EnableWebSecurity - static class HasAnyAuthorityConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().hasAnyAuthority("ROLE_ADMIN","ROLE_DBA") - } - } - - def "authorizeRequests() hasIpAddress"() { - setup: - loadConfig(HasIpAddressConfig) - when: - request.remoteAddr = "192.168.1.1" - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_UNAUTHORIZED - when: - super.setup() - request.remoteAddr = "192.168.1.0" - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - } - - @EnableWebSecurity - static class HasIpAddressConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().hasIpAddress("192.168.1.0") - } - } - - def "authorizeRequests() anonymous"() { - setup: - loadConfig(AnonymousConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - when: - super.setup() - login() - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_FORBIDDEN - } - - @EnableWebSecurity - static class AnonymousConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().anonymous() - } - } - - def "authorizeRequests() rememberMe"() { - setup: - loadConfig(RememberMeConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_UNAUTHORIZED - when: - super.setup() - login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER"))) - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - } - - @EnableWebSecurity - static class RememberMeConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .rememberMe() - .and() - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().rememberMe() - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER") - } - } - - def "authorizeRequests() denyAll"() { - setup: - loadConfig(DenyAllConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_UNAUTHORIZED - when: - super.setup() - login(new UsernamePasswordAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER"))) - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_FORBIDDEN - } - - @EnableWebSecurity - static class DenyAllConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().denyAll() - } - } - - def "authorizeRequests() not denyAll"() { - setup: - loadConfig(NotDenyAllConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - when: - super.setup() - login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER"))) - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - } - - @EnableWebSecurity - static class NotDenyAllConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().not().denyAll() - } - } - - def "authorizeRequests() fullyAuthenticated"() { - setup: - loadConfig(FullyAuthenticatedConfig) - when: - super.setup() - login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER"))) - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_UNAUTHORIZED - when: - super.setup() - login() - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - } - - @EnableWebSecurity - static class FullyAuthenticatedConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .rememberMe() - .and() - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().fullyAuthenticated() - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER") - } - } - - def "authorizeRequests() access"() { - setup: - loadConfig(AccessConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: "Access is granted due to GET" - response.status == HttpServletResponse.SC_OK - when: - super.setup() - login() - request.method = "POST" - springSecurityFilterChain.doFilter(request,response,chain) - then: "Access is granted due to role" - response.status == HttpServletResponse.SC_OK - when: - super.setup() - request.method = "POST" - springSecurityFilterChain.doFilter(request,response,chain) - then: "Access is denied" - response.status == HttpServletResponse.SC_UNAUTHORIZED - } - - @EnableWebSecurity - static class AccessConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .rememberMe() - .and() - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().access("hasRole('ROLE_USER') or request.method == 'GET'") - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER") - } - } - - - def "invoke authorizeUrls twice does not reset"() { - setup: - loadConfig(InvokeTwiceDoesNotResetConfig) - when: - request.method = "POST" - springSecurityFilterChain.doFilter(request,response,chain) - then: "Access is denied" - response.status == HttpServletResponse.SC_UNAUTHORIZED - } - - @EnableWebSecurity - static class InvokeTwiceDoesNotResetConfig extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .httpBasic() - .and() - .authorizeRequests() - .anyRequest().authenticated() - .and() - .authorizeRequests() - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - } - } - - def "All Properties are accessible and chain properly"() { - when: - loadConfig(AllPropertiesWorkConfig) - then: - noExceptionThrown() - } - - def "AuthorizedRequests withPostProcessor"() { - setup: - ApplicationListener al = Mock() - AuthorizedRequestsWithPostProcessorConfig.AL = al - loadConfig(AuthorizedRequestsWithPostProcessorConfig) - when: - springSecurityFilterChain.doFilter(request, response, chain) - then: - 1 * al.onApplicationEvent(_ as AuthorizedEvent) - } - - def "Use @permission.check in access"() { - setup: - loadConfig(UseBeansInExpressions) - when: "invoke standard expression that denies access" - login() - request.servletPath = "/admin/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "standard expression works - get forbidden" - response.status == HttpServletResponse.SC_FORBIDDEN - when: "invoke standard expression that allows access" - super.setup() - login() - request.servletPath = "/user/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "standard expression works - get ok" - response.status == HttpServletResponse.SC_OK - when: "invoke custom bean as expression that allows access" - super.setup() - login() - request.servletPath = "/allow/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "custom bean expression allows access" - response.status == HttpServletResponse.SC_OK - when: "invoke custom bean as expression that denies access" - super.setup() - login() - request.servletPath = "/deny/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "custom bean expression denies access" - response.status == HttpServletResponse.SC_FORBIDDEN - } - - def "Use custom expressionroot in access"() { - setup: - loadConfig(CustomExpressionRootConfig) - when: "invoke standard expression that denies access" - login() - request.servletPath = "/admin/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "standard expression works - get forbidden" - response.status == HttpServletResponse.SC_FORBIDDEN - when: "invoke standard expression that allows access" - super.setup() - login() - request.servletPath = "/user/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "standard expression works - get ok" - response.status == HttpServletResponse.SC_OK - when: "invoke custom bean as expression that allows access" - super.setup() - login() - request.servletPath = "/allow/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "custom bean expression allows access" - response.status == HttpServletResponse.SC_OK - when: "invoke custom bean as expression that denies access" - super.setup() - login() - request.servletPath = "/deny/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "custom bean expression denies access" - response.status == HttpServletResponse.SC_FORBIDDEN - } - - def "SEC-3011: Default AccessDecisionManager postProcessed"() { - when: - loadConfig(Sec3011Config) - then: - context.getBean(MockBeanPostProcessor).beans.find { it instanceof AccessDecisionManager } - } - - @EnableWebSecurity - static class Sec3011Config extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated(); - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication(); - } - - @Bean - static MockBeanPostProcessor mbpp() { - return new MockBeanPostProcessor(); - } - } - - static class MockBeanPostProcessor implements BeanPostProcessor { - List beans = new ArrayList(); - - public Object postProcessBeforeInitialization(Object bean, - String beanName) throws BeansException { - beans.add(bean); - return bean; - } - - public Object postProcessAfterInitialization(Object bean, - String beanName) throws BeansException { - - return bean; - } - - } - - def "permissionEvaluator autowired"() { - setup: - loadConfig(PermissionEvaluatorConfig) - when: "invoke hasPermission expression that allows access" - super.setup() - login() - request.servletPath = "/allow/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "permissionEvaluator with id and type works - allows access" - response.status == HttpServletResponse.SC_OK - when: "invoke hasPermission expression that denies access" - super.setup() - login() - request.servletPath = "/deny/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "permissionEvaluator with id and type works - denies access" - response.status == HttpServletResponse.SC_FORBIDDEN - when: "invoke hasPermission expression that allows access" - super.setup() - login() - request.servletPath = "/allowObject/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "permissionEvaluator with object works - allows access" - response.status == HttpServletResponse.SC_OK - when: "invoke hasPermission expression that denies access" - super.setup() - login() - request.servletPath = "/denyObject/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "permissionEvaluator with object works - denies access" - response.status == HttpServletResponse.SC_FORBIDDEN - } - - @EnableWebSecurity - static class PermissionEvaluatorConfig extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/allow/**").access("hasPermission('ID', 'TYPE', 'PERMISSION')") - .antMatchers("/allowObject/**").access("hasPermission('TESTOBJ', 'PERMISSION')") - .antMatchers("/deny/**").access("hasPermission('ID', 'TYPE', 'NO PERMISSION')") - .antMatchers("/denyObject/**").access("hasPermission('TESTOBJ', 'NO PERMISSION')") - .anyRequest().permitAll(); - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER") - } - - @Bean - public PermissionEvaluator permissionEvaluator(){ - return new PermissionEvaluator(){ - @Override - public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { - return "TESTOBJ".equals(targetDomainObject) && "PERMISSION".equals(permission); - } - @Override - public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, - Object permission) { - return "ID".equals(targetId) && "TYPE".equals(targetType) && "PERMISSION".equals(permission); - } - }; - } - - } - - @EnableWebSecurity - static class RoleHierarchyConfig extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/allow/**").access("hasRole('XXX')") - .antMatchers("/deny/**").access("hasRole('NOPE')") - .anyRequest().permitAll(); - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER") - } - - @Bean - public RoleHierarchy roleHierarchy(){ - return new RoleHierarchyImpl("USER > XXX"); - } - - } - - def "roleHierarchy autowired"() { - setup: - loadConfig(PermissionEvaluatorConfig) - when: "invoke roleHierarchy expression that allows access" - super.setup() - login() - request.servletPath = "/allow/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "permissionEvaluator with id and type works - allows access" - response.status == HttpServletResponse.SC_OK - when: "invoke roleHierarchy expression that denies access" - super.setup() - login() - request.servletPath = "/deny/1" - springSecurityFilterChain.doFilter(request, response, chain) - then: "permissionEvaluator with id and type works - denies access" - response.status == HttpServletResponse.SC_FORBIDDEN - } - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java new file mode 100644 index 0000000000..cda09431f1 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java @@ -0,0 +1,1028 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.web.configurers; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.access.PermissionEvaluator; +import org.springframework.security.access.event.AuthorizedEvent; +import org.springframework.security.access.expression.SecurityExpressionHandler; +import org.springframework.security.access.expression.SecurityExpressionOperations; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; +import org.springframework.security.access.vote.AffirmativeBased; +import org.springframework.security.authentication.AuthenticationTrustResolverImpl; +import org.springframework.security.authentication.RememberMeAuthenticationToken; +import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; +import org.springframework.security.web.access.expression.WebExpressionVoter; +import org.springframework.security.web.access.expression.WebSecurityExpressionRoot; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.Serializable; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for {@link ExpressionUrlAuthorizationConfigurer} + * + * @author Rob Winch + * @author Eleftheria Stein + */ +public class ExpressionUrlAuthorizationConfigurerTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void configureWhenHasRoleStartingWithStringRoleThenException() { + assertThatThrownBy(() -> this.spring.register(HasRoleStartingWithRoleConfig.class).autowire()) + .isInstanceOf(BeanCreationException.class) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("role should not start with 'ROLE_' since it is automatically inserted. Got 'ROLE_USER'"); + } + + @EnableWebSecurity + static class HasRoleStartingWithRoleConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("ROLE_USER"); + // @formatter:on + } + } + + @Test + public void configureWhenNoCustomAccessDecisionManagerThenUsesAffirmativeBased() { + this.spring.register(NoSpecificAccessDecisionManagerConfig.class).autowire(); + + verify(NoSpecificAccessDecisionManagerConfig.objectPostProcessor) + .postProcess(any(AffirmativeBased.class)); + } + + @EnableWebSecurity + static class NoSpecificAccessDecisionManagerConfig extends WebSecurityConfigurerAdapter { + static ObjectPostProcessor objectPostProcessor = spy(ReflectingObjectPostProcessor.class); + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("USER"); + // @formatter:on + } + + @Bean + static ObjectPostProcessor objectPostProcessor() { + return objectPostProcessor; + } + } + + @Test + public void configureWhenAuthorizedRequestsAndNoRequestsThenException() { + assertThatThrownBy(() -> this.spring.register(NoRequestsConfig.class).autowire()) + .isInstanceOf(BeanCreationException.class) + .hasMessageContaining("At least one mapping is required (i.e. authorizeRequests().anyRequest().authenticated())"); + } + + @EnableWebSecurity + static class NoRequestsConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests(); + // @formatter:on + } + } + + @Test + public void configureWhenAnyRequestIncompleteMappingThenException() { + assertThatThrownBy(() -> this.spring.register(IncompleteMappingConfig.class).autowire()) + .isInstanceOf(BeanCreationException.class) + .hasMessageContaining("An incomplete mapping was found for "); + } + + @EnableWebSecurity + static class IncompleteMappingConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .antMatchers("/a").authenticated() + .anyRequest(); + // @formatter:on + } + } + + @Test + public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() + throws Exception { + this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").authorities(new SimpleGrantedAuthority("ROLE_USER")))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden() + throws Exception { + this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").authorities(new SimpleGrantedAuthority("ROLE_ADMIN")))) + .andExpect(status().isForbidden()); + } + + @Test + public void getWhenHasAnyAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() + throws Exception { + this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isUnauthorized()); + } + + @EnableWebSecurity + static class RoleUserAnyAuthorityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().hasAnyAuthority("ROLE_USER"); + // @formatter:on + } + } + + @Test + public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() + throws Exception { + this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").authorities(new SimpleGrantedAuthority("ROLE_USER")))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden() + throws Exception { + this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").authorities(new SimpleGrantedAuthority("ROLE_ADMIN")))) + .andExpect(status().isForbidden()); + } + + @Test + public void getWhenHasAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() + throws Exception { + this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isUnauthorized()); + } + + @EnableWebSecurity + static class RoleUserAuthorityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().hasAuthority("ROLE_USER"); + // @formatter:on + } + } + + @Test + public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleUserThenRespondsWithOk() + throws Exception { + this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").authorities(new SimpleGrantedAuthority("ROLE_USER")))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleAdminThenRespondsWithOk() + throws Exception { + this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").authorities(new SimpleGrantedAuthority("ROLE_ADMIN")))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleOtherThenRespondsWithForbidden() + throws Exception { + this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").authorities(new SimpleGrantedAuthority("ROLE_OTHER")))) + .andExpect(status().isForbidden()); + } + + @Test + public void getWhenAuthorityRoleUserOrAdminAuthRequiredAndNoUserThenRespondsWithUnauthorized() + throws Exception { + this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isUnauthorized()); + } + + @EnableWebSecurity + static class RoleUserOrRoleAdminAuthorityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().hasAnyAuthority("ROLE_USER", "ROLE_ADMIN"); + // @formatter:on + } + } + + @Test + public void getWhenHasAnyRoleUserConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { + this.spring.register(RoleUserConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenHasAnyRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidden() throws Exception { + this.spring.register(RoleUserConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").roles("ADMIN"))) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class RoleUserConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasAnyRole("USER"); + // @formatter:on + } + } + + @Test + public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { + this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenRoleUserOrAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception { + this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").roles("ADMIN"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbidden() throws Exception { + this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").roles("OTHER"))) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class RoleUserOrAdminConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasAnyRole("USER", "ADMIN"); + // @formatter:on + } + } + + @Test + public void getWhenHasIpAddressConfiguredAndIpAddressMatchesThenRespondsWithOk() throws Exception { + this.spring.register(HasIpAddressConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(request -> { + request.setRemoteAddr("192.168.1.0"); + return request; + })) + .andExpect(status().isOk()); + } + + @Test + public void getWhenHasIpAddressConfiguredAndIpAddressDoesNotMatchThenRespondsWithUnauthorized() throws Exception { + this.spring.register(HasIpAddressConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(request -> { + request.setRemoteAddr("192.168.1.1"); + return request; + })) + .andExpect(status().isUnauthorized()); + } + + @EnableWebSecurity + static class HasIpAddressConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().hasIpAddress("192.168.1.0"); + // @formatter:on + } + } + + @Test + public void getWhenAnonymousConfiguredAndAnonymousUserThenRespondsWithOk() throws Exception { + this.spring.register(AnonymousConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isOk()); + } + + @Test + public void getWhenAnonymousConfiguredAndLoggedInUserThenRespondsWithForbidden() throws Exception { + this.spring.register(AnonymousConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user"))) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class AnonymousConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().anonymous(); + // @formatter:on + } + } + + @Test + public void getWhenRememberMeConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception { + this.spring.register(RememberMeConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isUnauthorized()); + } + + @Test + public void getWhenRememberMeConfiguredAndRememberMeTokenThenRespondsWithOk() throws Exception { + this.spring.register(RememberMeConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(authentication(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER"))))) + .andExpect(status().isOk()); + } + + @EnableWebSecurity + static class RememberMeConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .rememberMe() + .and() + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().rememberMe(); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + // @formatter:on + } + } + + @Test + public void getWhenDenyAllConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception { + this.spring.register(DenyAllConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isUnauthorized()); + } + + @Test + public void getWheDenyAllConfiguredAndUserLoggedInThenRespondsWithForbidden() throws Exception { + this.spring.register(DenyAllConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").roles("USER"))) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class DenyAllConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().denyAll(); + // @formatter:on + } + } + + @Test + public void getWhenNotDenyAllConfiguredAndNoUserThenRespondsWithOk() throws Exception { + this.spring.register(NotDenyAllConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isOk()); + } + + @Test + public void getWhenNotDenyAllConfiguredAndRememberMeTokenThenRespondsWithOk() throws Exception { + this.spring.register(NotDenyAllConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(authentication(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER"))))) + .andExpect(status().isOk()); + } + + @EnableWebSecurity + static class NotDenyAllConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().not().denyAll(); + // @formatter:on + } + } + + @Test + public void getWhenFullyAuthenticatedConfiguredAndRememberMeTokenThenRespondsWithUnauthorized() throws Exception { + this.spring.register(FullyAuthenticatedConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(authentication(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER"))))) + .andExpect(status().isUnauthorized()); + } + + @Test + public void getWhenFullyAuthenticatedConfiguredAndUserThenRespondsWithOk() throws Exception { + this.spring.register(FullyAuthenticatedConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @EnableWebSecurity + static class FullyAuthenticatedConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .rememberMe() + .and() + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().fullyAuthenticated(); + // @formatter:on + } + } + + @Test + public void getWhenAccessRoleUserOrGetRequestConfiguredThenRespondsWithOk() throws Exception { + this.spring.register(AccessConfig.class, BasicController.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(status().isOk()); + } + + @Test + public void postWhenAccessRoleUserOrGetRequestConfiguredAndRoleUserThenRespondsWithOk() throws Exception { + this.spring.register(AccessConfig.class, BasicController.class).autowire(); + + this.mvc.perform(post("/") + .with(csrf()) + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void postWhenAccessRoleUserOrGetRequestConfiguredThenRespondsWithUnauthorized() throws Exception { + this.spring.register(AccessConfig.class, BasicController.class).autowire(); + + this.mvc.perform(post("/") + .with(csrf())) + .andExpect(status().isUnauthorized()); + } + + @EnableWebSecurity + static class AccessConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .rememberMe() + .and() + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().access("hasRole('ROLE_USER') or request.method == 'GET'"); + // @formatter:on + } + } + + @Test + public void authorizeRequestsWhenInvokedTwiceThenUsesOriginalConfiguration() throws Exception { + this.spring.register(InvokeTwiceDoesNotResetConfig.class, BasicController.class).autowire(); + + this.mvc.perform(post("/") + .with(csrf())) + .andExpect(status().isUnauthorized()); + } + + @EnableWebSecurity + static class InvokeTwiceDoesNotResetConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeRequests() + .anyRequest().authenticated() + .and() + .authorizeRequests(); + // @formatter:on + } + } + + @Test + public void configureWhenUsingAllAuthorizeRequestPropertiesThenCompiles() { + this.spring.register(AllPropertiesWorkConfig.class).autowire(); + } + + @EnableWebSecurity + static class AllPropertiesWorkConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + SecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler(); + WebExpressionVoter expressionVoter = new WebExpressionVoter(); + AffirmativeBased adm = new AffirmativeBased(Collections.singletonList(expressionVoter)); + // @formatter:off + http + .authorizeRequests() + .expressionHandler(handler) + .accessDecisionManager(adm) + .filterSecurityInterceptorOncePerRequest(true) + .antMatchers("/a", "/b").hasRole("ADMIN") + .anyRequest().permitAll() + .and() + .formLogin(); + // @formatter:on + } + } + + @Test + public void configureWhenRegisteringObjectPostProcessorThenApplicationListenerInvokedOnAuthorizedEvent() + throws Exception { + this.spring.register(AuthorizedRequestsWithPostProcessorConfig.class).autowire(); + + this.mvc.perform(get("/")); + + verify(AuthorizedRequestsWithPostProcessorConfig.AL).onApplicationEvent(any(AuthorizedEvent.class)); + } + + @EnableWebSecurity + static class AuthorizedRequestsWithPostProcessorConfig extends WebSecurityConfigurerAdapter { + static ApplicationListener AL = mock(ApplicationListener.class); + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().permitAll() + .withObjectPostProcessor(new ObjectPostProcessor() { + public O postProcess( + O fsi) { + fsi.setPublishAuthorizationSuccess(true); + return fsi; + } + }); + // @formatter:on + } + + @Bean + public ApplicationListener applicationListener() { + return AL; + } + } + + @Test + public void getWhenPermissionCheckAndRoleDoesNotMatchThenRespondsWithForbidden() throws Exception { + this.spring.register(UseBeansInExpressions.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/admin") + .with(user("user").roles("USER"))) + .andExpect(status().isForbidden()); + } + + @Test + public void getWhenPermissionCheckAndRoleMatchesThenRespondsWithOk() throws Exception { + this.spring.register(UseBeansInExpressions.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/user") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenPermissionCheckAndAuthenticationNameMatchesThenRespondsWithOk() throws Exception { + this.spring.register(UseBeansInExpressions.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/allow") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenPermissionCheckAndAuthenticationNameDoesNotMatchThenRespondsWithForbidden() throws Exception { + this.spring.register(UseBeansInExpressions.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/deny") + .with(user("user").roles("USER"))) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class UseBeansInExpressions extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .antMatchers("/admin").hasRole("ADMIN") + .antMatchers("/user").hasRole("USER") + .antMatchers("/allow").access("@permission.check(authentication,'user')") + .anyRequest().access("@permission.check(authentication,'admin')"); + // @formatter:on + } + + @Bean + public Checker permission() { + return new Checker(); + } + + static class Checker { + public boolean check(Authentication authentication, String customArg) { + return authentication.getName().contains(customArg); + } + } + } + + @Test + public void getWhenCustomExpressionHandlerAndRoleDoesNotMatchThenRespondsWithForbidden() + throws Exception { + this.spring.register(CustomExpressionRootConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/admin") + .with(user("user").roles("USER"))) + .andExpect(status().isForbidden()); + } + + @Test + public void getWhenCustomExpressionHandlerAndRoleMatchesThenRespondsWithOk() + throws Exception { + this.spring.register(CustomExpressionRootConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/user") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenCustomExpressionHandlerAndAuthenticationNameMatchesThenRespondsWithOk() + throws Exception { + this.spring.register(CustomExpressionRootConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/allow") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenCustomExpressionHandlerAndAuthenticationNameDoesNotMatchThenRespondsWithForbidden() + throws Exception { + this.spring.register(CustomExpressionRootConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/deny") + .with(user("user").roles("USER"))) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class CustomExpressionRootConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .expressionHandler(expressionHandler()) + .antMatchers("/admin").hasRole("ADMIN") + .antMatchers("/user").hasRole("USER") + .antMatchers("/allow").access("check('user')") + .anyRequest().access("check('admin')"); + // @formatter:on + } + + @Bean + public CustomExpressionHandler expressionHandler() { + return new CustomExpressionHandler(); + } + + static class CustomExpressionHandler extends DefaultWebSecurityExpressionHandler { + + @Override + protected SecurityExpressionOperations createSecurityExpressionRoot( + Authentication authentication, FilterInvocation fi) { + WebSecurityExpressionRoot root = new CustomExpressionRoot(authentication, fi); + root.setPermissionEvaluator(getPermissionEvaluator()); + root.setTrustResolver(new AuthenticationTrustResolverImpl()); + root.setRoleHierarchy(getRoleHierarchy()); + return root; + } + } + + static class CustomExpressionRoot extends WebSecurityExpressionRoot { + + public CustomExpressionRoot(Authentication a, FilterInvocation fi) { + super(a, fi); + } + + public boolean check(String customArg) { + Authentication auth = this.getAuthentication(); + return auth.getName().contains(customArg); + } + } + } + + //SEC-3011 + @Test + public void configureWhenRegisteringObjectPostProcessorThenInvokedOnAccessDecisionManager() { + this.spring.register(Sec3011Config.class).autowire(); + + verify(Sec3011Config.objectPostProcessor) + .postProcess(any(AccessDecisionManager.class)); + } + + @EnableWebSecurity + static class Sec3011Config extends WebSecurityConfigurerAdapter { + static ObjectPostProcessor objectPostProcessor = spy(ReflectingObjectPostProcessor.class); + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().authenticated(); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication(); + // @formatter:on + } + + @Bean + static ObjectPostProcessor objectPostProcessor() { + return objectPostProcessor; + } + } + + @Test + public void getWhenRegisteringPermissionEvaluatorAndPermissionWithIdAndTypeMatchesThenRespondsWithOk() + throws Exception { + this.spring.register(PermissionEvaluatorConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/allow")) + .andExpect(status().isOk()); + } + + @Test + public void getWhenRegisteringPermissionEvaluatorAndPermissionWithIdAndTypeDoesNotMatchThenRespondsWithForbidden() + throws Exception { + this.spring.register(PermissionEvaluatorConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/deny")) + .andExpect(status().isForbidden()); + } + + @Test + public void getWhenRegisteringPermissionEvaluatorAndPermissionWithObjectMatchesThenRespondsWithOk() + throws Exception { + this.spring.register(PermissionEvaluatorConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/allowObject")) + .andExpect(status().isOk()); + } + + @Test + public void getWhenRegisteringPermissionEvaluatorAndPermissionWithObjectDoesNotMatchThenRespondsWithForbidden() + throws Exception { + this.spring.register(PermissionEvaluatorConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/denyObject")) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class PermissionEvaluatorConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .antMatchers("/allow").access("hasPermission('ID', 'TYPE', 'PERMISSION')") + .antMatchers("/allowObject").access("hasPermission('TESTOBJ', 'PERMISSION')") + .antMatchers("/deny").access("hasPermission('ID', 'TYPE', 'NO PERMISSION')") + .antMatchers("/denyObject").access("hasPermission('TESTOBJ', 'NO PERMISSION')") + .anyRequest().permitAll(); + // @formatter:on + } + + @Bean + public PermissionEvaluator permissionEvaluator() { + return new PermissionEvaluator() { + @Override + public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { + return "TESTOBJ".equals(targetDomainObject) && "PERMISSION".equals(permission); + } + + @Override + public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, + Object permission) { + return "ID".equals(targetId) && "TYPE".equals(targetType) && "PERMISSION".equals(permission); + } + }; + } + } + + @Test + public void getWhenRegisteringRoleHierarchyAndRelatedRoleAllowedThenRespondsWithOk() throws Exception { + this.spring.register(RoleHierarchyConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/allow") + .with(user("user").roles("USER"))) + .andExpect(status().isOk()); + } + + @Test + public void getWhenRegisteringRoleHierarchyAndNoRelatedRolesAllowedThenRespondsWithForbidden() throws Exception { + this.spring.register(RoleHierarchyConfig.class, WildcardController.class).autowire(); + + this.mvc.perform(get("/deny") + .with(user("user").roles("USER"))) + .andExpect(status().isForbidden()); + } + + @EnableWebSecurity + static class RoleHierarchyConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .antMatchers("/allow").access("hasRole('MEMBER')") + .antMatchers("/deny").access("hasRole('ADMIN')") + .anyRequest().permitAll(); + // @formatter:on + } + + @Bean + public RoleHierarchy roleHierarchy() { + RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); + roleHierarchy.setHierarchy("ROLE_USER > ROLE_MEMBER"); + return roleHierarchy; + } + } + + @RestController + static class BasicController { + @GetMapping("/") + public void rootGet() { + } + + @PostMapping("/") + public void rootPost() { + } + } + + @RestController + static class WildcardController { + @GetMapping("/{path}") + public void wildcard(@PathVariable String path) { + } + } + + static class ReflectingObjectPostProcessor implements ObjectPostProcessor { + @Override + public O postProcess(O object) { + return object; + } + } +}