This is an automated email from the ASF dual-hosted git repository.
Lukas-Finster 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 e2d6f297e9 Implemented: Security configuration for rest-api
(OFBIZ-12514)
e2d6f297e9 is described below
commit e2d6f297e9f32167192663d0f7b5fd85fdf1da4d
Author: Lukas Finster <[email protected]>
AuthorDate: Tue Aug 11 17:53:52 2026 +0200
Implemented: Security configuration for rest-api (OFBIZ-12514)
* With this change, users need base Permission when authenticating with
rest-api. Additionally, primaryPermission and mainAction can now be
specified for each rest resource and operation. These are then added
to the context for the underlying service called.
Note that verification of whether the user in fact has these
permissions needs to be performed by the underlying service, as this
change only provides a framework for describing which permissions a
user needs.
---
framework/rest-api/config/rest-api.properties | 19 +++++++
.../data/RestSecurityPermissionSeedData.xml | 37 ++++++++++++
framework/rest-api/dtd/rest-api.xsd | 4 ++
framework/rest-api/ofbiz-component.xml | 8 ++-
.../apache/ofbiz/ws/rs/core/OFBizApiConfig.java | 5 +-
.../ofbiz/ws/rs/listener/ApiContextListener.java | 3 +
.../apache/ofbiz/ws/rs/model/ModelApiReader.java | 6 ++
.../apache/ofbiz/ws/rs/model/ModelOperation.java | 65 ++++++++++++++++++++-
.../apache/ofbiz/ws/rs/model/ModelResource.java | 66 +++++++++++++++++++++-
.../ofbiz/ws/rs/process/ServiceRequestHandler.java | 30 ++++++++++
.../ws/rs/security/auth/HttpBasicAuthFilter.java | 15 +++++
.../org/apache/ofbiz/ws/rs/util/OpenApiUtil.java | 16 +++---
.../org/apache/ofbiz/ws/rs/util/RestApiUtil.java | 2 +
13 files changed, 262 insertions(+), 14 deletions(-)
diff --git a/framework/rest-api/config/rest-api.properties
b/framework/rest-api/config/rest-api.properties
new file mode 100644
index 0000000000..cec719547a
--- /dev/null
+++ b/framework/rest-api/config/rest-api.properties
@@ -0,0 +1,19 @@
+###############################################################################
+# 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.
+###############################################################################
+rest-api.auth.baseSecurityGroupPermission = REST-API_ADMIN
diff --git a/framework/rest-api/data/RestSecurityPermissionSeedData.xml
b/framework/rest-api/data/RestSecurityPermissionSeedData.xml
new file mode 100644
index 0000000000..eeffa900c8
--- /dev/null
+++ b/framework/rest-api/data/RestSecurityPermissionSeedData.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<entity-engine-xml>
+
+ <!-- REST-API base permission -->
+ <SecurityPermission description="REST-API base permission for basic auth."
permissionId="REST-API_ADMIN"/>
+
+ <!-- REST-API security group and permission -->
+ <SecurityGroup groupId="REST-API" groupName="REST-API group"
description="Group for accessing the REST-API"/>
+ <SecurityGroupPermission fromDate="2026-01-01 12:00:00.0"
groupId="REST-API" permissionId="REST-API_ADMIN"/>
+
+ <!-- REST-API Test-User -->
+ <Party partyId="REST_API_TEST_USER" statusId="PARTY_ENABLED" />
+ <UserLogin userLoginId="REST_API_TEST_USER"
currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a"
partyId="REST_API_TEST_USER" hasLoggedOut="N" enabled="Y" />
+ <UserLoginSecurityGroup groupId="REST-API"
userLoginId="REST_API_TEST_USER" fromDate="2026-01-01 12:00:00.0" />
+
+ <!-- DemoAdmin -->
+ <UserLoginSecurityGroup groupId="REST-API" userLoginId="admin"
fromDate="2026-01-01 12:00:00.0" />
+
+</entity-engine-xml>
\ No newline at end of file
diff --git a/framework/rest-api/dtd/rest-api.xsd
b/framework/rest-api/dtd/rest-api.xsd
index 8c2fbd7938..2c9f4fb740 100644
--- a/framework/rest-api/dtd/rest-api.xsd
+++ b/framework/rest-api/dtd/rest-api.xsd
@@ -44,6 +44,8 @@ under the License.
<xs:attribute name="description" type="xs:string"/>
<xs:attribute name="publish" type="xs:boolean" default="true"/>
<xs:attribute name="auth" type="xs:boolean" default="true"/>
+ <xs:attribute name="primaryPermission" type="xs:string"/>
+ <xs:attribute name="mainAction" type="xs:string"/>
<xs:attribute name="customHeaders" type="xs:string"/>
</xs:complexType>
</xs:element>
@@ -83,6 +85,8 @@ under the License.
<xs:attribute name="description" type="xs:string"/>
<xs:attribute name="auth" type="xs:boolean" default="true"/>
<xs:attribute name="addApiResponses" type="xs:string"/>
+ <xs:attribute name="primaryPermission" type="xs:string"/>
+ <xs:attribute name="mainAction" type="xs:string"/>
<xs:attribute name="customHeaders" type="xs:string"/>
</xs:complexType>
</xs:element>
diff --git a/framework/rest-api/ofbiz-component.xml
b/framework/rest-api/ofbiz-component.xml
index d256e386dd..3b89960bcc 100644
--- a/framework/rest-api/ofbiz-component.xml
+++ b/framework/rest-api/ofbiz-component.xml
@@ -26,8 +26,12 @@ under the License.
<!-- place the config directory on the classpath to access configuration
files -->
<classpath type="dir" location="config"/>
-
- <!-- service resources: model(s), eca(s) and group definitions -->
+
+ <classpath type="dir" location="dtd"/>
+
+ <!-- entity resources: model(s), eca(s), group, and data definitions -->
+ <entity-resource type="data" reader-name="demo" loader="main"
location="data/RestSecurityPermissionSeedData.xml"/>
+
<service-resource type="model" loader="main"
location="servicedef/services.xml"/>
<test-suite loader="main" location="testdef/rest-apiTests.xml"/>
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/core/OFBizApiConfig.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/core/OFBizApiConfig.java
index aec2953bcd..cfc12e5bdc 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/core/OFBizApiConfig.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/core/OFBizApiConfig.java
@@ -149,6 +149,9 @@ public class OFBizApiConfig extends ResourceConfig {
.name(modelResource.getName());
for (ModelOperation op : modelResource.getOperations()) {
+ String serviceName = op.getService();
+ ServiceRequestHandler requestHandler = new
ServiceRequestHandler(serviceName, op.getPrimaryPermission(),
op.getMainAction());
+
String verb = op.getVerb().toUpperCase();
boolean isOtherThanGet = verb.matches(HttpMethod.POST + "|" +
HttpMethod.PUT + "|" + HttpMethod.PATCH);
String opPath = op.getPath();
@@ -168,7 +171,7 @@ public class OFBizApiConfig extends ResourceConfig {
if (op.isAuth()) {
methodBuilder.nameBindings(Secured.class);
}
- methodBuilder.handledBy(new
ServiceRequestHandler(op.getService()));
+ methodBuilder.handledBy(requestHandler);
}
// Register the current resource
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/listener/ApiContextListener.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/listener/ApiContextListener.java
index 06457ba542..427704c754 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/listener/ApiContextListener.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/listener/ApiContextListener.java
@@ -23,6 +23,7 @@ import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.DelegatorFactory;
import org.apache.ofbiz.service.LocalDispatcher;
import org.apache.ofbiz.service.ServiceContainer;
+import org.apache.ofbiz.webapp.WebAppUtil;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
@@ -62,6 +63,7 @@ public class ApiContextListener implements
ServletContextListener {
Debug.logInfo("Api Jersey Context initialized, delegator " + delegator
+ ", dispatcher", MODULE);
servletContext.setAttribute("delegator", delegator);
servletContext.setAttribute("dispatcher", dispatcher);
+ servletContext.setAttribute("security",
WebAppUtil.getSecurity(servletContext));
}
/**
@@ -76,6 +78,7 @@ public class ApiContextListener implements
ServletContextListener {
Debug.logInfo("Api Jersey Context destroyed, removing delegator and
dispatcher ", MODULE);
context.removeAttribute("delegator");
context.removeAttribute("dispatcher");
+ context.removeAttribute("security");
context = null;
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
index f8c7ef17b3..be74a9d882 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
@@ -109,6 +109,8 @@ public final class ModelApiReader {
.path(UtilXml.checkEmpty(resourceEle.getAttribute("path")).intern())
.publish(Boolean.parseBoolean(UtilXml.checkEmpty(resourceEle.getAttribute("publish")).intern()))
.auth(Boolean.parseBoolean(UtilXml.checkEmpty(resourceEle.getAttribute("auth")).intern()))
+
.primaryPermission(UtilXml.checkEmpty(resourceEle.getAttribute("primaryPermission")).intern())
+
.mainAction(UtilXml.checkEmpty(resourceEle.getAttribute("mainAction")).intern())
.customHeaders(UtilXml.checkEmpty(resourceEle.getAttribute("customHeaders")).intern());
}
@@ -124,6 +126,10 @@ public final class ModelApiReader {
.consumes(UtilXml.checkEmpty(operationEle.getAttribute("consumes")).intern())
.description(UtilXml.checkEmpty(operationEle.getAttribute("description")).intern())
.auth(Boolean.parseBoolean(UtilXml.checkEmpty(operationEle.getAttribute("auth")).intern()))
+
.primaryPermission(UtilXml.checkEmpty(operationEle.getAttribute("primaryPermission"),
+
resourceEle.getAttribute("primaryPermission")).intern())
+
.mainAction(UtilXml.checkEmpty(operationEle.getAttribute("mainAction"),
+
resourceEle.getAttribute("mainAction")).intern())
.addApiResponses(UtilXml.checkEmpty(operationEle.getAttribute("addApiResponses")).intern())
.customHeaders(UtilXml.checkEmpty(operationEle.getAttribute("customHeaders"),
resourceEle.getAttribute("customHeaders")).intern());
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
index cdc15f8f41..3c43732c7d 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
@@ -32,6 +32,8 @@ public class ModelOperation {
private String path;
private String description;
private boolean auth;
+ private String primaryPermission;
+ private String mainAction;
private String addApiResponses;
private String customHeaders;
@@ -53,6 +55,65 @@ public class ModelOperation {
this.auth = auth;
}
+ /**
+ * Gets the primary permission
+ *
+ * @return the primaryPermission
+ */
+ public String getPrimaryPermission() {
+ return primaryPermission;
+ }
+
+ /**
+ * Sets the primary permission
+ *
+ * @param primaryPermission the primaryPermission to set
+ */
+ public void setPrimaryPermission(String primaryPermission) {
+ this.primaryPermission = primaryPermission;
+ }
+
+ /**
+ * Sets the primaryPermission and returns this ModelOperation
+ *
+ * @param primaryPermission
+ * @return this ModelOperation
+ */
+ public ModelOperation primaryPermission(String primaryPermission) {
+ this.primaryPermission = primaryPermission;
+ return this;
+ }
+
+ /**
+ * Sets the mainAction (VIEW, CREATE, UPDATE, DELETE, ADMIN)
+ *
+ * @return the mainAction
+ */
+ public String getMainAction() {
+ return mainAction;
+ }
+
+ /**
+ * Gets the mainAction (VIEW, CREATE, UPDATE, DELETE, ADMIN)
+ *
+ * @param mainAction the mainAction to set
+ */
+ public void setMainAction(String mainAction) {
+ this.mainAction = mainAction;
+ }
+
+ /**
+ * Sets the mainAction (VIEW, CREATE, UPDATE, DELETE, ADMIN)
+ * and returns this instance
+ *
+ * @param mainAction
+ * @return this ModelOperation
+ */
+ public ModelOperation mainAction(String mainAction) {
+ this.mainAction = mainAction;
+ return this;
+ }
+
/**
* @return the addApiResponses
*/
@@ -319,8 +380,8 @@ public class ModelOperation {
@Override
public String toString() {
return "service: " + service + ", path: " + path + ", verb: " + verb +
", description: " + description
- + ", produces: " + produces + ", addApiResponses:" +
addApiResponses + ", customHeaders: "
- + customHeaders;
+ + ", produces: " + produces + ", primaryPermission: " +
primaryPermission + ", mainAction: "
+ + mainAction + ", addApiResponses:" + addApiResponses + ",
customHeaders: " + customHeaders;
}
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
index 2f01ffbf56..51ef1efcec 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
@@ -34,8 +34,11 @@ public class ModelResource {
private String description;
private boolean publish;
private boolean auth;
+ private String primaryPermission;
+ private String mainAction;
private String customHeaders;
+
/**
* Returns whether the user is authenticated.
*
@@ -65,6 +68,64 @@ public class ModelResource {
this.auth = auth;
return this;
}
+ /**
+ * Gets the primary permission
+ *
+ * @return the primaryPermission
+ */
+ public String getPrimaryPermission() {
+ return primaryPermission;
+ }
+
+ /**
+ * Sets the primary permission
+ *
+ * @param primaryPermission the primaryPermission to set
+ */
+ public void setPrimaryPermission(String primaryPermission) {
+ this.primaryPermission = primaryPermission;
+ }
+
+ /**
+ * Sets the primaryPermission and returns this ModelOperation
+ *
+ * @param primaryPermission
+ * @return this ModelResource
+ */
+ public ModelResource primaryPermission(String primaryPermission) {
+ this.primaryPermission = primaryPermission;
+ return this;
+ }
+
+ /**
+ * Sets the mainAction (VIEW, CREATE, UPDATE, DELETE, ADMIN)
+ *
+ * @return the mainAction
+ */
+ public String getMainAction() {
+ return mainAction;
+ }
+
+ /**
+ * Gets the mainAction (VIEW, CREATE, UPDATE, DELETE, ADMIN)
+ *
+ * @param mainAction the mainAction to set
+ */
+ public void setMainAction(String mainAction) {
+ this.mainAction = mainAction;
+ }
+
+ /**
+ * Sets the mainAction (VIEW, CREATE, UPDATE, DELETE, ADMIN)
+ * and returns this instance
+ *
+ * @param mainAction
+ * @return this ModelResource
+ */
+ public ModelResource mainAction(String mainAction) {
+ this.mainAction = mainAction;
+ return this;
+ }
/**
* Sets whether the item is published.
@@ -89,7 +150,7 @@ public class ModelResource {
* Adds an operation to this resource and returns this instance
* to support method chaining.
*
- * @param operation the {@link ModelOperation} to add
+ * @param operation the {@link ModelResource} to add
* @return this {@link ModelResource} instance
*/
public ModelResource addOperation(ModelOperation operation) {
@@ -307,7 +368,8 @@ public class ModelResource {
public String toString() {
// TODO Auto-generated method stub
return "name: " + name + ", path: " + path + ", displayName: " +
displayName + ", description: " + description
- + ", publish: " + publish + ", customHeaders: " +
customHeaders;
+ + ", publish: " + publish + ", primaryPermission: " +
primaryPermission + ", mainAction: " + mainAction
+ + ", customHeaders: " + customHeaders;
}
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java
index e1cb7256dd..a2e4002604 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java
@@ -40,11 +40,24 @@ public final class ServiceRequestHandler extends
RestRequestHandler {
private static final String MODULE = ServiceRequestHandler.class.getName();
private String service;
+ private String primaryPermission;
+ private String mainAction;
public ServiceRequestHandler(String service) {
this.service = service;
}
+ public ServiceRequestHandler(String service, String primaryPermission) {
+ this.service = service;
+ this.primaryPermission = primaryPermission;
+ }
+
+ public ServiceRequestHandler(String service, String primaryPermission,
String mainAction) {
+ this.service = service;
+ this.primaryPermission = primaryPermission;
+ this.mainAction = mainAction;
+ }
+
/**
* {@inheritDoc}
*
@@ -66,6 +79,7 @@ public final class ServiceRequestHandler extends
RestRequestHandler {
@Override
protected Response execute(ContainerRequestContext ctx, Map<String,
Object> arguments) {
ServiceNameContextHolder.set(service);
+ addSecurityParameters(arguments);
LocalDispatcher dispatcher = (LocalDispatcher)
getServletContext().getAttribute("dispatcher");
Map<String, Object> serviceContext = null;
try {
@@ -102,4 +116,20 @@ public final class ServiceRequestHandler extends
RestRequestHandler {
}
return svc;
}
+
+ /**
+ * Adds Security parameters to the the service parameters.
+ * @param arguments
+ */
+ private void addSecurityParameters(Map<String, Object> arguments) {
+ if (arguments != null) {
+ if (primaryPermission != null) {
+ arguments.put("primaryPermission", primaryPermission);
+ }
+ if (mainAction != null) {
+ arguments.put("mainAction", mainAction);
+ }
+ }
+ }
+
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/security/auth/HttpBasicAuthFilter.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/security/auth/HttpBasicAuthFilter.java
index 74585dff23..39ff92f692 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/security/auth/HttpBasicAuthFilter.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/security/auth/HttpBasicAuthFilter.java
@@ -25,7 +25,11 @@ import java.util.Map;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilHttp;
import org.apache.ofbiz.base.util.UtilMisc;
+import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.util.EntityUtilProperties;
+import org.apache.ofbiz.security.Security;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
import org.apache.ofbiz.service.ServiceUtil;
@@ -123,6 +127,17 @@ public class HttpBasicAuthFilter implements
ContainerRequestFilter {
throw new ForbiddenException(ServiceUtil.getErrorMessage(result));
}
+ String securityGroupPermission =
EntityUtilProperties.getPropertyValue("rest-api",
"rest-api.auth.baseSecurityGroupPermission",
+ (Delegator) servletContext.getAttribute("delegator"));
+ if (UtilValidate.isNotEmpty(securityGroupPermission)) {
+ Security security = (Security)
servletContext.getAttribute("security");
+ if (security != null &&
!security.hasPermission(securityGroupPermission, (GenericValue)
result.get("userLogin"))) {
+ Debug.logInfo("The specified user has no base permission to
use the REST-API.", MODULE);
+ throw new
ForbiddenException(ServiceUtil.getErrorMessage(result));
+ }
+ }
+
+
GenericValue userLogin = (GenericValue) result.get("userLogin");
httpRequest.setAttribute("userLogin", userLogin);
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
index 9c060622db..69329bbb1b 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
@@ -354,13 +354,15 @@ public final class OpenApiUtil {
parentSchema.setType("object");
List<String> required = UtilMisc.toList();
service.getInParamNamesMap().forEach((name, type) -> {
- ModelParam param = service.getParam(name);
- if (!param.isOptional()) {
- required.add(name);
- }
- Schema<?> attrSchema = getAttributeSchema(service, param);
- if (attrSchema != null) {
- parentSchema.addProperty(name, getAttributeSchema(service,
service.getParam(name)));
+ if (!RestApiUtil.SECURITY_PARAMS.contains(name) && (op == null ||
(op != null && !op.getCustomHeadersList().contains(name)))) {
+ ModelParam param = service.getParam(name);
+ if (!param.isOptional()) {
+ required.add(name);
+ }
+ Schema<?> attrSchema = getAttributeSchema(service, param);
+ if (attrSchema != null) {
+ parentSchema.addProperty(name, getAttributeSchema(service,
service.getParam(name)));
+ }
}
});
parentSchema.setRequired(required);
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
index a8e2ab5cf0..53962a8dff 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
@@ -34,6 +34,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.ofbiz.base.util.UtilGenerics;
+import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilProperties;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.service.ModelService;
@@ -50,6 +51,7 @@ import jakarta.ws.rs.core.Response.StatusType;
public final class RestApiUtil {
public static final String RESPONSE_STATUS_KEY = "httpResponseStatus";
+ public static final List<String> SECURITY_PARAMS =
UtilMisc.toList("primaryPermission", "mainAction");
private static final String DEFAULT_MSG_UI_LABEL_RESOURCE = "ApiUiLabels";
private static final String QUERY_STRING_SEPARATOR = "&";