This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 00dd9d2  SLING-13389 : no NPE when an anonymous request carries a 
"resource" parameter
00dd9d2 is described below

commit 00dd9d2cc9f9a2561711624ec02b273e15e74913
Author: dulvac <[email protected]>
AuthorDate: Tue Oct 13 16:10:38 2026 +0200

    SLING-13389 : no NPE when an anonymous request carries a "resource" 
parameter
    
    The "resource" fallback added for SLING-13025 also ran in
    getAnonymousResolver, before a resource resolver is on the request,
    so AuthUtil.getMappedLoginResourcePath threw a NullPointerException for
    the login form (which carries resource=<protected page>).
    
    - getMappedLoginResourcePath returns the unmapped path when the request
      has no resolver attribute, for the jakarta and the javax variant
    - getAnonymousResolver only redirects on an explicit sling.auth.redirect;
      the "resource" fallback is for the redirect after a successful login,
      on the anonymous path it would send the login form to the protected
      page and loop
    - tests for the anonymous login page request, the explicit redirect on
      anonymous access, and the no-resolver case of AuthUtil and both
      feedback handlers
---
 .../java/org/apache/sling/auth/core/AuthUtil.java  | 13 +++++---
 .../sling/auth/core/impl/SlingAuthenticator.java   | 11 +++++--
 .../org/apache/sling/auth/core/AuthUtilTest.java   | 20 ++++++++++--
 .../auth/core/impl/SlingAuthenticatorTest.java     | 37 ++++++++++++++++++++++
 .../DefaultAuthenticationFeedbackHandlerTest.java  | 15 +++++++++
 ...ltJakartaAuthenticationFeedbackHandlerTest.java | 15 +++++++++
 6 files changed, 103 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/apache/sling/auth/core/AuthUtil.java 
b/src/main/java/org/apache/sling/auth/core/AuthUtil.java
index e50a668..d8a71ef 100644
--- a/src/main/java/org/apache/sling/auth/core/AuthUtil.java
+++ b/src/main/java/org/apache/sling/auth/core/AuthUtil.java
@@ -215,8 +215,10 @@ public final class AuthUtil {
      * Returns the mapped resource path (to redirect to after a successful 
authentication).
      * It still needs to be validated by the caller.
      * Use this method to issue a redirect instead of {@link 
#getLoginResource(HttpServletRequest, String)} to correctly consider resource 
resolver mapping.
+     * If the request carries no resource resolver (see {@link 
AuthenticationSupport#REQUEST_ATTRIBUTE_RESOLVER}),
+     * for example while anonymous access is being established, the path is 
returned unmapped.
      * @return the mapped path of the resource target or {@code null} if non 
is given in the request
-     * @since 1.7.0 (Bundle Version 2.1.0)
+     * @since 1.7.0 (Bundle Version 2.1.2)
      */
     public static @Nullable String getMappedLoginResourcePath(
             final HttpServletRequest request, String defaultLoginResource) {
@@ -224,17 +226,19 @@ public final class AuthUtil {
         if (resourcePath == null) {
             return null;
         }
-        return getResourceResolver(request).map(request, resourcePath);
+        final ResourceResolver resolver = getResourceResolver(request);
+        return resolver == null ? resourcePath : resolver.map(request, 
resourcePath);
     }
 
     /**
      * Returns the mapped resource path (to redirect to after a successful 
authentication).
      * It still needs to be validated by the caller.
      * Use this method to issue a redirect instead of {@link 
#getLoginResource(javax.servlet.http.HttpServletRequest, String)} to correctly 
consider resource resolver mapping.
+     * If the request carries no resource resolver, the path is returned 
unmapped.
      * @return the mapped path of the resource target or {@code null} if non 
is given in the request
      *
      * @deprecated Use {@link #getMappedLoginResourcePath(HttpServletRequest, 
String)}
-     * @since 1.7.0 (Bundle Version 2.1.0)
+     * @since 1.7.0 (Bundle Version 2.1.2)
      */
     @Deprecated
     public static @Nullable String getMappedLoginResourcePath(
@@ -243,7 +247,8 @@ public final class AuthUtil {
         if (resourcePath == null) {
             return null;
         }
-        return getResourceResolver(request).map(request, resourcePath);
+        final ResourceResolver resolver = getResourceResolver(request);
+        return resolver == null ? resourcePath : resolver.map(request, 
resourcePath);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java 
b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
index 4d0a61a..340519f 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
@@ -916,8 +916,15 @@ public class SlingAuthenticator implements Authenticator, 
AuthenticationSupport,
                 ResourceResolver resolver = 
resourceResolverFactory.getResourceResolver(authInfo);
 
                 // check whether the client asked for redirect after
-                // authentication and/or impersonation
-                if 
(DefaultJakartaAuthenticationFeedbackHandler.handleRedirect(request, response)) 
{
+                // authentication and/or impersonation. Only an explicit
+                // sling.auth.redirect counts here: the "resource" fallback of
+                // handleRedirect is meant for the redirect after a successful
+                // login. An anonymous request carrying "resource" is typically
+                // the login form itself, redirecting it to the protected
+                // resource would loop (and there is no resolver on the
+                // request yet to map the path with)
+                if 
(request.getParameter(AuthenticationSupport.REDIRECT_PARAMETER) != null
+                        && 
DefaultJakartaAuthenticationFeedbackHandler.handleRedirect(request, response)) {
 
                     // request will now be terminated, so close the resolver
                     // to release resources
diff --git a/src/test/java/org/apache/sling/auth/core/AuthUtilTest.java 
b/src/test/java/org/apache/sling/auth/core/AuthUtilTest.java
index 60a83b1..c813323 100644
--- a/src/test/java/org/apache/sling/auth/core/AuthUtilTest.java
+++ b/src/test/java/org/apache/sling/auth/core/AuthUtilTest.java
@@ -228,11 +228,20 @@ public class AuthUtilTest {
     @Test
     public void test_getMappedLoginResourcePath_null_jakarta() {
         HttpServletRequest req = mock(HttpServletRequest.class);
-        // no resolver -> getResourceResolver returns null -> NPE guarded by 
null path only
-        // here defaultLoginResource is null and no param -> resourcePath null 
-> returns null
+        // no resolver, no parameter, no default -> nothing to map, returns 
null
         Assert.assertNull(AuthUtil.getMappedLoginResourcePath(req, null));
     }
 
+    @Test
+    public void test_getMappedLoginResourcePath_without_resolver_jakarta() {
+        // anonymous request (e.g. the login page) carrying resource=... but 
no resolver attribute yet:
+        // the path cannot be mapped and must be returned as is instead of 
failing
+        HttpServletRequest req = mock(HttpServletRequest.class);
+        
when(req.getParameter(Authenticator.LOGIN_RESOURCE)).thenReturn("/content/protected");
+        Assert.assertEquals("/content/protected", 
AuthUtil.getMappedLoginResourcePath(req, null));
+        Assert.assertEquals("/def", 
AuthUtil.getMappedLoginResourcePath(mock(HttpServletRequest.class), "/def"));
+    }
+
     @Test
     public void test_setLoginResourceAttribute_jakarta() {
         HttpServletRequest req = mock(HttpServletRequest.class);
@@ -428,6 +437,13 @@ public class AuthUtilTest {
         Assert.assertNull(AuthUtil.getMappedLoginResourcePath(request2, null));
     }
 
+    @Test
+    public void test_getMappedLoginResourcePath_without_resolver_javax() {
+        javax.servlet.http.HttpServletRequest req = 
mock(javax.servlet.http.HttpServletRequest.class);
+        
when(req.getParameter(Authenticator.LOGIN_RESOURCE)).thenReturn("/content/protected");
+        Assert.assertEquals("/content/protected", 
AuthUtil.getMappedLoginResourcePath(req, null));
+    }
+
     @SuppressWarnings("deprecation")
     @Test
     public void test_setLoginResourceAttribute_javax() {
diff --git 
a/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorTest.java 
b/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorTest.java
index 7e67d49..e0f4af0 100644
--- a/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorTest.java
+++ b/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorTest.java
@@ -29,6 +29,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import junitx.util.PrivateAccessor;
 import org.apache.sling.api.SlingJakartaHttpServletRequest;
 import org.apache.sling.api.SlingJakartaHttpServletResponse;
+import org.apache.sling.api.auth.Authenticator;
 import org.apache.sling.api.auth.NoAuthenticationHandlerException;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
@@ -726,6 +727,42 @@ public class SlingAuthenticatorTest {
         verify(resolver).close();
     }
 
+    @Test
+    public void test_handleSecurity_anonymous_login_page_with_resource_param() 
throws Exception {
+        // the login form is requested anonymously and carries 
resource=<protected page>; the request
+        // must proceed to render the form (no redirect, no exception), see 
the 2.1.0 vote
+        final ResourceResolverFactory rrf = 
mock(ResourceResolverFactory.class);
+        final ResourceResolver resolver = mock(ResourceResolver.class);
+        
when(rrf.getResourceResolver(any(java.util.Map.class))).thenReturn(resolver);
+        final SlingAuthenticator auth = createAuthenticator(rrf);
+
+        final HttpServletRequest request = 
requestFor("/system/sling/form/login");
+        
when(request.getParameter(Authenticator.LOGIN_RESOURCE)).thenReturn("/content/protected");
+        final HttpServletResponse response = mock(HttpServletResponse.class);
+
+        Assert.assertTrue(auth.handleSecurity(request, response));
+        verify(response, never()).sendRedirect(anyString());
+        verify(resolver, never()).close();
+        
verify(request).setAttribute(AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER, 
resolver);
+    }
+
+    @Test
+    public void test_handleSecurity_anonymous_with_explicit_redirect_param() 
throws Exception {
+        // sling.auth.redirect on an anonymous request keeps redirecting, as 
before 2.1.0
+        final ResourceResolverFactory rrf = 
mock(ResourceResolverFactory.class);
+        final ResourceResolver resolver = mock(ResourceResolver.class);
+        
when(rrf.getResourceResolver(any(java.util.Map.class))).thenReturn(resolver);
+        final SlingAuthenticator auth = createAuthenticator(rrf);
+
+        final HttpServletRequest request = requestFor("/content");
+        
when(request.getParameter(AuthenticationSupport.REDIRECT_PARAMETER)).thenReturn("/content/target");
+        final HttpServletResponse response = mock(HttpServletResponse.class);
+
+        Assert.assertFalse(auth.handleSecurity(request, response));
+        verify(response).sendRedirect("/content/target");
+        verify(resolver).close();
+    }
+
     @Test
     public void test_handleSecurity_doing_auth() {
         final SlingAuthenticator auth = createAuthenticator(infoHolder("/", 
AuthenticationInfo.DOING_AUTH));
diff --git 
a/src/test/java/org/apache/sling/auth/core/spi/DefaultAuthenticationFeedbackHandlerTest.java
 
b/src/test/java/org/apache/sling/auth/core/spi/DefaultAuthenticationFeedbackHandlerTest.java
index 0ae6527..4f2f95a 100644
--- 
a/src/test/java/org/apache/sling/auth/core/spi/DefaultAuthenticationFeedbackHandlerTest.java
+++ 
b/src/test/java/org/apache/sling/auth/core/spi/DefaultAuthenticationFeedbackHandlerTest.java
@@ -97,4 +97,19 @@ public class DefaultAuthenticationFeedbackHandlerTest {
         DefaultAuthenticationFeedbackHandler handler = new 
DefaultAuthenticationFeedbackHandler();
         Assert.assertFalse(handler.authenticationSucceeded(request, response, 
new AuthenticationInfo("test")));
     }
+
+    @Test
+    public void test_redirect_resource_fallback_without_resolver() throws 
Exception {
+        // SLING-13025 fallback to the "resource" parameter, on a request that 
has no resolver
+        // attribute yet (anonymous access): must redirect to the unmapped 
path, not fail
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        HttpServletResponse response = mock(HttpServletResponse.class);
+        
when(request.getParameter(org.apache.sling.api.auth.Authenticator.LOGIN_RESOURCE))
+                .thenReturn("/content/protected");
+        when(request.getContextPath()).thenReturn("");
+        when(request.getServletPath()).thenReturn("/content/protected");
+        when(request.getRequestURI()).thenReturn("/content/protected");
+        
Assert.assertTrue(DefaultAuthenticationFeedbackHandler.handleRedirect(request, 
response));
+        verify(response).sendRedirect("/content/protected");
+    }
 }
diff --git 
a/src/test/java/org/apache/sling/auth/core/spi/DefaultJakartaAuthenticationFeedbackHandlerTest.java
 
b/src/test/java/org/apache/sling/auth/core/spi/DefaultJakartaAuthenticationFeedbackHandlerTest.java
index be79b7a..2f2ac28 100644
--- 
a/src/test/java/org/apache/sling/auth/core/spi/DefaultJakartaAuthenticationFeedbackHandlerTest.java
+++ 
b/src/test/java/org/apache/sling/auth/core/spi/DefaultJakartaAuthenticationFeedbackHandlerTest.java
@@ -118,4 +118,19 @@ public class 
DefaultJakartaAuthenticationFeedbackHandlerTest {
         DefaultJakartaAuthenticationFeedbackHandler handler = new 
DefaultJakartaAuthenticationFeedbackHandler();
         Assert.assertFalse(handler.authenticationSucceeded(request, response, 
new AuthenticationInfo("test")));
     }
+
+    @Test
+    public void test_redirect_resource_fallback_without_resolver() throws 
Exception {
+        // SLING-13025 fallback to the "resource" parameter, on a request that 
has no resolver
+        // attribute yet (anonymous access): must redirect to the unmapped 
path, not fail
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        HttpServletResponse response = mock(HttpServletResponse.class);
+        
when(request.getParameter(org.apache.sling.api.auth.Authenticator.LOGIN_RESOURCE))
+                .thenReturn("/content/protected");
+        when(request.getContextPath()).thenReturn("");
+        when(request.getServletPath()).thenReturn("/content/protected");
+        when(request.getRequestURI()).thenReturn("/content/protected");
+        
Assert.assertTrue(DefaultJakartaAuthenticationFeedbackHandler.handleRedirect(request,
 response));
+        verify(response).sendRedirect("/content/protected");
+    }
 }

Reply via email to