AuthorizeHttpRequestsConfigurer.AuthorizedUrl.hasRole should look up for a RoleHierarchy bean in the context

Closes gh-12473
This commit is contained in:
Evgeniy Cheban 2023-01-08 19:09:32 +01:00 committed by Josh Cummings
parent ce11015e53
commit d84b8d2d12
2 changed files with 57 additions and 6 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,11 +17,14 @@
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
@ -38,6 +41,7 @@ import org.springframework.security.web.access.intercept.RequestMatcherDelegatin
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert;
import org.springframework.util.function.SingletonSupplier;
/**
* Adds a URL based authorization using {@link AuthorizationManager}.
@ -56,6 +60,8 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
private final AuthorizationEventPublisher publisher;
private final Supplier<RoleHierarchy> roleHierarchy;
/**
* Creates an instance.
* @param context the {@link ApplicationContext} to use
@ -68,6 +74,8 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
else {
this.publisher = new SpringAuthorizationEventPublisher(context);
}
this.roleHierarchy = SingletonSupplier.of(() -> (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy());
}
/**
@ -251,7 +259,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
return access(AuthorityAuthorizationManager.hasRole(role));
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasRole(role)));
}
/**
@ -263,7 +271,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {
return access(AuthorityAuthorizationManager.hasAnyRole(roles));
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(roles)));
}
/**
@ -273,7 +281,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
return access(AuthorityAuthorizationManager.hasAuthority(authority));
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority)));
}
/**
@ -284,7 +292,13 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)));
}
private AuthorityAuthorizationManager<RequestAuthorizationContext> withRoleHierarchy(
AuthorityAuthorizationManager<RequestAuthorizationContext> manager) {
manager.setRoleHierarchy(AuthorizeHttpRequestsConfigurer.this.roleHierarchy.get());
return manager;
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,6 +26,8 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authorization.AuthorizationDecision;
@ -272,6 +274,17 @@ public class AuthorizeHttpRequestsConfigurerTests {
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
}
@Test
public void getWhenHasRoleUserAndRoleHierarchyConfiguredThenGreaterRoleTakesPrecedence() throws Exception {
this.spring.register(RoleHierarchyUserConfig.class, BasicController.class).autowire();
// @formatter:off
MockHttpServletRequestBuilder requestWithAdmin = get("/")
.with(user("user")
.roles("ADMIN"));
// @formatter:on
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
}
@Test
public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
@ -770,6 +783,30 @@ public class AuthorizeHttpRequestsConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class RoleHierarchyUserConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
return http
.authorizeHttpRequests((requests) -> requests
.anyRequest().hasRole("USER")
)
.build();
// @formatter:on
}
@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
return roleHierarchy;
}
}
@Configuration
@EnableWebSecurity
static class RoleUserOrAdminConfig {