mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-01 09:42:13 +00:00
Support RoleHierarchy Bean in authorizeHttpRequests Kotlin DSL
Closes gh-15136
This commit is contained in:
parent
ed2b654f71
commit
7c43fc111f
@ -18,6 +18,8 @@ package org.springframework.security.config.annotation.web
|
|||||||
|
|
||||||
import org.springframework.context.ApplicationContext
|
import org.springframework.context.ApplicationContext
|
||||||
import org.springframework.http.HttpMethod
|
import org.springframework.http.HttpMethod
|
||||||
|
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.AuthenticatedAuthorizationManager
|
||||||
import org.springframework.security.authorization.AuthorityAuthorizationManager
|
import org.springframework.security.authorization.AuthorityAuthorizationManager
|
||||||
import org.springframework.security.authorization.AuthorizationDecision
|
import org.springframework.security.authorization.AuthorizationDecision
|
||||||
@ -65,6 +67,7 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
|||||||
|
|
||||||
private val authorizationRules = mutableListOf<AuthorizationManagerRule>()
|
private val authorizationRules = mutableListOf<AuthorizationManagerRule>()
|
||||||
private val rolePrefix: String
|
private val rolePrefix: String
|
||||||
|
private val roleHierarchy: RoleHierarchy
|
||||||
|
|
||||||
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
|
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
|
||||||
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
|
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
|
||||||
@ -210,7 +213,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
|||||||
* @return the [AuthorizationManager] with the provided authority
|
* @return the [AuthorizationManager] with the provided authority
|
||||||
*/
|
*/
|
||||||
fun hasAuthority(authority: String): AuthorizationManager<RequestAuthorizationContext> {
|
fun hasAuthority(authority: String): AuthorizationManager<RequestAuthorizationContext> {
|
||||||
return AuthorityAuthorizationManager.hasAuthority(authority)
|
val manager = AuthorityAuthorizationManager.hasAuthority<RequestAuthorizationContext>(authority)
|
||||||
|
return withRoleHierarchy(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -220,7 +224,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
|||||||
* @return the [AuthorizationManager] with the provided authorities
|
* @return the [AuthorizationManager] with the provided authorities
|
||||||
*/
|
*/
|
||||||
fun hasAnyAuthority(vararg authorities: String): AuthorizationManager<RequestAuthorizationContext> {
|
fun hasAnyAuthority(vararg authorities: String): AuthorizationManager<RequestAuthorizationContext> {
|
||||||
return AuthorityAuthorizationManager.hasAnyAuthority(*authorities)
|
val manager = AuthorityAuthorizationManager.hasAnyAuthority<RequestAuthorizationContext>(*authorities)
|
||||||
|
return withRoleHierarchy(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,7 +235,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
|||||||
* @return the [AuthorizationManager] with the provided role
|
* @return the [AuthorizationManager] with the provided role
|
||||||
*/
|
*/
|
||||||
fun hasRole(role: String): AuthorizationManager<RequestAuthorizationContext> {
|
fun hasRole(role: String): AuthorizationManager<RequestAuthorizationContext> {
|
||||||
return AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, arrayOf(role))
|
val manager = AuthorityAuthorizationManager.hasAnyRole<RequestAuthorizationContext>(this.rolePrefix, arrayOf(role))
|
||||||
|
return withRoleHierarchy(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,7 +246,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
|||||||
* @return the [AuthorizationManager] with the provided roles
|
* @return the [AuthorizationManager] with the provided roles
|
||||||
*/
|
*/
|
||||||
fun hasAnyRole(vararg roles: String): AuthorizationManager<RequestAuthorizationContext> {
|
fun hasAnyRole(vararg roles: String): AuthorizationManager<RequestAuthorizationContext> {
|
||||||
return AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, arrayOf(*roles))
|
val manager = AuthorityAuthorizationManager.hasAnyRole<RequestAuthorizationContext>(this.rolePrefix, arrayOf(*roles))
|
||||||
|
return withRoleHierarchy(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,15 +303,34 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.rolePrefix = "ROLE_"
|
this.rolePrefix = "ROLE_"
|
||||||
|
this.roleHierarchy = NullRoleHierarchy()
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(context: ApplicationContext) {
|
constructor(context: ApplicationContext) {
|
||||||
|
val rolePrefix = resolveRolePrefix(context)
|
||||||
|
this.rolePrefix = rolePrefix
|
||||||
|
val roleHierarchy = resolveRoleHierarchy(context)
|
||||||
|
this.roleHierarchy = roleHierarchy
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveRolePrefix(context: ApplicationContext): String {
|
||||||
val beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults::class.java)
|
val beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults::class.java)
|
||||||
if (beanNames.size > 0) {
|
if (beanNames.isNotEmpty()) {
|
||||||
val grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults::class.java);
|
return context.getBean(GrantedAuthorityDefaults::class.java).rolePrefix
|
||||||
this.rolePrefix = grantedAuthorityDefaults.rolePrefix
|
|
||||||
} else {
|
|
||||||
this.rolePrefix = "ROLE_"
|
|
||||||
}
|
}
|
||||||
|
return "ROLE_";
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveRoleHierarchy(context: ApplicationContext): RoleHierarchy {
|
||||||
|
val beanNames = context.getBeanNamesForType(RoleHierarchy::class.java)
|
||||||
|
if (beanNames.isNotEmpty()) {
|
||||||
|
return context.getBean(RoleHierarchy::class.java)
|
||||||
|
}
|
||||||
|
return NullRoleHierarchy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withRoleHierarchy(manager: AuthorityAuthorizationManager<RequestAuthorizationContext>): AuthorityAuthorizationManager<RequestAuthorizationContext> {
|
||||||
|
manager.setRoleHierarchy(this.roleHierarchy)
|
||||||
|
return manager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired
|
|||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.http.HttpMethod
|
import org.springframework.http.HttpMethod
|
||||||
|
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.AuthorizationDecision
|
||||||
import org.springframework.security.authorization.AuthorizationManager
|
import org.springframework.security.authorization.AuthorizationManager
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
@ -892,4 +894,70 @@ class AuthorizeHttpRequestsDslTests {
|
|||||||
return GrantedAuthorityDefaults("CUSTOM_")
|
return GrantedAuthorityDefaults("CUSTOM_")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `hasRole when role hierarchy configured then honor hierarchy`() {
|
||||||
|
this.spring.register(RoleHierarchyConfig::class.java).autowire()
|
||||||
|
this.mockMvc.get("/protected") {
|
||||||
|
with(httpBasic("admin", "password"))
|
||||||
|
}.andExpect {
|
||||||
|
status {
|
||||||
|
isOk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.mockMvc.get("/protected") {
|
||||||
|
with(httpBasic("user", "password"))
|
||||||
|
}.andExpect {
|
||||||
|
status {
|
||||||
|
isOk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
open class RoleHierarchyConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
authorizeHttpRequests {
|
||||||
|
authorize("/protected", hasRole("USER"))
|
||||||
|
}
|
||||||
|
httpBasic { }
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun roleHierarchy(): RoleHierarchy {
|
||||||
|
return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > ROLE_USER")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun userDetailsService(): UserDetailsService {
|
||||||
|
val user = User.withDefaultPasswordEncoder()
|
||||||
|
.username("user")
|
||||||
|
.password("password")
|
||||||
|
.roles("USER")
|
||||||
|
.build()
|
||||||
|
val admin = User.withDefaultPasswordEncoder()
|
||||||
|
.username("admin")
|
||||||
|
.password("password")
|
||||||
|
.roles("ADMIN")
|
||||||
|
.build()
|
||||||
|
return InMemoryUserDetailsManager(user, admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
internal class PathController {
|
||||||
|
|
||||||
|
@RequestMapping("/protected")
|
||||||
|
fun path() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,3 +5,5 @@ Spring Security 6.4 provides a number of new features.
|
|||||||
Below are the highlights of the release, or you can view https://github.com/spring-projects/spring-security/releases[the release notes] for a detailed listing of each feature and bug fix.
|
Below are the highlights of the release, or you can view https://github.com/spring-projects/spring-security/releases[the release notes] for a detailed listing of each feature and bug fix.
|
||||||
|
|
||||||
- https://github.com/spring-projects/spring-security/issues/4186[gh-4186] - Support `RoleHierarchy` in `AclAuthorizationStrategyImpl`
|
- https://github.com/spring-projects/spring-security/issues/4186[gh-4186] - Support `RoleHierarchy` in `AclAuthorizationStrategyImpl`
|
||||||
|
- https://github.com/spring-projects/spring-security/issues/15136[gh-15136] - Support `RoleHierarchy` Bean in `authorizeHttpRequests` Kotlin DSL
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user