Bladeren bron

Use available RoleHierachy Bean for MethodSecurity Config

Closes gh-12783
kandaguru17 1 jaar geleden
bovenliggende
commit
b76f7c029d

+ 10 - 1
config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java

@@ -22,9 +22,13 @@ import org.aopalliance.intercept.MethodInvocation;
 
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Role;
+import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
 import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
@@ -49,8 +53,13 @@ final class Jsr250MethodSecurityConfiguration {
 	static MethodInterceptor jsr250AuthorizationMethodInterceptor(
 			ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
 			ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
-			ObjectProvider<ObservationRegistry> registryProvider) {
+			ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
 		Jsr250AuthorizationManager jsr250 = new Jsr250AuthorizationManager();
+		AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();
+		RoleHierarchy roleHierarchy = (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
+				? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy();
+		authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy);
+		jsr250.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);
 		defaultsProvider.ifAvailable((d) -> jsr250.setRolePrefix(d.getRolePrefix()));
 		SecurityContextHolderStrategy strategy = strategyProvider
 			.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);

+ 5 - 0
config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java

@@ -33,6 +33,8 @@ import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
 import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
+import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
@@ -123,6 +125,9 @@ final class PrePostMethodSecurityConfiguration {
 	private static MethodSecurityExpressionHandler defaultExpressionHandler(
 			ObjectProvider<GrantedAuthorityDefaults> defaultsProvider, ApplicationContext context) {
 		DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
+		RoleHierarchy roleHierarchy = (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
+				? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy();
+		handler.setRoleHierarchy(roleHierarchy);
 		defaultsProvider.ifAvailable((d) -> handler.setDefaultRolePrefix(d.getRolePrefix()));
 		handler.setApplicationContext(context);
 		return handler;

+ 10 - 1
config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java

@@ -22,10 +22,14 @@ import org.aopalliance.intercept.MethodInvocation;
 
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Role;
 import org.springframework.security.access.annotation.Secured;
+import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
 import org.springframework.security.authorization.method.SecuredAuthorizationManager;
@@ -48,8 +52,13 @@ final class SecuredMethodSecurityConfiguration {
 	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 	static MethodInterceptor securedAuthorizationMethodInterceptor(
 			ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
-			ObjectProvider<ObservationRegistry> registryProvider) {
+			ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
 		SecuredAuthorizationManager secured = new SecuredAuthorizationManager();
+		AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();
+		RoleHierarchy roleHierarchy = (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
+				? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy();
+		authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy);
+		secured.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);
 		SecurityContextHolderStrategy strategy = strategyProvider
 			.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
 		AuthorizationManager<MethodInvocation> manager = new DeferringObservationAuthorizationManager<>(

+ 4 - 1
config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java

@@ -50,7 +50,7 @@ public interface MethodSecurityService {
 	@PermitAll
 	String jsr250PermitAll();
 
-	@RolesAllowed("ADMIN")
+	@RolesAllowed({ "ADMIN", "USER" })
 	String jsr250RolesAllowed();
 
 	@Secured({ "ROLE_USER", "RUN_AS_SUPER" })
@@ -68,6 +68,9 @@ public interface MethodSecurityService {
 	@PreAuthorize("hasRole('ADMIN')")
 	void preAuthorizeAdmin();
 
+	@PreAuthorize("hasRole('USER')")
+	void preAuthorizeUser();
+
 	@PreAuthorize("hasPermission(#object,'read')")
 	String hasPermission(String object);
 

+ 4 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java

@@ -73,6 +73,10 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
 	public void preAuthorizeAdmin() {
 	}
 
+	@Override
+	public void preAuthorizeUser() {
+	}
+
 	@Override
 	public String preAuthorizePermitAll() {
 		return null;

+ 33 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java

@@ -46,6 +46,8 @@ import org.springframework.security.access.annotation.ExpressionProtectedBusines
 import org.springframework.security.access.annotation.Jsr250BusinessServiceImpl;
 import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
 import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
@@ -447,6 +449,24 @@ public class PrePostMethodSecurityConfigurationTests {
 			.autowire();
 	}
 
+	@WithMockUser(roles = "ADMIN")
+	@Test
+	public void methodSecurityAdminWhenRoleHierarchyBeanAvailableThenUses() {
+		this.spring.register(RoleHierarchyConfig.class, MethodSecurityServiceConfig.class).autowire();
+		this.methodSecurityService.preAuthorizeAdmin();
+		this.methodSecurityService.secured();
+		this.methodSecurityService.jsr250RolesAllowed();
+	}
+
+	@WithMockUser
+	@Test
+	public void methodSecurityUserWhenRoleHierarchyBeanAvailableThenUses() {
+		this.spring.register(RoleHierarchyConfig.class, MethodSecurityServiceConfig.class).autowire();
+		this.methodSecurityService.preAuthorizeUser();
+		this.methodSecurityService.securedUser();
+		this.methodSecurityService.jsr250RolesAllowed();
+	}
+
 	private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
 		return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
 	}
@@ -627,4 +647,17 @@ public class PrePostMethodSecurityConfigurationTests {
 
 	}
 
+	@Configuration
+	@EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true)
+	static class RoleHierarchyConfig {
+
+		@Bean
+		RoleHierarchy roleHierarchy() {
+			RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
+			roleHierarchyImpl.setHierarchy("ADMIN > USER");
+			return roleHierarchyImpl;
+		}
+
+	}
+
 }