Hello, Spring Security Team.
I have encountered an issue when configuring security with Kotlin DSL and RoleHierarchy. The behavior seems inconsistent compared to the traditional DSL configuration.
Controller
@RestController
class SecurityController {
@GetMapping("/")
fun index(): String {
return "index"
}
@GetMapping("/user")
fun user(): String {
return "user"
}
@GetMapping("/db")
fun db(): String {
return "db"
}
@GetMapping("/admin")
fun admin(): String {
return "admin"
}
}
UserDetailsService
@Bean
fun userDetailsService(): UserDetailsService {
val user = User.withUsername("user").password("{noop}1111").roles("USER").build()
val db = User.withUsername("db").password("{noop}1111").roles("DB").build()
val admin = User.withUsername("admin").password("{noop}1111").roles("ADMIN").build()
return InMemoryUserDetailsManager(user, db, admin)
}
RoleHierarchy Bean
The RoleHierarchy bean is configured as follows:
@Bean
fun roleHierarchy(): RoleHierarchy {
val roleHierarchy = RoleHierarchyImpl()
roleHierarchy.setHierarchy(
"""
ROLE_ADMIN > ROLE_DB
ROLE_DB > ROLE_USER
ROLE_USER > ROLE_ANONYMOUS
""".trimIndent()
)
return roleHierarchy
}
Traditional DSL Configuration
Using the traditional DSL configuration, RoleHierarchy works as expected:
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http.authorizeHttpRequests {
it
.requestMatchers("/user").hasRole("USER")
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/db").hasRole("DB")
}
.formLogin(Customizer.withDefaults())
.csrf { it.disable() }
return http.build()
}
Kotlin DSL Configuration
However, when using the Kotlin DSL configuration, the RoleHierarchy seems to behave inconsistently:
import org.springframework.security.config.annotation.web.invoke
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/user", hasRole("USER"))
authorize("/admin", hasRole("ADMIN"))
authorize("/db", hasRole("DB"))
authorize(anyRequest, authenticated)
}
formLogin { }
csrf { disable() }
}
return http.build()
}
Environment
- Spring Boot : 3.2.5
- spring security: 6.2.4
- kotlin version : 1.9.23
Comment From: marcusdacoregio
Hi, @ttasjwi. Thanks for the report.
Can you clarify what is inconsistent between the two configurations?
Comment From: ttasjwi
Hi, @ttasjwi. Thanks for the report.
Can you clarify what is inconsistent between the two configurations?
Hi,
To clarify the inconsistencies between the two configurations:
When using the traditional DSL configuration, the RoleHierarchy works as expected. However, when using the Kotlin DSL configuration, the RoleHierarchy seems to behave inconsistently. For example, an ADMIN user should be able to access the /user endpoint, but in practice, this is not always the case.
Here's a summary of the issue I’m encountering:
- Traditional DSL Configuration: The RoleHierarchy is properly applied, and an ADMIN user can access endpoints that a DB or USER role can access.
- Kotlin DSL Configuration: The RoleHierarchy does not seem to work as expected. An ADMIN user is unable to access certain endpoints that should be accessible based on the RoleHierarchy.
The only difference between the two setups is the declaration of the securityFilterChain method. All other components, including the Controller, UserDetailsService, and RoleHierarchy bean, remain the same.
Here are the two securityFilterChain configurations for comparison:
Traditional DSL Configuration
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http.authorizeHttpRequests {
it
.requestMatchers("/user").hasRole("USER")
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/db").hasRole("DB")
}
.formLogin(Customizer.withDefaults())
.csrf { it.disable() }
return http.build()
}
Kotlin DSL Configuration
import org.springframework.security.config.annotation.web.invoke
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/user", hasRole("USER"))
authorize("/admin", hasRole("ADMIN"))
authorize("/db", hasRole("DB"))
authorize(anyRequest, authenticated)
}
formLogin { }
csrf { disable() }
}
return http.build()
}
I would like to know if I am missing something in my Kotlin DSL configuration or if there are additional steps required to properly set up the RoleHierarchy in Kotlin DSL.
Thank you for your assistance.
Comment From: marcusdacoregio
Hi @ttasjwi. I see that there is no support for RoleHierarchy bean in the authorizeHttpRequests Kotlin DSL. The issue https://github.com/spring-projects/spring-security/issues/13188 has added it but only to the Java DSL.
I'll repurpose this ticket to add the support to the Kotlin DSL.