From 7fea639a43b4de19d49a4739c6c59f9897950c4a Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 11 Apr 2022 15:16:16 -0300 Subject: [PATCH] Add Option to Filter All Dispatcher Types Closes gh-11092 --- .../AuthorizeHttpRequestsConfigurer.java | 16 +++++ .../authorize-http-requests.adoc | 22 ++++++ .../access/intercept/AuthorizationFilter.java | 28 ++++++++ .../intercept/AuthorizationFilterTests.java | 67 ++++++++++++++++++- 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java index 60a384ebd6..f06f4bde98 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java @@ -85,6 +85,7 @@ public final class AuthorizeHttpRequestsConfigurer authorizationManager = this.registry.createAuthorizationManager(); AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager); authorizationFilter.setAuthorizationEventPublisher(this.publisher); + authorizationFilter.setShouldFilterAllDispatcherTypes(this.registry.shouldFilterAllDispatcherTypes); http.addFilter(postProcess(authorizationFilter)); } @@ -117,6 +118,8 @@ public final class AuthorizeHttpRequestsConfigurer authorize + .shouldFilterAllDispatcherTypes(true) + .anyRequest.authenticated() + ) + // ... + + return http.build(); +} +---- +==== diff --git a/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java b/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java index 25b5b643ea..e772cf96f1 100644 --- a/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java +++ b/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java @@ -50,6 +50,8 @@ public class AuthorizationFilter extends OncePerRequestFilter { private AuthorizationEventPublisher eventPublisher = AuthorizationFilter::noPublish; + private boolean shouldFilterAllDispatcherTypes = false; + /** * Creates an instance. * @param authorizationManager the {@link AuthorizationManager} to use @@ -80,6 +82,22 @@ public class AuthorizationFilter extends OncePerRequestFilter { return authentication; } + @Override + protected void doFilterNestedErrorDispatch(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + doFilterInternal(request, response, filterChain); + } + + @Override + protected boolean shouldNotFilterAsyncDispatch() { + return !this.shouldFilterAllDispatcherTypes; + } + + @Override + protected boolean shouldNotFilterErrorDispatch() { + return !this.shouldFilterAllDispatcherTypes; + } + /** * Use this {@link AuthorizationEventPublisher} to publish * {@link AuthorizationDeniedEvent}s and {@link AuthorizationGrantedEvent}s. @@ -99,6 +117,16 @@ public class AuthorizationFilter extends OncePerRequestFilter { return this.authorizationManager; } + /** + * Sets whether to filter all dispatcher types. + * @param shouldFilterAllDispatcherTypes should filter all dispatcher types. Default + * is {@code false} + * @since 5.7 + */ + public void setShouldFilterAllDispatcherTypes(boolean shouldFilterAllDispatcherTypes) { + this.shouldFilterAllDispatcherTypes = shouldFilterAllDispatcherTypes; + } + private static void noPublish(Supplier authentication, T object, AuthorizationDecision decision) { diff --git a/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java b/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java index a271b43947..e0d885fee1 100644 --- a/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.security.web.access.intercept; import java.util.function.Supplier; +import javax.servlet.DispatcherType; import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; @@ -38,6 +39,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.web.util.WebUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -165,4 +167,67 @@ public class AuthorizationFilterTests { any(AuthorizationDecision.class)); } + @Test + public void doFilterWhenErrorThenDoNotFilter() throws Exception { + AuthorizationManager authorizationManager = mock(AuthorizationManager.class); + AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager); + MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path"); + mockRequest.setDispatcherType(DispatcherType.ERROR); + mockRequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error"); + MockHttpServletResponse mockResponse = new MockHttpServletResponse(); + FilterChain mockFilterChain = mock(FilterChain.class); + + authorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain); + verifyNoInteractions(authorizationManager); + } + + @Test + public void doFilterWhenErrorAndShouldFilterAllDispatcherTypesThenFilter() throws Exception { + AuthorizationManager authorizationManager = mock(AuthorizationManager.class); + AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager); + authorizationFilter.setShouldFilterAllDispatcherTypes(true); + MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path"); + mockRequest.setDispatcherType(DispatcherType.ERROR); + mockRequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error"); + MockHttpServletResponse mockResponse = new MockHttpServletResponse(); + FilterChain mockFilterChain = mock(FilterChain.class); + + authorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain); + verify(authorizationManager).check(any(Supplier.class), any(HttpServletRequest.class)); + } + + @Test + public void doFilterNestedErrorDispatchWhenAuthorizationManagerThenUses() throws Exception { + AuthorizationManager authorizationManager = mock(AuthorizationManager.class); + AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager); + authorizationFilter.setShouldFilterAllDispatcherTypes(true); + MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path"); + mockRequest.setDispatcherType(DispatcherType.ERROR); + mockRequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error"); + MockHttpServletResponse mockResponse = new MockHttpServletResponse(); + FilterChain mockFilterChain = mock(FilterChain.class); + + authorizationFilter.doFilterNestedErrorDispatch(mockRequest, mockResponse, mockFilterChain); + verify(authorizationManager).check(any(Supplier.class), any(HttpServletRequest.class)); + } + + @Test + public void doFilterNestedErrorDispatchWhenAuthorizationEventPublisherThenUses() throws Exception { + AuthorizationFilter authorizationFilter = new AuthorizationFilter( + AuthenticatedAuthorizationManager.authenticated()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path"); + MockHttpServletResponse mockResponse = new MockHttpServletResponse(); + FilterChain mockFilterChain = mock(FilterChain.class); + + SecurityContext securityContext = new SecurityContextImpl(); + securityContext.setAuthentication(new TestingAuthenticationToken("user", "password", "ROLE_USER")); + SecurityContextHolder.setContext(securityContext); + + AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class); + authorizationFilter.setAuthorizationEventPublisher(eventPublisher); + authorizationFilter.doFilterNestedErrorDispatch(mockRequest, mockResponse, mockFilterChain); + verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class), + any(AuthorizationDecision.class)); + } + }