This is an automated email from the ASF dual-hosted git repository. massx1 pushed a commit to branch 4_1_X in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 7f71ad78570b984b0808b64c4ce0a4830e4fbc67 Author: Massimiliano Perrone <[email protected]> AuthorDate: Thu Jun 4 06:07:23 2026 +0200 Avoid authentication error detail disclosure --- .../SyncopeBasicAuthenticationEntryPoint.java | 4 +- .../SyncopeBasicAuthenticationEntryPointTest.java | 59 +++++++++++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPoint.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPoint.java index 24efdce799..1f03bf4479 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPoint.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPoint.java @@ -32,11 +32,13 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationEn */ public class SyncopeBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { + public static final String AUTHENTICATION_FAILED = "Authentication failed"; + @Override public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException { - response.addHeader(RESTHeaders.ERROR_INFO, authException.getMessage()); + response.addHeader(RESTHeaders.ERROR_INFO, AUTHENTICATION_FAILED); if (authException instanceof RateLimitAuthenticationException rateLimit) { response.addHeader(HttpHeaders.RETRY_AFTER, Long.toString(rateLimit.getRetryAfterSeconds())); response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(), authException.getMessage()); diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPointTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPointTest.java index 7f34017eb8..86ce543ecb 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPointTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeBasicAuthenticationEntryPointTest.java @@ -21,17 +21,29 @@ package org.apache.syncope.core.spring.security; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.syncope.common.rest.api.RESTHeaders; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; class SyncopeBasicAuthenticationEntryPointTest { + private SyncopeBasicAuthenticationEntryPoint entryPoint; + + @BeforeEach + public void setUp() throws Exception { + entryPoint = new SyncopeBasicAuthenticationEntryPoint(); + entryPoint.setRealmName("Apache Syncope authentication"); + entryPoint.afterPropertiesSet(); + } + @Test void rateLimitAuthenticationExceptionReturnsTooManyRequests() throws Exception { - SyncopeBasicAuthenticationEntryPoint entryPoint = new SyncopeBasicAuthenticationEntryPoint(); MockHttpServletResponse response = new MockHttpServletResponse(); entryPoint.commence( @@ -41,6 +53,49 @@ class SyncopeBasicAuthenticationEntryPointTest { assertEquals(HttpStatus.TOO_MANY_REQUESTS.value(), response.getStatus()); assertEquals("30", response.getHeader(HttpHeaders.RETRY_AFTER)); - assertEquals("Too many authentication failures", response.getHeader(RESTHeaders.ERROR_INFO)); + assertEquals(SyncopeBasicAuthenticationEntryPoint.AUTHENTICATION_FAILED, + response.getHeader(RESTHeaders.ERROR_INFO)); + } + + @Test + public void badCredentialsExposeGenericErrorInfo() throws Exception { + MockHttpServletResponse response = new MockHttpServletResponse(); + + entryPoint.commence( + new MockHttpServletRequest(), + response, + new BadCredentialsException("rossini: invalid password provided")); + + assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus()); + assertEquals(SyncopeBasicAuthenticationEntryPoint.AUTHENTICATION_FAILED, + response.getHeader(RESTHeaders.ERROR_INFO)); + } + + @Test + public void missingUserExposeGenericErrorInfo() throws Exception { + MockHttpServletResponse response = new MockHttpServletResponse(); + + entryPoint.commence( + new MockHttpServletRequest(), + response, + new UsernameNotFoundException("not-a-user")); + + assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus()); + assertEquals(SyncopeBasicAuthenticationEntryPoint.AUTHENTICATION_FAILED, + response.getHeader(RESTHeaders.ERROR_INFO)); + } + + @Test + public void disabledUserExposeGenericErrorInfo() throws Exception { + MockHttpServletResponse response = new MockHttpServletResponse(); + + entryPoint.commence( + new MockHttpServletRequest(), + response, + new DisabledException("User rossini is suspended")); + + assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus()); + assertEquals(SyncopeBasicAuthenticationEntryPoint.AUTHENTICATION_FAILED, + response.getHeader(RESTHeaders.ERROR_INFO)); } }
