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
This commit is contained in:
Rob Winch 2016-04-19 11:47:38 -05:00 committed by Joe Grandja
parent 933a7e8363
commit c872a77ad1
6 changed files with 99 additions and 0 deletions

View File

@ -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<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;

View File

@ -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<H extends HttpSecurityBu
if (trustResolver != null) {
defaultHandler.setTrustResolver(trustResolver);
}
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if(context != null) {
String[] roleHiearchyBeanNames = context.getBeanNamesForType(RoleHierarchy.class);
if(roleHiearchyBeanNames.length == 1) {
defaultHandler.setRoleHierarchy(context.getBean(roleHiearchyBeanNames[0], RoleHierarchy.class));
}
}
expressionHandler = postProcess(defaultHandler);
}

View File

@ -15,6 +15,10 @@
*/
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.junit.After;
@ -470,4 +474,26 @@ public class GlobalMethodSecurityConfigurationTests extends BaseSpringSpec {
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")
}
}
}

View File

@ -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);

View File

@ -55,6 +55,10 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
return SecurityContextHolder.getContext().getAuthentication();
}
@Override
public void preAuthorizeAdmin() {
}
@Override
public String preAuthorizePermitAll() {
return null;

View File

@ -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);