From 029e31ebe8bfbc0bf0c48501b52b74081ea19b73 Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Thu, 25 Sep 2025 09:45:52 -0500 Subject: [PATCH] DelegatingAuthenticationEntryPoint.Builder allows just defaultEntryPoint Previously build threw an Exception when entryPoints was empty and defaultEntryPoint was specified. This commit changes build to return the defaultEntryPoint instead. Closes gh-17955 --- .../DelegatingAuthenticationEntryPoint.java | 5 +- ...legatingAuthenticationEntryPointTests.java | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPoint.java b/web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPoint.java index 16f2a0e981..878ff3712f 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPoint.java +++ b/web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPoint.java @@ -211,15 +211,18 @@ public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPo * @return the {@link AuthenticationEntryPoint} to use. */ public AuthenticationEntryPoint build() { - Assert.notEmpty(this.entryPoints, "entryPoints cannot be empty"); AuthenticationEntryPoint defaultEntryPoint = this.defaultEntryPoint; if (defaultEntryPoint == null) { + Assert.state(!this.entryPoints.isEmpty(), "entryPoints cannot be empty if defaultEntryPoint is null"); AuthenticationEntryPoint firstAuthenticationEntryPoint = this.entryPoints.get(0).getEntry(); if (this.entryPoints.size() == 1) { return firstAuthenticationEntryPoint; } defaultEntryPoint = firstAuthenticationEntryPoint; } + else if (this.entryPoints.isEmpty()) { + return defaultEntryPoint; + } return new DelegatingAuthenticationEntryPoint(defaultEntryPoint, this.entryPoints); } diff --git a/web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTests.java b/web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTests.java index 963eba746f..ea697c57c1 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTests.java @@ -16,10 +16,12 @@ package org.springframework.security.web.authentication; +import java.io.IOException; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; +import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,11 +31,15 @@ import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.util.matcher.RequestMatcher; 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.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; /** * Test class for {@link DelegatingAuthenticationEntryPoint} @@ -202,4 +208,49 @@ public class DelegatingAuthenticationEntryPointTests { verify(this.defaultEntryPoint, never()).commence(this.request, null, null); } + @Test + void builderWhenDefaultNullAndSingleEntryPointThenReturnsSingle() { + AuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class); + + AuthenticationEntryPoint result = DelegatingAuthenticationEntryPoint.builder() + .addEntryPointFor(entryPoint, mock(RequestMatcher.class)) + .build(); + + assertThat(result).isEqualTo(entryPoint); + } + + @Test + void builderWhenDefaultNullThenFirstIsDefault() throws ServletException, IOException { + AuthenticationEntryPoint firstEntryPoint = mock(AuthenticationEntryPoint.class); + AuthenticationEntryPoint secondEntryPoint = mock(AuthenticationEntryPoint.class); + RequestMatcher neverMatch = mock(RequestMatcher.class); + given(neverMatch.matches(this.request)).willReturn(false); + AuthenticationEntryPoint result = DelegatingAuthenticationEntryPoint.builder() + .addEntryPointFor(firstEntryPoint, neverMatch) + .addEntryPointFor(secondEntryPoint, neverMatch) + .build(); + + result.commence(this.request, null, null); + + verify(firstEntryPoint).commence(any(), any(), any()); + verifyNoInteractions(secondEntryPoint); + } + + @Test + void builderWhenDefaultAndEmptyEntryPointsThenReturnsDefault() { + AuthenticationEntryPoint defaultEntryPoint = mock(AuthenticationEntryPoint.class); + + AuthenticationEntryPoint result = DelegatingAuthenticationEntryPoint.builder() + .defaultEntryPoint(defaultEntryPoint) + .build(); + + assertThat(result).isEqualTo(defaultEntryPoint); + } + + @Test + void builderWhenNoEntryPointsThenIllegalStateException() { + DelegatingAuthenticationEntryPoint.Builder builder = DelegatingAuthenticationEntryPoint.builder(); + assertThatIllegalStateException().isThrownBy(builder::build); + } + }