Przeglądaj źródła

Use GrantedAuthorityDefaults Bean in Kotlin DSL

Closes gh-15171
Josh Cummings 1 rok temu
rodzic
commit
a7f9ccb6d6

+ 21 - 4
config/src/main/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDsl.kt

@@ -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_"
+        }
+    }
 }

+ 2 - 1
config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDsl.kt

@@ -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)
     }
 

+ 42 - 3
config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDslTests.kt

@@ -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_")
+        }
+    }
 }