diff --git a/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java b/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java index ffea430b3a..29ed636d0c 100644 --- a/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java +++ b/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java @@ -48,6 +48,8 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho private final List>> mappings; + private AuthorizationManager defaultManager = (authentication, request) -> null; + private RequestMatcherDelegatingAuthorizationManager( List>> mappings) { Assert.notEmpty(mappings, "mappings cannot be empty"); @@ -81,8 +83,10 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho new RequestAuthorizationContext(request, matchResult.getVariables())); } } - this.logger.trace("Abstaining since did not find matching RequestMatcher"); - return null; + if (this.logger.isTraceEnabled()) { + this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, this.defaultManager)); + } + return this.defaultManager.check(authentication, new RequestAuthorizationContext(request)); } /** @@ -93,6 +97,21 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho return new Builder(); } + /** + * Use this {@link AuthorizationManager} if the request fails to match any other + * configured {@link AuthorizationManager}. + * + *

+ * This is specifically handy when considering whether to accept or deny requests by + * default. The default is to abstain from deciding on requests that don't match + * configuration. + * @param authorizationManager the {@link AuthorizationManager} to use + * @since 5.8 + */ + public void setDefaultAuthorizationManager(AuthorizationManager authorizationManager) { + this.defaultManager = authorizationManager; + } + /** * A builder for {@link RequestMatcherDelegatingAuthorizationManager}. */ diff --git a/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java b/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java index 624a6ecee8..340b2b5bc9 100644 --- a/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java +++ b/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java @@ -24,6 +24,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.util.matcher.AnyRequestMatcher; @@ -31,6 +32,10 @@ import org.springframework.security.web.util.matcher.RequestMatcherEntry; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Tests for {@link RequestMatcherDelegatingAuthorizationManager}. @@ -115,6 +120,20 @@ public class RequestMatcherDelegatingAuthorizationManagerTests { assertThat(unmapped.isGranted()).isFalse(); } + @Test + public void checkWhenNoMatchesThenUsesDefaultAuthorizationManager() { + RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() + .add((request) -> false, (authentication, context) -> new AuthorizationDecision(false)).build(); + AuthorizationManager defaultManager = mock(AuthorizationManager.class); + given(defaultManager.check(any(), any())).willReturn(new AuthorizationDecision(true)); + manager.setDefaultAuthorizationManager(defaultManager); + Supplier authentication = () -> new TestingAuthenticationToken("user", "password"); + AuthorizationDecision decision = manager.check(authentication, new MockHttpServletRequest(null, "/endpoint")); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + verify(defaultManager).check(any(), any()); + } + @Test public void addWhenMappingsConsumerNullThenException() { assertThatIllegalArgumentException()