Use GrantedAuthorityDefaults Bean in Kotlin DSL

Closes gh-15171
This commit is contained in:
Josh Cummings 2024-06-06 14:56:37 -06:00
parent 24e3bb11bc
commit a7f9ccb6d6
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
3 changed files with 65 additions and 8 deletions

View File

@ -16,6 +16,7 @@
package org.springframework.security.config.annotation.web
import org.springframework.context.ApplicationContext
import org.springframework.http.HttpMethod
import org.springframework.security.authorization.AuthenticatedAuthorizationManager
import org.springframework.security.authorization.AuthorityAuthorizationManager
@ -23,10 +24,11 @@ import org.springframework.security.authorization.AuthorizationDecision
import org.springframework.security.authorization.AuthorizationManager
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer
import org.springframework.security.config.core.GrantedAuthorityDefaults
import org.springframework.security.core.Authentication
import org.springframework.security.web.access.IpAddressAuthorizationManager
import org.springframework.security.web.access.intercept.AuthorizationFilter
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
import org.springframework.security.web.access.IpAddressAuthorizationManager
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
@ -41,7 +43,7 @@ import java.util.function.Supplier
* @since 5.7
* @property shouldFilterAllDispatcherTypes whether the [AuthorizationFilter] should filter all dispatcher types
*/
class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
@Deprecated("""
Add authorization rules to DispatcherType directly.
@ -62,6 +64,7 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
var shouldFilterAllDispatcherTypes: Boolean? = null
private val authorizationRules = mutableListOf<AuthorizationManagerRule>()
private val rolePrefix: String
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
@ -227,7 +230,7 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
* @return the [AuthorizationManager] with the provided role
*/
fun hasRole(role: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasRole(role)
return AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, arrayOf(role))
}
/**
@ -237,7 +240,7 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
* @return the [AuthorizationManager] with the provided roles
*/
fun hasAnyRole(vararg roles: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasAnyRole(*roles)
return AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, arrayOf(*roles))
}
/**
@ -290,4 +293,18 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
}
}
}
constructor() {
this.rolePrefix = "ROLE_"
}
constructor(context: ApplicationContext) {
val beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults::class.java)
if (beanNames.size > 0) {
val grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults::class.java);
this.rolePrefix = grantedAuthorityDefaults.rolePrefix
} else {
this.rolePrefix = "ROLE_"
}
}
}

View File

@ -77,6 +77,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
var authenticationManager: AuthenticationManager? = null
val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)
/**
* Applies a [SecurityConfigurerAdapter] to this [HttpSecurity]
@ -298,7 +299,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* @since 5.7
*/
fun authorizeHttpRequests(authorizeHttpRequestsConfiguration: AuthorizeHttpRequestsDsl.() -> Unit) {
val authorizeHttpRequestsCustomizer = AuthorizeHttpRequestsDsl().apply(authorizeHttpRequestsConfiguration).get()
val authorizeHttpRequestsCustomizer = AuthorizeHttpRequestsDsl(this.context).apply(authorizeHttpRequestsConfiguration).get()
this.http.authorizeHttpRequests(authorizeHttpRequestsCustomizer)
}

View File

@ -16,7 +16,8 @@
package org.springframework.security.config.annotation.web
import org.assertj.core.api.Assertions.*
import jakarta.servlet.DispatcherType
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.UnsatisfiedDependencyException
@ -28,6 +29,7 @@ import org.springframework.security.authorization.AuthorizationDecision
import org.springframework.security.authorization.AuthorizationManager
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.core.GrantedAuthorityDefaults
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.core.Authentication
@ -55,7 +57,6 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import org.springframework.web.util.WebUtils
import java.util.function.Supplier
import jakarta.servlet.DispatcherType
/**
* Tests for [AuthorizeHttpRequestsDsl]
@ -835,7 +836,6 @@ class AuthorizeHttpRequestsDslTests {
@EnableWebSecurity
@EnableWebMvc
open class HasIpAddressConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
@ -853,4 +853,43 @@ class AuthorizeHttpRequestsDslTests {
}
}
}
fun `hasRole when prefixed by configured role prefix should fail to configure`() {
assertThatThrownBy { this.spring.register(RoleValidationConfig::class.java).autowire() }
.isInstanceOf(UnsatisfiedDependencyException::class.java)
.hasRootCauseInstanceOf(IllegalArgumentException::class.java)
.hasMessageContaining(
"ROLE_JUNIPER should not start with ROLE_ since ROLE_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead."
)
assertThatThrownBy { this.spring.register(RoleValidationConfig::class.java, GrantedAuthorityDefaultsConfig::class.java).autowire() }
.isInstanceOf(UnsatisfiedDependencyException::class.java)
.hasRootCauseInstanceOf(IllegalArgumentException::class.java)
.hasMessageContaining(
"CUSTOM_JUNIPER should not start with CUSTOM_ since CUSTOM_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead."
)
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class RoleValidationConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/role", hasAnyRole("ROLE_JUNIPER"))
authorize("/custom", hasRole("CUSTOM_JUNIPER"))
}
}
return http.build()
}
}
@Configuration
open class GrantedAuthorityDefaultsConfig {
@Bean
open fun grantedAuthorityDefaults(): GrantedAuthorityDefaults {
return GrantedAuthorityDefaults("CUSTOM_")
}
}
}