diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java index 63af3affff7..d8dfeda71af 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java @@ -379,9 +379,8 @@ private boolean addAuthorizationManager(SecurityFilterChain securityFilterChain, } if (filter instanceof AuthorizationFilter authorization) { AuthorizationManager authorizationManager = authorization.getAuthorizationManager(); - builder.add(securityFilterChain::matches, - (authentication, context) -> (AuthorizationDecision) authorizationManager - .authorize(authentication, context.getRequest())); + builder.add(securityFilterChain::matches, (authentication, context) -> authorizationManager + .authorize(authentication, context.getRequest())); mappings = true; } } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityTests.java index 34520754ef5..08812308554 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityTests.java @@ -17,6 +17,7 @@ package org.springframework.security.config.annotation.web.builders; import java.io.IOException; +import java.util.List; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.ObservationTextPublisher; @@ -35,13 +36,19 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.authorization.FactorAuthorizationDecision; +import org.springframework.security.authorization.RequiredFactor; +import org.springframework.security.authorization.RequiredFactorError; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator; import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.web.bind.annotation.RequestMapping; @@ -111,6 +118,19 @@ public void requestRejectedHandlerInvokedWhenOperationalObservationRegistry() th assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST); } + // gh-19282 + @Test + public void privilegeEvaluatorWhenAuthorizationManagerReturnsFactorDecisionThenIsAllowedReturnsFalseWithoutClassCastException() { + loadConfig(FactorDecisionConfig.class); + WebInvocationPrivilegeEvaluator evaluator = this.context.getBean(WebInvocationPrivilegeEvaluator.class); + TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER"); + // Before gh-19282 the lambda registered in WebSecurity#addAuthorizationManager + // cast the result of AuthorizationManager#authorize to AuthorizationDecision, + // which fails for AuthorizationResult subtypes such as + // FactorAuthorizationDecision. + assertThat(evaluator.isAllowed("/secure", authentication)).isFalse(); + } + // gh-19128 @Test public void ignoringWhenBuilderBeanWithBasePathThenHonorsBasePath() throws Exception { @@ -263,6 +283,25 @@ String path() { } + // gh-19282 + @Configuration + @EnableWebSecurity + static class FactorDecisionConfig { + + @Bean + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeHttpRequests((requests) -> requests + .anyRequest().access((authentication, context) -> new FactorAuthorizationDecision( + List.of(RequiredFactorError.createMissing( + RequiredFactor.withAuthority(FactorGrantedAuthority.OTT_AUTHORITY).build()))))); + // @formatter:on + return http.build(); + } + + } + @Configuration @EnableWebSecurity static class RequestRejectedHandlerConfig {