From c872a77ad1cad832e933cd24f6920299dfb15648 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 19 Apr 2016 11:47:38 -0500 Subject: [PATCH] RoleHiearchy Bean used in GlobalMethodSecurity (#3394) Previously it required quite a bit of extra work to use RoleHiearchy within Java Based Spring Security configuration. Now if a single RoleHiearchy Bean is defined it will automatically be picked up and used by method security. Fixes gh-3394 --- .../GlobalMethodSecurityConfiguration.java | 6 +++ .../ExpressionUrlAuthorizationConfigurer.java | 10 ++++ ...balMethodSecurityConfigurationTests.groovy | 26 ++++++++++ .../MethodSecurityService.groovy | 3 ++ .../MethodSecurityServiceImpl.groovy | 4 ++ .../configurers/AuthorizeRequestsTests.java | 50 +++++++++++++++++++ 6 files changed, 99 insertions(+) diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java index b579960a71..f1a7b7e803 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java @@ -43,6 +43,7 @@ import org.springframework.security.access.expression.method.ExpressionBasedAnno import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice; import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.intercept.AfterInvocationManager; import org.springframework.security.access.intercept.AfterInvocationProviderManager; import org.springframework.security.access.intercept.RunAsManager; @@ -333,6 +334,11 @@ public class GlobalMethodSecurityConfiguration implements ImportAware { this.defaultMethodExpressionHandler.setTrustResolver(trustResolver); } + @Autowired(required = false) + public void setRoleHierarchy(RoleHierarchy roleHierarchy){ + this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy); + } + @Autowired(required = false) public void setObjectPostProcessor(ObjectPostProcessor objectPostProcessor) { this.objectPostProcessor = objectPostProcessor; diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java index f9c7fdeccf..634ae6b14c 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java @@ -20,10 +20,12 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; +import org.springframework.context.ApplicationContext; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.expression.SecurityExpressionHandler; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; @@ -188,6 +190,14 @@ public final class ExpressionUrlAuthorizationConfigurer ROLE_ADMIN") + } + } } diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.groovy index f4bc422e7e..280f55a4b0 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.groovy @@ -51,6 +51,9 @@ public interface MethodSecurityService { @PreAuthorize("permitAll") public String preAuthorizePermitAll(); + @PreAuthorize("hasRole('ADMIN')") + public void preAuthorizeAdmin(); + @PreAuthorize("hasPermission(#object,'read')") public String hasPermission(String object); diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.groovy index fb3787a5fb..26e14d5895 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.groovy @@ -55,6 +55,10 @@ public class MethodSecurityServiceImpl implements MethodSecurityService { return SecurityContextHolder.getContext().getAuthentication(); } + @Override + public void preAuthorizeAdmin() { + } + @Override public String preAuthorizePermitAll() { return null; diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeRequestsTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeRequestsTests.java index 4267ef23f5..8233d78d00 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeRequestsTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeRequestsTests.java @@ -22,16 +22,24 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 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.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -198,6 +206,48 @@ public class AuthorizeRequestsTests { } } + // gh-3394 + @Test + public void roleHiearchy() throws Exception { + loadConfig(RoleHiearchyConfig.class); + + SecurityContext securityContext = new SecurityContextImpl(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("test", "notused", AuthorityUtils.createAuthorityList("ROLE_USER"))); + this.request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext); + + this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain); + + assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); + } + + @EnableWebSecurity + @Configuration + static class RoleHiearchyConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasRole("ADMIN"); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication(); + // @formatter:on + } + + @Bean + public RoleHierarchy roleHiearchy() { + RoleHierarchyImpl result = new RoleHierarchyImpl(); + result.setHierarchy("ROLE_USER > ROLE_ADMIN"); + return result; + } + } + public void loadConfig(Class... configs) { this.context = new AnnotationConfigWebApplicationContext(); this.context.register(configs);