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 {