|
|
@@ -17,6 +17,8 @@
|
|
|
package org.springframework.security.web.access;
|
|
|
|
|
|
import java.util.Collection;
|
|
|
+import java.util.List;
|
|
|
+import java.util.stream.Stream;
|
|
|
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
@@ -29,13 +31,18 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
|
|
import org.springframework.security.access.AccessDeniedException;
|
|
|
import org.springframework.security.authorization.AuthorityAuthorizationDecision;
|
|
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
|
|
+import org.springframework.security.authorization.FactorAuthorizationDecision;
|
|
|
+import org.springframework.security.authorization.RequiredFactor;
|
|
|
+import org.springframework.security.authorization.RequiredFactorError;
|
|
|
import org.springframework.security.core.GrantedAuthority;
|
|
|
import org.springframework.security.core.authority.AuthorityUtils;
|
|
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
|
|
+import org.springframework.security.web.WebAttributes;
|
|
|
import org.springframework.security.web.savedrequest.RequestCache;
|
|
|
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
|
|
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
|
|
|
|
|
+import static org.assertj.core.api.Assertions.assertThat;
|
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
|
import static org.mockito.Mockito.mock;
|
|
|
import static org.mockito.Mockito.verify;
|
|
|
@@ -136,10 +143,29 @@ class DelegatingMissingAuthorityAccessDeniedHandlerTests {
|
|
|
verify(basicPasswordEntryPoint).commence(any(), any(), any());
|
|
|
}
|
|
|
|
|
|
+ // gh-18000
|
|
|
+ @Test
|
|
|
+ void whenMultipleFactorErrorsThenPropagatesAll() throws Exception {
|
|
|
+ AccessDeniedHandler accessDeniedHandler = this.builder.build();
|
|
|
+ accessDeniedHandler.handle(this.request, this.response, missingFactors("FACTOR", "PASSWORD"));
|
|
|
+ verify(this.factorEntryPoint).commence(any(), any(), any());
|
|
|
+ Object attribute = this.request.getAttribute(WebAttributes.REQUIRED_FACTOR_ERRORS);
|
|
|
+ assertThat(attribute).isInstanceOf(Collection.class);
|
|
|
+ assertThat((Collection<?>) attribute).hasSize(2);
|
|
|
+ }
|
|
|
+
|
|
|
AuthorizationDeniedException missingAuthorities(String... authorities) {
|
|
|
Collection<GrantedAuthority> granted = AuthorityUtils.createAuthorityList(authorities);
|
|
|
AuthorityAuthorizationDecision decision = new AuthorityAuthorizationDecision(false, granted);
|
|
|
return new AuthorizationDeniedException("access denied", decision);
|
|
|
}
|
|
|
|
|
|
+ AuthorizationDeniedException missingFactors(String... factors) {
|
|
|
+ List<RequiredFactorError> errors = Stream.of(factors)
|
|
|
+ .map((f) -> RequiredFactorError.createMissing(RequiredFactor.withAuthority(f).build()))
|
|
|
+ .toList();
|
|
|
+ FactorAuthorizationDecision decision = new FactorAuthorizationDecision(errors);
|
|
|
+ return new AuthorizationDeniedException("access denied", decision);
|
|
|
+ }
|
|
|
+
|
|
|
}
|