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

abernal pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jspwiki.git


The following commit(s) were added to refs/heads/master by this push:
     new 041cd532b JSPWIKI-1056: Enhance Absolute URL Utility Methods in 
HttpUtil
     new 0cf7a0429 Merge pull request #311 from arturobernalg/JSPWIKI-1056
041cd532b is described below

commit 041cd532b56b78bcc080b2ef7404ef8eda246548
Author: Arturo Bernal <[email protected]>
AuthorDate: Mon Oct 2 21:19:24 2023 +0200

    JSPWIKI-1056: Enhance Absolute URL Utility Methods in HttpUtil
    
    - Implement getAbsoluteUrl methods in HttpUtil to construct absolute URLs, 
considering various headers like X-Forwarded-Host, X-Forwarded-Proto, and 
X-Forwarded-Server.
    - This enhancement allows emails to now include absolute URLs for the login 
page, replacing the previous relative URLs.
---
 ChangeLog.md                                       |   7 ++
 .../src/main/java/org/apache/wiki/api/Release.java |   2 +-
 .../wiki/tasks/auth/SaveUserProfileTask.java       |   6 +-
 .../main/java/org/apache/wiki/util/HttpUtil.java   |  60 ++++++++++
 .../main/java/org/apache/wiki/util/URIScheme.java  |  56 ++++++++++
 .../java/org/apache/wiki/util/HttpUtilTest.java    | 121 ++++++++++++++++++++-
 6 files changed, 248 insertions(+), 4 deletions(-)

diff --git a/ChangeLog.md b/ChangeLog.md
index b35b0f975..4d3099f88 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -17,6 +17,13 @@ specific language governing permissions and limitations
 under the License.
 -->
 
+**2023-10-02  Arturo Bernal (abernal AT apache DOT org)**
+
+* _2.12.2-git-05_
+
+* [JSPWIKI-1056](https://issues.apache.org/jira/browse/JSPWIKI-1056) - URL in 
registration mail is relative while it should be absolute.
+
+
 **2023-10-02  Juan Pablo Santos (juanpablo AT apache DOT org)**
 
 * _2.12.2-git-04_
diff --git a/jspwiki-api/src/main/java/org/apache/wiki/api/Release.java 
b/jspwiki-api/src/main/java/org/apache/wiki/api/Release.java
index 5345f4a32..6b9594465 100644
--- a/jspwiki-api/src/main/java/org/apache/wiki/api/Release.java
+++ b/jspwiki-api/src/main/java/org/apache/wiki/api/Release.java
@@ -69,7 +69,7 @@ public final class Release {
      *  <p>
      *  If the build identifier is empty, it is not added.
      */
-    public static final String     BUILD         = "04";
+    public static final String     BUILD         = "05";
 
     /**
      *  This is the generic version string you should use when printing out 
the version.  It is of
diff --git 
a/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java
 
b/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java
index 17fa58ecc..7893ebdc5 100644
--- 
a/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java
+++ 
b/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java
@@ -27,6 +27,7 @@ import org.apache.wiki.auth.UserManager;
 import org.apache.wiki.auth.user.UserProfile;
 import org.apache.wiki.i18n.InternationalizationManager;
 import org.apache.wiki.tasks.TasksManager;
+import org.apache.wiki.util.HttpUtil;
 import org.apache.wiki.util.MailUtil;
 import org.apache.wiki.workflow.Outcome;
 import org.apache.wiki.workflow.Task;
@@ -78,12 +79,15 @@ public class SaveUserProfileTask extends Task {
                                                  
"notification.createUserProfile.accept.subject", app );
 
                 final String loginUrl = context.getEngine().getURL( 
ContextEnum.WIKI_LOGIN.getRequestContext(), null, null );
+
+                final String absoluteLoginUrl = 
HttpUtil.getAbsoluteUrl(context.getHttpRequest(), loginUrl);
+
                 final String content = i18n.get( 
InternationalizationManager.DEF_TEMPLATE, m_loc,
                                                  
"notification.createUserProfile.accept.content", app,
                                                  profile.getLoginName(),
                                                  profile.getFullname(),
                                                  profile.getEmail(),
-                                                 loginUrl );
+                                                 absoluteLoginUrl );
                 MailUtil.sendMessage( context.getEngine().getWikiProperties(), 
to, subject, content );
             } catch ( final AddressException e) {
                 LOG.debug( e.getMessage(), e );
diff --git a/jspwiki-util/src/main/java/org/apache/wiki/util/HttpUtil.java 
b/jspwiki-util/src/main/java/org/apache/wiki/util/HttpUtil.java
index ad956f162..33e089329 100644
--- a/jspwiki-util/src/main/java/org/apache/wiki/util/HttpUtil.java
+++ b/jspwiki-util/src/main/java/org/apache/wiki/util/HttpUtil.java
@@ -261,4 +261,64 @@ public final class HttpUtil {
         response.addCookie( cookie );
     }
 
+    /**
+     * Generates an absolute URL based on the given HttpServletRequest and a 
relative URL.
+     * This method takes into account various headers like X-Forwarded-Host, 
X-Forwarded-Proto,
+     * and X-Forwarded-Server to construct the absolute URL.
+     *
+     * @param request The HttpServletRequest object, used to obtain scheme, 
server name, and port.
+     * @param relativeUrl The relative URL to be appended to the base URL. Can 
be null.
+     * @return The absolute URL as a String.
+     * @since 2.12.2
+     */
+    public static String getAbsoluteUrl(final HttpServletRequest request, 
final String relativeUrl) {
+        StringBuilder baseUrl = new StringBuilder();
+
+        // Check for proxy headers
+        final String forwardedHost = request.getHeader("X-Forwarded-Host");
+        final String forwardedProto = request.getHeader("X-Forwarded-Proto");
+        final String forwardedServer = request.getHeader("X-Forwarded-Server");
+
+        if (forwardedHost != null && forwardedProto != null) {
+            baseUrl.append(forwardedProto).append("://").append(forwardedHost);
+        } else if (forwardedServer != null && forwardedProto != null) {
+            
baseUrl.append(forwardedProto).append("://").append(forwardedServer);
+        } else {
+            // Fallback to HttpServletRequest
+            final String scheme = request.getScheme();
+            final String serverName = request.getServerName();
+            final int port = request.getServerPort();
+
+            baseUrl.append(scheme).append("://").append(serverName);
+
+            // Include port only if it's not the default port for the scheme
+            if ((URIScheme.HTTP.same(scheme) && port != 80)
+                    || (URIScheme.HTTPS.same(scheme) && port != 443)) {
+                baseUrl.append(':');
+                baseUrl.append(port);
+            }
+        }
+
+        if (relativeUrl != null) {
+            baseUrl.append(relativeUrl);
+        }
+
+        return baseUrl.toString();
+    }
+
+
+    /**
+     * Generate an absolute URL based solely on the given HttpServletRequest.
+     * This is a convenience method that calls {@link 
#getAbsoluteUrl(HttpServletRequest, String)}
+     * with a null relative URL.
+     *
+     * @param request The HttpServletRequest object, used to obtain scheme, 
server name, and port.
+     * @return The absolute URL as a String.
+     * @see #getAbsoluteUrl(HttpServletRequest, String)
+     * @since 2.12.2
+     */
+    public static String getAbsoluteUrl(final HttpServletRequest request) {
+        return getAbsoluteUrl(request, null);
+    }
+
 }
diff --git a/jspwiki-util/src/main/java/org/apache/wiki/util/URIScheme.java 
b/jspwiki-util/src/main/java/org/apache/wiki/util/URIScheme.java
new file mode 100644
index 000000000..e19e5dbea
--- /dev/null
+++ b/jspwiki-util/src/main/java/org/apache/wiki/util/URIScheme.java
@@ -0,0 +1,56 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+ */
+
+package org.apache.wiki.util;
+
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Enumerates supported URI schemes.
+ *
+ */
+public enum URIScheme {
+
+    HTTP("http"), HTTPS("https");
+
+    public final String id;
+
+    URIScheme(final String id) {
+        if( StringUtils.isNotBlank( id ) ) {
+            this.id = id;
+        } else {
+            throw new IllegalArgumentException("id must not be blank");
+        }
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public boolean same(final String scheme) {
+        return id.equalsIgnoreCase(scheme);
+    }
+
+    @Override
+    public String toString() {
+        return id;
+    }
+
+}
diff --git a/jspwiki-util/src/test/java/org/apache/wiki/util/HttpUtilTest.java 
b/jspwiki-util/src/test/java/org/apache/wiki/util/HttpUtilTest.java
index 1afc63e1d..c96d9728a 100644
--- a/jspwiki-util/src/test/java/org/apache/wiki/util/HttpUtilTest.java
+++ b/jspwiki-util/src/test/java/org/apache/wiki/util/HttpUtilTest.java
@@ -23,6 +23,11 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 
 public class HttpUtilTest {
@@ -59,11 +64,123 @@ public class HttpUtilTest {
         final MockHttpServletRequest req = new MockHttpServletRequest( 
"/wiki", "/example" );
         req.setCookies( cookies );
 
-        Assertions.assertEquals( "value1", HttpUtil.retrieveCookieValue( req, 
"cookie1" ) );
-        Assertions.assertEquals( "value2", HttpUtil.retrieveCookieValue( req, 
"cookie2" ) );
+        assertEquals( "value1", HttpUtil.retrieveCookieValue( req, "cookie1" ) 
);
+        assertEquals( "value2", HttpUtil.retrieveCookieValue( req, "cookie2" ) 
);
         Assertions.assertNull( HttpUtil.retrieveCookieValue( req, "cookie3" ) 
);
         Assertions.assertNull( HttpUtil.retrieveCookieValue( req, "cookie4" ) 
);
         Assertions.assertNull( HttpUtil.retrieveCookieValue( req, "cookie5" ) 
);
     }
 
+    @Test
+    public void testGetAbsoluteUrlWithRelativeUrl() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getScheme()).thenReturn("http");
+        when(request.getServerName()).thenReturn("localhost");
+        when(request.getServerPort()).thenReturn(8080);
+
+        String relativeUrl = "/login";
+        String expected = "http://localhost:8080/login";;
+
+        String actual = HttpUtil.getAbsoluteUrl(request, relativeUrl);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testGetAbsoluteUrlWithoutRelativeUrl() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getScheme()).thenReturn("https");
+        when(request.getServerName()).thenReturn("localhost");
+        when(request.getServerPort()).thenReturn(443);
+
+        String expected = "https://localhost";;
+
+        String actual = HttpUtil.getAbsoluteUrl(request);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testGetAbsoluteUrlWithDefaultHttpPort() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getScheme()).thenReturn("http");
+        when(request.getServerName()).thenReturn("localhost");
+        when(request.getServerPort()).thenReturn(80);
+
+        String relativeUrl = "/login";
+        String expected = "http://localhost/login";;
+
+        String actual = HttpUtil.getAbsoluteUrl(request, relativeUrl);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testGetAbsoluteUrlWithDefaultHttpsPort() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getScheme()).thenReturn("https");
+        when(request.getServerName()).thenReturn("localhost");
+        when(request.getServerPort()).thenReturn(443);
+
+        String relativeUrl = "/login";
+        String expected = "https://localhost/login";;
+
+        String actual = HttpUtil.getAbsoluteUrl(request, relativeUrl);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testGetAbsoluteUrlWithForwardedHostAndProto() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getHeader("X-Forwarded-Host")).thenReturn("proxyhost");
+        when(request.getHeader("X-Forwarded-Proto")).thenReturn("https");
+
+        String relativeUrl = "/login";
+        String expected = "https://proxyhost/login";;
+
+        String actual = HttpUtil.getAbsoluteUrl(request, relativeUrl);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testGetAbsoluteUrlWithForwardedServerAndProto() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        
when(request.getHeader("X-Forwarded-Server")).thenReturn("proxyserver");
+        when(request.getHeader("X-Forwarded-Proto")).thenReturn("https");
+
+        String relativeUrl = "/login";
+        String expected = "https://proxyserver/login";;
+
+        String actual = HttpUtil.getAbsoluteUrl(request, relativeUrl);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testGetAbsoluteUrlWithNoForwardedHeaders() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getScheme()).thenReturn("http");
+        when(request.getServerName()).thenReturn("localhost");
+        when(request.getServerPort()).thenReturn(8080);
+
+        String relativeUrl = "/login";
+        String expected = "http://localhost:8080/login";;
+
+        String actual = HttpUtil.getAbsoluteUrl(request, relativeUrl);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testGetAbsoluteUrlWithAllHeaders() {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        
when(request.getHeader("X-Forwarded-Host")).thenReturn("forwardedHost");
+        
when(request.getHeader("X-Forwarded-Proto")).thenReturn("forwardedProto");
+        
when(request.getHeader("X-Forwarded-Server")).thenReturn("forwardedServer");
+        when(request.getScheme()).thenReturn("https");
+        when(request.getServerName()).thenReturn("localhost");
+        when(request.getServerPort()).thenReturn(443);
+
+        String expected = "forwardedProto://forwardedHost";
+
+        String actual = HttpUtil.getAbsoluteUrl(request);
+        assertEquals(expected, actual);
+    }
+
+
 }

Reply via email to