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

mridulpathak 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 b361fe2b34 Fixed: moveContentJson lets a caller without UPDATE 
permission re-parent content associations
b361fe2b34 is described below

commit b361fe2b34f0825b996bdcc155962fc891c005e9
Author: Mridul Pathak <[email protected]>
AuthorDate: Mon Sep 21 17:06:07 2026 +0530

    Fixed: moveContentJson lets a caller without UPDATE permission re-parent 
content associations
    
    ContentJsonEvents.moveContent (backing POST 
/content/control/moveContentJson) performed no permission check at all, unlike 
the equivalent updateContent/createContentAssoc/removeContentAssoc services, 
which all require genericContentPermission UPDATE, so any authenticated user, 
including one with only view-only content roles, could call the endpoint 
directly and move a content association away from its current parent to any 
other regardless of ownership.
    
    Check UPDATE permission on both the old and the new parent content before 
performing the move, mirroring what createContentAssoc/removeContentAssoc each 
already require for the equivalent entity-level change.
---
 .../ofbiz/content/cms/ContentJsonEvents.java       | 26 +++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git 
a/applications/content/src/main/java/org/apache/ofbiz/content/cms/ContentJsonEvents.java
 
b/applications/content/src/main/java/org/apache/ofbiz/content/cms/ContentJsonEvents.java
index b6beb0dad7..bc8bc7ca3b 100644
--- 
a/applications/content/src/main/java/org/apache/ofbiz/content/cms/ContentJsonEvents.java
+++ 
b/applications/content/src/main/java/org/apache/ofbiz/content/cms/ContentJsonEvents.java
@@ -28,12 +28,14 @@ import java.util.Map;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
 
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.ofbiz.base.lang.JSON;
 import org.apache.ofbiz.base.util.UtilDateTime;
 import org.apache.ofbiz.base.util.UtilGenerics;
+import org.apache.ofbiz.base.util.UtilHttp;
 import org.apache.ofbiz.base.util.UtilMisc;
 import org.apache.ofbiz.entity.Delegator;
 import org.apache.ofbiz.entity.GenericEntityException;
@@ -43,6 +45,9 @@ import org.apache.ofbiz.entity.condition.EntityCondition;
 import org.apache.ofbiz.entity.transaction.TransactionUtil;
 import org.apache.ofbiz.entity.util.EntityUtil;
 import org.apache.ofbiz.entity.util.EntityQuery;
+import org.apache.ofbiz.service.GenericServiceException;
+import org.apache.ofbiz.service.LocalDispatcher;
+import org.apache.ofbiz.service.ServiceUtil;
 
 public class ContentJsonEvents {
 
@@ -83,8 +88,13 @@ public class ContentJsonEvents {
         return "success";
     }
 
-    public static String moveContent(HttpServletRequest request, 
HttpServletResponse response) throws GenericEntityException, IOException {
+    public static String moveContent(HttpServletRequest request, 
HttpServletResponse response)
+            throws GenericEntityException, GenericServiceException, 
IOException {
         final Delegator delegator = (Delegator) 
request.getAttribute("delegator");
+        final LocalDispatcher dispatcher = (LocalDispatcher) 
request.getAttribute("dispatcher");
+        final HttpSession session = request.getSession();
+        final GenericValue userLogin = (GenericValue) 
session.getAttribute("userLogin");
+        final Locale locale = UtilHttp.getLocale(request);
 
         final String contentIdTo = request.getParameter("contentIdTo");
         final String contentIdFrom = request.getParameter("contentIdFrom");
@@ -92,6 +102,11 @@ public class ContentJsonEvents {
         final String contentAssocTypeId = 
request.getParameter("contentAssocTypeId");
         final Timestamp fromDate = 
Timestamp.valueOf(request.getParameter("fromDate"));
 
+        // moving a content association modifies both the old and the new 
parent, so both need
+        // the same UPDATE permission that 
createContentAssoc/removeContentAssoc each enforce
+        checkUpdateContentPermission(dispatcher, userLogin, locale, 
contentIdFrom);
+        checkUpdateContentPermission(dispatcher, userLogin, locale, 
contentIdFromNew);
+
         final Timestamp now = UtilDateTime.nowTimestamp();
         GenericValue assoc = TransactionUtil.inTransaction(() -> {
             GenericValue oldAssoc = 
EntityQuery.use(delegator).from("ContentAssoc")
@@ -120,6 +135,15 @@ public class ContentJsonEvents {
         return "success";
     }
 
+    private static void checkUpdateContentPermission(LocalDispatcher 
dispatcher, GenericValue userLogin, Locale locale, String contentId)
+            throws GenericServiceException {
+        Map<String, Object> permSvcCtx = UtilMisc.toMap("userLogin", 
userLogin, "locale", locale, "mainAction", "UPDATE", "contentId", contentId);
+        Map<String, Object> permSvcResp = 
dispatcher.runSync("genericContentPermission", permSvcCtx);
+        if (ServiceUtil.isError(permSvcResp) || 
!Boolean.TRUE.equals(permSvcResp.get("hasPermission"))) {
+            throw new GenericServiceException("Permission denied to move 
content [" + contentId + "]");
+        }
+    }
+
     public static String deleteContent(HttpServletRequest request, 
HttpServletResponse response) throws GenericEntityException {
         Delegator delegator = (Delegator) request.getAttribute("delegator");
         String contentId = request.getParameter("contentId");

Reply via email to