ソースを参照

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
Rob Winch 9 年 前
コミット
c872a77ad1

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

+ 10 - 0
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<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);
 		}
 

+ 26 - 0
config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy

@@ -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")
+		}
+	}
 }

+ 3 - 0
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);
 

+ 4 - 0
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;

+ 50 - 0
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);