From 8da8457da6aa20d8eaf10d1de37ca6802e0f55ee Mon Sep 17 00:00:00 2001 From: kandaguru17 Date: Tue, 31 Oct 2023 20:20:13 +1300 Subject: [PATCH] Use available RoleHierachy Bean for MethodSecurity Config Closes gh-12783 --- .../Jsr250MethodSecurityConfiguration.java | 11 ++++++- .../PrePostMethodSecurityConfiguration.java | 5 +++ .../SecuredMethodSecurityConfiguration.java | 11 ++++++- .../configuration/MethodSecurityService.java | 5 ++- .../MethodSecurityServiceImpl.java | 4 +++ ...ePostMethodSecurityConfigurationTests.java | 33 +++++++++++++++++++ 6 files changed, 66 insertions(+), 3 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java index 667eb7e5250..f3f0f18364e 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java @@ -22,9 +22,13 @@ 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 defaultsProvider, ObjectProvider strategyProvider, - ObjectProvider registryProvider) { + ObjectProvider 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); diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java index 79133e62a4d..208b0921dcf 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java @@ -33,6 +33,8 @@ 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 @@ static MethodInterceptor postFilterAuthorizationMethodInterceptor( private static MethodSecurityExpressionHandler defaultExpressionHandler( ObjectProvider 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; diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java index a2f565f117d..e42efb66a6f 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java @@ -22,10 +22,14 @@ 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 strategyProvider, - ObjectProvider registryProvider) { + ObjectProvider 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 manager = new DeferringObservationAuthorizationManager<>( diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java index 1411eb92f65..9dddcad251b 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java +++ b/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); diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java index 18a2b7eb59d..b414b2d93aa 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java @@ -73,6 +73,10 @@ public void preAuthorizeBean(boolean b) { public void preAuthorizeAdmin() { } + @Override + public void preAuthorizeUser() { + } + @Override public String preAuthorizePermitAll() { return null; diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java index 7e049bed7cd..9458d363df9 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java @@ -46,6 +46,8 @@ 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 void configureWhenBeanOverridingDisallowedThenWorks() { .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 disallowBeanOverriding() { return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false); } @@ -627,4 +647,17 @@ Authz authz() { } + @Configuration + @EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true) + static class RoleHierarchyConfig { + + @Bean + RoleHierarchy roleHierarchy() { + RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); + roleHierarchyImpl.setHierarchy("ADMIN > USER"); + return roleHierarchyImpl; + } + + } + }