Skip to content

Support GrantedAuthorityDefaults Bean in authorizeHttpRequests Kotlin DSL #15171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ttasjwi opened this issue May 27, 2024 · 1 comment
Closed
Assignees
Labels
in: config An issue in spring-security-config type: enhancement A general enhancement
Milestone

Comments

@ttasjwi
Copy link

ttasjwi commented May 27, 2024

Overview

  • In the Java DSL configuration, the role prefix is correctly applied based on the configured GrantedAuthorityDefaultsbean.
  • However, in the Kotlin DSL configuration, the role prefix is disregarded, leading to unexpected behavior.

Expected Behavior

  • The role prefix configured in the GrantedAuthorityDefaults bean should be respected and applied consistently across both Java DSL and Kotlin DSL configurations.

Current Behavior

  • The role prefix is properly applied in the Java DSL configuration but ignored in the Kotlin DSL configuration.

Example Code

1. Common - 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"
    }
}

2. Common - SecurityConfig

    @Bean
    fun grantedAuthorityDefaults(): GrantedAuthorityDefaults {
        return GrantedAuthorityDefaults("MYPREFIX_")
    }

    @Bean
    fun userDetailsService(): UserDetailsService {
        val user = User.withUsername("user").password("{noop}1111").authorities("MYPREFIX_USER").build()
        val db = User.withUsername("db").password("{noop}1111").authorities("MYPREFIX_DB").build()
        val admin = User.withUsername("admin").password("{noop}1111").authorities("MYPREFIX_ADMIN").build()
        return InMemoryUserDetailsManager(user, db, admin)
    }

3. JavaDSL SecurityFilterChain

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .authorizeHttpRequests {
                it
                    .requestMatchers("/user").hasRole("USER")
                    .requestMatchers("/admin").hasRole("ADMIN")
                    .requestMatchers("/db").hasRole("DB")
                    .anyRequest().authenticated()
            }
            .formLogin(Customizer.withDefaults())
        return http.build()
    }

4. KotlinDSL SecurityFilterChain

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeHttpRequests {
                authorize("/user", hasRole("USER"))
                authorize("/admin", hasRole("ADMIN"))
                authorize("/db", hasRole("DB"))
                authorize(anyRequest, authenticated)
            }
            formLogin { }
        }
        return http.build()
    }

Probable Cause

  • In Java DSL, the AuthorizeHttpRequestsConfigurer operates, utilizing the GrantedAuthorityDefaults bean registered by the developer to apply the role prefix.
        // AuthorizeHttpRequestsConfigurer
	public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
		this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
		if (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {
			this.publisher = context.getBean(AuthorizationEventPublisher.class);
		}
		else {
			this.publisher = new SpringAuthorizationEventPublisher(context);
		}
		this.roleHierarchy = SingletonSupplier.of(() -> (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
				? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy());
		String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
		if (grantedAuthorityDefaultsBeanNames.length > 0) {
			GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults.class);
			this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
		}
	}
  • However, in Kotlin DSL, the hasRole configuration invokes the hasRole method of AuthorizeHttpRequestsDsl, which in turn calls AuthorityAuthorizationManager.hasRole(role), disregarding the GrantedAuthorityDefaults bean registered by the developer.
    // AuthorizeHttpRequestsDsl.hasRole
    fun hasRole(role: String): AuthorizationManager<RequestAuthorizationContext> {
        return AuthorityAuthorizationManager.hasRole(role)
    }
        // AuthorityAuthorizationManager
	public static <T> AuthorityAuthorizationManager<T> hasRole(String role) {
		Assert.notNull(role, "role cannot be null");
		Assert.isTrue(!role.startsWith(ROLE_PREFIX), () -> role + " should not start with " + ROLE_PREFIX + " since "
				+ ROLE_PREFIX + " is automatically prepended when using hasRole. Consider using hasAuthority instead.");
		return hasAuthority(ROLE_PREFIX + role);
	}
  • This results in only the ROLE_ prefix being used, diverging from the developer's intention.
  • For instance, a user with the "MYPREFIX_USER" role might fail authorization for the /user endpoint.
@ttasjwi ttasjwi added status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels May 27, 2024
@ttasjwi ttasjwi changed the title Support GrantedAuthorityDefaults Bean in authorizeHttpRequests Kotlin DSL Support GrantedAuthorityDefaults Bean in authorizeHttpRequests Kotlin DSL May 27, 2024
@jzheaux jzheaux added in: config An issue in spring-security-config and removed status: waiting-for-triage An issue we've not yet triaged labels Jun 6, 2024
@jzheaux jzheaux self-assigned this Jun 6, 2024
@jzheaux jzheaux added this to the 6.4.0-M1 milestone Jun 6, 2024
@jzheaux jzheaux closed this as completed in a7f9ccb Jun 6, 2024
@jzheaux
Copy link
Contributor

jzheaux commented Jun 6, 2024

Thanks for the report, @ttasjwi. This is now merged into main and will go out in the next milestone. Please check the latest 6.4 snapshot to confirm the feature works for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: config An issue in spring-security-config type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants