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

ashishvijaywargiya pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new ee52aa8a8d Improve controller request/view resolution - 
suffix-selected (#1644)
ee52aa8a8d is described below

commit ee52aa8a8d8e1594f02a39a7d68cc09a167004f9
Author: Ashish Vijaywargiya <[email protected]>
AuthorDate: Thu Aug 13 19:58:09 2026 +0530

    Improve controller request/view resolution - suffix-selected (#1644)
    
    Tighten how suffix-selected views resolve against their owning request
    map, with related view-map adjustments and test coverage.
---
 .../common/webcommon/WEB-INF/portal-controller.xml |  2 +-
 .../ofbiz/webapp/control/RequestHandler.java       | 38 +++++++++++++++++++++-
 .../ofbiz/webapp/control/RequestHandlerTests.java  | 31 ++++++++++++++++++
 3 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/framework/common/webcommon/WEB-INF/portal-controller.xml 
b/framework/common/webcommon/WEB-INF/portal-controller.xml
index 142516ca52..d6989dae07 100644
--- a/framework/common/webcommon/WEB-INF/portal-controller.xml
+++ b/framework/common/webcommon/WEB-INF/portal-controller.xml
@@ -162,7 +162,7 @@ under the License.
     </request-map>
     <request-map uri="LookupPortalPage"><security https="true" 
auth="true"/><response name="success" type="view" 
value="LookupPortalPage"/></request-map>
     <!-- View Mappings -->
-    <view-map name="showPortalPage" type="screen" 
page="component://common/widget/PortalPageScreens.xml#showPortalPage" 
auth="false"/>
+    <view-map name="showPortalPage" type="screen" 
page="component://common/widget/PortalPageScreens.xml#showPortalPage" 
auth="true"/>
     <view-map name="showPortlet" type="screen" 
page="component://common/widget/PortalPageScreens.xml#showPortlet"/>
     <view-map name="showPortletMainDecorator" type="screen" 
page="component://common/widget/PortalPageScreens.xml#showPortletMainDecorator"/>
     <view-map name="showPortletSimpleDecorator" type="screen" 
page="component://common/widget/PortalPageScreens.xml#showPortletSimpleDecorator"/>
diff --git 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
index 6d54d7f0a5..a43e6535a5 100644
--- 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
+++ 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
@@ -197,6 +197,39 @@ public final class RequestHandler {
         return Collections.emptyList();
     }
 
+    /**
+     * Determines whether {@code overrideViewUri} must not be reachable 
anonymously
+     * because it is the declared view response of at least one authenticated 
request map.
+     * <p>{@link #resolveURI} lets a client-supplied path suffix substitute 
the view
+     * rendered by a matched request map (see {@link #getOverrideViewUri}), 
and the
+     * substituted view is later authorized only against its own, often-unset,
+     * {@code ViewMap.auth} flag. Without this check, an anonymous request to 
any
+     * unauthenticated request map could render a view that is otherwise only 
ever
+     * reached through an authenticated request map, bypassing that request 
map's
+     * {@code security auth="true"} declaration entirely.
+     * @param ccfg the controller containing the current configuration
+     * @param overrideViewUri the view suffix taken from the request path, may 
be {@code null}
+     * @return true if {@code overrideViewUri} must not be rendered anonymously
+     */
+    static boolean overrideViewRequiresAuth(ControllerConfig ccfg, String 
overrideViewUri) {
+        if (overrideViewUri == null) {
+            return false;
+        }
+        for (List<RequestMap> maps : ccfg.getRequestMapMultiMap().values()) {
+            for (RequestMap requestMap : maps) {
+                if (!requestMap.isSecurityAuth()) {
+                    continue;
+                }
+                for (ConfigXMLReader.RequestResponse response : 
requestMap.getRequestResponseMap().values()) {
+                    if ("view".equals(response.getType()) && 
overrideViewUri.equals(response.getValue())) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     public static String getRequestUri(String path) {
         List<String> pathInfo = StringUtil.split(path, "/");
         if (UtilValidate.isEmpty(pathInfo)) {
@@ -606,7 +639,10 @@ public final class RequestHandler {
         }
 
         // Perform security check.
-        if (requestMap.isSecurityAuth()) {
+        // A view-suffix override (see resolveURI/getOverrideViewUri) must not 
let an anonymous
+        // request map render a view that is otherwise only ever handed out by 
an authenticated
+        // request map: treat that case as if this request map itself required 
auth.
+        if (requestMap.isSecurityAuth() || overrideViewRequiresAuth(ccfg, 
overrideViewUri)) {
             // Invoke the security handler
             // catch exceptions and throw RequestHandlerException if failed.
             if (Debug.verboseOn()) {
diff --git 
a/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
 
b/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
index 1614c64843..a3be4917ec 100644
--- 
a/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
+++ 
b/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
@@ -39,6 +39,7 @@ import java.util.Map;
 
 import jakarta.servlet.http.HttpServletRequest;
 
+import org.apache.ofbiz.base.util.UtilXml;
 import org.apache.ofbiz.base.util.collections.MultivaluedMapContext;
 import org.apache.ofbiz.webapp.control.ConfigXMLReader.ControllerConfig;
 import org.apache.ofbiz.webapp.control.ConfigXMLReader.RequestMap;
@@ -219,6 +220,36 @@ public class RequestHandlerTests {
             when(req.getPathInfo()).thenReturn("/baz");
             assertTrue(RequestHandler.resolveURI(ccfg, req).isEmpty());
         }
+
+        /**
+         * A view suffix must not let an anonymous request to an unrelated, 
unauthenticated
+         * request map (here "publicRequest") render a view that is only ever 
handed out as
+         * the response of an authenticated request map (here 
"protectedRequest") -
+         * path-suffix view substitution authorization bypass.
+         */
+        @Test
+        public void resolveURIOverrideViewAuth() throws Exception {
+            Element publicElement = UtilXml.readXmlDocument(
+                    "<request-map uri=\"publicRequest\">"
+                    + "<security https=\"true\" auth=\"false\"/>"
+                    + "<response name=\"success\" type=\"view\" 
value=\"publicView\"/>"
+                    + "</request-map>").getDocumentElement();
+            RequestMap publicRequest = new RequestMap(publicElement);
+
+            Element protectedElement = UtilXml.readXmlDocument(
+                    "<request-map uri=\"protectedRequest\">"
+                    + "<security https=\"true\" auth=\"true\"/>"
+                    + "<response name=\"success\" type=\"view\" 
value=\"protectedView\"/>"
+                    + "</request-map>").getDocumentElement();
+            RequestMap protectedRequest = new RequestMap(protectedElement);
+
+            reqMaps.putSingle("publicRequest", publicRequest);
+            reqMaps.putSingle("protectedRequest", protectedRequest);
+
+            assertTrue(RequestHandler.overrideViewRequiresAuth(ccfg, 
"protectedView"));
+            assertFalse(RequestHandler.overrideViewRequiresAuth(ccfg, 
"publicView"));
+            assertFalse(RequestHandler.overrideViewRequiresAuth(ccfg, null));
+        }
     }
 
     public static final class ResolveMethodTests {

Reply via email to