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

dhavaljrajpara pushed a commit to branch RANGER-5860
in repository https://gitbox.apache.org/repos/asf/ranger.git

commit fe0078bc351f828a932dbbb460a3c017b7fc6a77
Author: Dhaval Rajpara <[email protected]>
AuthorDate: Wed Oct 28 16:40:25 2026 +0530

    RANGER-5852 : Initial patch for render contraint policy in New Policy 
Create form
---
 .../main/webapp/react-webapp/src/utils/XAEnums.js  |   6 +
 .../main/webapp/react-webapp/src/utils/XAUtils.js  | 260 +++++++++++++++++++--
 .../main/webapp/react-webapp/src/utils/appState.js |  50 +++-
 .../NewPolicyForm/CreateNewPolicyForm.jsx          |   3 +-
 .../AccessRulesStep.jsx                            |   6 +
 .../PolicyPermissionItemVertical.jsx               | 211 +++++++++++++++--
 .../PolicyCreateStepperStepComp/ServiceStep.jsx    |  48 +++-
 .../src/views/Resources/ResourceComp.jsx           |   2 +-
 8 files changed, 537 insertions(+), 49 deletions(-)

diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js 
b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js
index 5b07e14e8..cfec1cdd8 100755
--- a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js
+++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js
@@ -551,6 +551,12 @@ export const RangerPolicyType = {
     label: "Row Level Filter",
     rbkey: "xa.enum.AccessResult.ACCESS_RESULT_DENIED",
     tt: "lbl.AccessResult_ACCESS_RESULT_DENIED"
+  },
+  RANGER_CONSTRAINT_POLICY_TYPE: {
+    value: 4,
+    label: "Constraint",
+    rbkey: "xa.enum.AccessResult.ACCESS_RESULT_ALLOWED",
+    tt: "lbl.AccessResult_ACCESS_RESULT_ALLOWED"
   }
 };
 
diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js 
b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
index c33cb351f..89b3916a3 100644
--- a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
+++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
@@ -46,7 +46,11 @@ import {
   cloneDeep,
   toUpper,
   groupBy,
-  isArray
+  isArray,
+  startsWith,
+  pickBy,
+  uniqBy,
+  difference
 } from "lodash";
 import { matchRoutes } from "react-router-dom";
 import dateFormat from "dateformat";
@@ -106,6 +110,10 @@ export const isRenderRowFilter = (rowFilterDef) => {
     : false;
 };
 
+export const isRenderConstraint = (constraintDef) => {
+  return constraintDef && constraintDef.length > 0 ? true : false;
+};
+
 // Returns the policy types (Access / Masking / Row Filter) supported by a 
service def.
 // Access is always supported; Masking and Row Filter depend on the service 
def.
 export const getSupportedPolicyTypes = (serviceDef) => {
@@ -113,13 +121,20 @@ export const getSupportedPolicyTypes = (serviceDef) => {
     RangerPolicyType.RANGER_ACCESS_POLICY_TYPE.value
   ];
   if (isRenderMasking(serviceDef?.dataMaskDef)) {
-    
supportedPolicyTypes.push(RangerPolicyType.RANGER_MASKING_POLICY_TYPE.value);
+    supportedPolicyTypes.push(
+      RangerPolicyType.RANGER_MASKING_POLICY_TYPE.value
+    );
   }
   if (isRenderRowFilter(serviceDef?.rowFilterDef)) {
     supportedPolicyTypes.push(
       RangerPolicyType.RANGER_ROW_FILTER_POLICY_TYPE.value
     );
   }
+  if (isRenderConstraint(serviceDef?.constraintDef)) {
+    supportedPolicyTypes.push(
+      RangerPolicyType.RANGER_CONSTRAINT_POLICY_TYPE.value
+    );
+  }
   return supportedPolicyTypes;
 };
 
@@ -1519,7 +1534,9 @@ export const requestDataTitle = (serviceType) => {
 //Policy condition evaluation
 
 export const policyConditionUpdatedJSON = (policyCond) => {
-  let newPolicyConditionJSON = [...(Array.isArray(policyCond) ? policyCond : 
[])];
+  let newPolicyConditionJSON = [
+    ...(Array.isArray(policyCond) ? policyCond : [])
+  ];
   newPolicyConditionJSON.filter(function (key) {
     if (!key?.uiHint || key?.uiHint == "") {
       if (
@@ -1545,12 +1562,67 @@ export const getResourcesDefVal = (serviceDef, 
policyType) => {
     RangerPolicyType.RANGER_ROW_FILTER_POLICY_TYPE.value == policyType
   ) {
     resources = sortBy(serviceDef.rowFilterDef.resources, "itemId");
+  } else if (
+    RangerPolicyType.RANGER_CONSTRAINT_POLICY_TYPE.value == policyType
+  ) {
+    resources = sortBy(getConstraintResources(serviceDef), "itemId");
   } else {
     resources = sortBy(serviceDef.resources, "itemId");
   }
   return resources;
 };
 
+export const getConstraintResources = (serviceDef) => {
+  let uniqueResources = uniq(flatMap(serviceDef?.constraintDefs, "resources"));
+  let result = filter(serviceDef?.resources, (item) =>
+    uniqueResources.includes(item.name)
+  );
+  //NOTE : CHANGE ISVALIDLEAF TO FALSE FOR CONSTRAINT POLICY
+  return map(result, (resource) => ({
+    ...resource,
+    isValidLeaf: false
+  }));
+};
+
+export const getConstraintNameFromField = (constraintNameField) => {
+  if (!constraintNameField) {
+    return null;
+  }
+  if (
+    typeof constraintNameField === "object" &&
+    constraintNameField.value != null
+  ) {
+    return constraintNameField.value;
+  }
+  if (typeof constraintNameField === "string") {
+    return constraintNameField;
+  }
+  return null;
+};
+
+export const getConstraintDefByName = (serviceDef, constraintName) => {
+  if (!serviceDef || !constraintName) {
+    return null;
+  }
+  return find(serviceDef.constraintDefs, { name: constraintName }) || null;
+};
+
+export const getAccessTypesForConstraintDef = (serviceDef, constraintDef) => {
+  const allAccessTypes = serviceDef?.accessTypes || [];
+  if (!constraintDef?.accessTypes?.length) {
+    return allAccessTypes;
+  }
+  const allowed = new Set(constraintDef.accessTypes);
+  return filter(allAccessTypes, (item) => allowed.has(item.name));
+};
+
+export const mapAccessTypesToSelectOptions = (accessTypes) => {
+  return sortBy(
+    (accessTypes || []).map(({ label, name: value }) => ({ label, value })),
+    [(option) => option.label.toLowerCase()]
+  );
+};
+
 // Get defult landing page
 
 export const getLandingPageURl = () => {
@@ -1727,7 +1799,11 @@ export const getResourceLevelKeysSorted = 
(serviceCompDetails) => {
   return grpResourcesKeys;
 };
 
-export const getAccessTypesByResource = (formValues, serviceCompDetails) => {
+export const getAccessTypesByResource = (
+  formValues,
+  serviceCompDetails,
+  getMatchedConstraint
+) => {
   if (!formValues || !serviceCompDetails) {
     return [];
   }
@@ -1747,41 +1823,127 @@ export const getAccessTypesByResource = (formValues, 
serviceCompDetails) => {
     formValues.policyType
   ) {
     srcOp = serviceCompDetails.rowFilterDef?.accessTypes || [];
+  } else if (
+    RangerPolicyType.RANGER_CONSTRAINT_POLICY_TYPE.value ==
+    formValues.policyType
+  ) {
+    srcOp =
+      getAccessTypesFromMatchedDefs(getMatchedConstraint, serviceCompDetails) 
||
+      [];
   } else {
     srcOp = serviceCompDetails.accessTypes;
   }
 
-  map(formValues.additionalResources, (resourceObj) => {
-    for (let i = grpResourcesKeys.length - 1; i >= 0; i--) {
-      let selectedResource = `resourceName-${grpResourcesKeys[i]}`;
-      if (
-        resourceObj[selectedResource] &&
-        resourceObj[selectedResource].value !== accessTypeNoneOption.value
-      ) {
-        if (resourceObj[selectedResource].accessTypeRestrictions?.length > 0) {
-          let op = [];
-          for (const name of resourceObj[selectedResource]
-            .accessTypeRestrictions) {
-            let typeOp = find(srcOp, { name });
-            if (typeOp) {
-              op.push(typeOp);
+  if (
+    RangerPolicyType.RANGER_CONSTRAINT_POLICY_TYPE.value !=
+    formValues.policyType
+  ) {
+    map(formValues.additionalResources, (resourceObj) => {
+      for (let i = grpResourcesKeys.length - 1; i >= 0; i--) {
+        let selectedResource = `resourceName-${grpResourcesKeys[i]}`;
+        if (
+          resourceObj[selectedResource] &&
+          resourceObj[selectedResource].value !== accessTypeNoneOption.value
+        ) {
+          if (
+            resourceObj[selectedResource].accessTypeRestrictions?.length > 0
+          ) {
+            let op = [];
+            for (const name of resourceObj[selectedResource]
+              .accessTypeRestrictions) {
+              let typeOp = find(srcOp, { name });
+              if (typeOp) {
+                op.push(typeOp);
+              }
             }
+            multiplePermissionItem = [...multiplePermissionItem, ...op];
+          } else {
+            multiplePermissionItem = [...srcOp];
           }
-          multiplePermissionItem = [...multiplePermissionItem, ...op];
-        } else {
-          multiplePermissionItem = [...srcOp];
+          break;
         }
-        break;
       }
-    }
-  });
-  srcOp = [...new Set(multiplePermissionItem)];
+    });
+    srcOp = [...new Set(multiplePermissionItem)];
+  }
   return sortBy(
     srcOp.map(({ label, name: value }) => ({ label, value })),
     [(o) => o.label.toLowerCase()]
   );
 };
 
+// // get constraint policy access types
+// export const getConstraintPolicyAccessTypes = (
+//   serviceCompDetails,
+//   formValues
+// ) => {
+//   const additionalResources = formValues?.additionalResources;
+//   const constraintDefs = serviceCompDetails?.constraintDefs;
+//   const allAccessTypes = serviceCompDetails?.accessTypes;
+
+//   if (
+//     !Array.isArray(additionalResources) ||
+//     !Array.isArray(constraintDefs) ||
+//     !Array.isArray(allAccessTypes)
+//   ) {
+//     return [];
+//   }
+
+//   // Helper: check if two arrays contain the exact same unique resources
+//   const isExactResourceMatch = (resA = [], resB = []) => {
+//     if (resA.length !== resB.length) return false;
+//     const setB = new Set(resB);
+//     return resA.every((name) => setB.has(name));
+//   };
+
+//   const matchedAccessTypeNames = new Set();
+//   const evaluatedSignatures = new Set();
+
+//   for (const row of additionalResources) {
+//     // Extract resource names in level order: ["caiiservicecrn", 
"namespace", ...]
+//     const resourceNames = [];
+//     for (const [key, val] of Object.entries(row)) {
+//       if (key.startsWith("resourceName-") && val?.name) {
+//         resourceNames.push(val.name);
+//       }
+//     }
+
+//     if (resourceNames.length === 0) continue;
+
+//     // Skip redundant evaluations if the same resource combination exists 
in another row
+//     const signature = resourceNames.slice().sort().join(",");
+//     if (evaluatedSignatures.has(signature)) continue;
+//     evaluatedSignatures.add(signature);
+
+//     // Find all constraintDefs that match this exact resource combination
+//     for (const def of constraintDefs) {
+//       if (isExactResourceMatch(resourceNames, def.resources)) {
+//         if (Array.isArray(def.accessTypes)) {
+//           def.accessTypes.forEach((type) => 
matchedAccessTypeNames.add(type));
+//         }
+//       }
+//     }
+//   }
+
+//   // Filter full accessType objects that exist in our matched names
+//   return allAccessTypes.filter((item) => 
matchedAccessTypeNames.has(item.name));
+// };
+
+// export const getAllConstraintAccessTypes = (serviceCompDetails) => {
+//   const constraintDefs = serviceCompDetails?.constraintDefs || [];
+//   const accessTypesList = serviceCompDetails?.accessTypes || [];
+
+//   // 1. Collect all unique accessType strings from every constraintDef
+//   const targetAccessTypeNames = new Set(
+//     flatMap(constraintDefs, (def) => def.accessTypes || [])
+//   );
+
+//   // 2. Filter the full objects matching those names
+//   return filter(accessTypesList, (item) =>
+//     targetAccessTypeNames.has(item.name)
+//   );
+// };
+
 export const prunePolicyItemAccessesToAllowedTypes = (
   form,
   selectedServiceComponentDef
@@ -1863,3 +2025,51 @@ export const getPolicyPermissionItemDisplayLbl = (
     }
   }
 };
+
+//Common function to get constraint policy items
+export const getMatchedConstraintDefs = (serviceCompDetails, formValues) => {
+  const additionalResources = formValues?.additionalResources || [];
+  const constraintDefs = serviceCompDetails?.constraintDefs || [];
+
+  if (isEmpty(additionalResources) || isEmpty(constraintDefs)) {
+    return [];
+  }
+
+  // Step 1: Extract resource names from each row (e.g. [['caiiservicecrn', 
'namespace', 'endpoint']])
+  const allSelectedResources = map(additionalResources, (row) => {
+    const resourceKeys = pickBy(row, (val, key) =>
+      startsWith(key, "resourceName-")
+    );
+    return map(resourceKeys, "name");
+  });
+
+  // Step 2: Helper to check if two arrays have the same items (regardless of 
order)
+  const isMatch = (arr1, arr2) =>
+    arr1.length === arr2.length && isEmpty(difference(arr1, arr2));
+
+  // Step 3: Find constraintDefs whose resources match any selected row
+  const matchedDefs = filter(constraintDefs, (def) =>
+    some(allSelectedResources, (selected) => isMatch(selected, def.resources))
+  );
+
+  // Step 4: Remove duplicates by def.name
+  return uniqBy(matchedDefs, "name");
+};
+
+export const getAccessTypesFromMatchedDefs = (
+  matchedDefs,
+  serviceCompDetails
+) => {
+  const allAccessTypes = serviceCompDetails?.accessTypes || [];
+
+  // Extract unique accessType names: ['execute', 'select', ...]
+  const accessTypeNames = _.chain(matchedDefs)
+    .flatMap("accessTypes")
+    .uniq()
+    .value();
+
+  // Filter full objects from serviceCompDetails.accessTypes
+  return _.filter(allAccessTypes, (item) =>
+    _.includes(accessTypeNames, item.name)
+  );
+};
diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/appState.js 
b/security-admin/src/main/webapp/react-webapp/src/utils/appState.js
index 7de43d08e..a15a7096d 100644
--- a/security-admin/src/main/webapp/react-webapp/src/utils/appState.js
+++ b/security-admin/src/main/webapp/react-webapp/src/utils/appState.js
@@ -32,16 +32,62 @@ function getServiceDef() {
   return serviceDefData;
 }
 
+function updateServiceDef(serviceDef, constraintDefs) {
+  return serviceDef.map((item) => {
+    if (item.id === 208) {
+      return { ...item, constraintDefs: constraintDefs }; // or shorthand { 
...item, constraintDefs }
+    }
+    return item;
+  });
+}
+
 function setServiceDef(
   serviceDef,
   tagServiceDef,
   gdsServiceDef,
   allServiceDefs
 ) {
-  serviceDefData.serviceDefs = serviceDef;
+  let constraintDefs = [
+    {
+      itemId: 1,
+      name: "requireApproval",
+      label: "Require approval",
+      resources: ["caiiservicecrn", "namespace", "endpoint"],
+      accessTypes: ["view", "access"],
+      parameters: [
+        {
+          itemId: 1,
+          name: "approver",
+          label: "Approver",
+          type: "user",
+          mandatory: true
+        }
+      ]
+    },
+    {
+      itemId: 2,
+      name: "maxRows",
+      label: "Maximum rows",
+      resources: ["caiiservicecrn", "namespace", "application"],
+      accessTypes: ["access", "manage"],
+      parameters: [
+        {
+          itemId: 1,
+          name: "rowCount",
+          type: "int",
+          mandatory: true
+        }
+      ]
+    }
+  ];
+
+  serviceDefData.serviceDefs = updateServiceDef(serviceDef, constraintDefs);
   serviceDefData.tagServiceDefs = tagServiceDef;
   serviceDefData.gdsServiceDef = gdsServiceDef;
-  serviceDefData.allServiceDefs = allServiceDefs;
+  serviceDefData.allServiceDefs = updateServiceDef(
+    allServiceDefs,
+    constraintDefs
+  );
 }
 
 export { getUserProfile, setUserProfile, getServiceDef, setServiceDef };
diff --git 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/CreateNewPolicyForm.jsx
 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/CreateNewPolicyForm.jsx
index 46273abd7..97ff12b19 100644
--- 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/CreateNewPolicyForm.jsx
+++ 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/CreateNewPolicyForm.jsx
@@ -669,7 +669,8 @@ const CreateNewPolicyForm = () => {
                     isDenyAllElse: false, // For deny all toggle
                     isEnabled: true, // Policy enabled by default
                     isAuditEnabled: true, // Audit enabled by default
-                    policyPriority: 0 // Normal priority by default
+                    policyPriority: 0, // Normal priority by default
+                    constraintPolicyItems: [{}] // For constraint
                   }}
                   blockUI={blockUI}
                   preventUnBlock={preventUnBlock}
diff --git 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/AccessRulesStep.jsx
 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/AccessRulesStep.jsx
index 448077256..4038fd0f7 100644
--- 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/AccessRulesStep.jsx
+++ 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/AccessRulesStep.jsx
@@ -132,6 +132,12 @@ const AccessRulesStep = ({
       title: "Row Filter Rules",
       msg: "Row filter rules grants access to resources when the specific 
criteria are met."
     });
+  } else if (policyType == 4) {
+    policyPermissionItems.push({
+      attrName: "constraintPolicyItems",
+      title: "Constraint Rules",
+      msg: "Constraint rules grants access to resources when the specific 
criteria are met."
+    });
   } else if (policyType == 0 && !enableDenyAndExceptions) {
     policyPermissionItems.push({
       attrName: "policyItems",
diff --git 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/PolicyPermissionItemVertical.jsx
 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/PolicyPermissionItemVertical.jsx
index 0ac3fd996..d153cfe5a 100644
--- 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/PolicyPermissionItemVertical.jsx
+++ 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/PolicyPermissionItemVertical.jsx
@@ -18,6 +18,7 @@
  */
 
 import React, { useState } from "react";
+import Select from "react-select";
 import { FieldArray } from "react-final-form-arrays";
 import {
   Row,
@@ -35,7 +36,12 @@ import UsersGroupsAndRolesSelectionComponent from 
"./UsersGroupsAndRolesSelectio
 import TagBasePermissionItem from "../../TagBasePermissionItem";
 import {
   getAccessTypesByResource,
-  getPolicyConditionDisplayLbl
+  getPolicyConditionDisplayLbl,
+  getMatchedConstraintDefs,
+  getConstraintNameFromField,
+  getConstraintDefByName,
+  getAccessTypesForConstraintDef,
+  mapAccessTypesToSelectOptions
 } from "Utils/XAUtils";
 import { CustomTooltip } from "Components/CommonComponents";
 import { getSelectedAccessTypesForRow } from "Utils/policyConditionUtils";
@@ -52,6 +58,7 @@ export default function PolicyPermissionItemVertical({
   const [showTagPermissionModalIndex, setShowTagPermissionModalIndex] =
     useState(null);
   const form = useForm(); // Get form instance
+  let getMatchedConstraint = [];
 
   const { conditionDefVal, actionReqsMap, leafResourceTypes } =
     usePolicyPermissionConditionContext({
@@ -61,12 +68,26 @@ export default function PolicyPermissionItemVertical({
       form,
       enableResourcePruneDefer
     });
-
+  if (
+    RangerPolicyType.RANGER_CONSTRAINT_POLICY_TYPE.value ==
+    formValues.policyType
+  ) {
+    getMatchedConstraint = getMatchedConstraintDefs(
+      serviceCompDetails,
+      formValues
+    );
+  }
   const accessTypeOptions = getAccessTypesByResource(
     formValues,
-    serviceCompDetails
+    serviceCompDetails,
+    getMatchedConstraint
   );
 
+  const constraintSelectOptions = getMatchedConstraint.map((constraint) => ({
+    label: constraint.label || constraint.name,
+    value: constraint.name
+  }));
+
   const getMaskingAccessTypeOptions = (index) => {
     if (serviceCompDetails?.dataMaskDef?.maskTypes?.length > 0) {
       if (
@@ -121,6 +142,14 @@ export default function PolicyPermissionItemVertical({
   // Required field validation
   const required = (value) => (value ? undefined : "Required");
 
+  const constraintSelectFirstMsg = "Please select constraint name first";
+
+  const handleConstraintNameChange = (input, fieldName, option) => {
+    input.onChange(option || null);
+    form.change(`${fieldName}.accesses`, undefined);
+    form.change(`${fieldName}.constraintInfo.parameters`, undefined);
+  };
+
   // Helper function to clear all data from a permission item
   const clearPermissionItem = (index) => {
     // Reset the entire permission item to an empty object
@@ -171,11 +200,81 @@ export default function PolicyPermissionItemVertical({
               </Row>
             )}
 
-            {fields.map((name, index) => (
+            {fields.map((name, index) => {
+              const isConstraintPolicy =
+                formValues?.policyType ===
+                RangerPolicyType.RANGER_CONSTRAINT_POLICY_TYPE.value;
+              const rowConstraintName = getConstraintNameFromField(
+                fields.value[index]?.constraintInfo?.constraintName
+              );
+              const rowConstraintDef = getConstraintDefByName(
+                serviceCompDetails,
+                rowConstraintName
+              );
+              const rowAccessTypeOptions = rowConstraintDef
+                ? mapAccessTypesToSelectOptions(
+                    getAccessTypesForConstraintDef(
+                      serviceCompDetails,
+                      rowConstraintDef
+                    )
+                  )
+                : [];
+              const permissionAccessOptions = isConstraintPolicy
+                ? rowAccessTypeOptions
+                : accessTypeOptions;
+              return (
               <React.Fragment key={name}>
                 <Row key={name} className="py-3">
                   {/* Rule Header */}
                   <Col sm={11}>
+                    {RangerPolicyType.RANGER_CONSTRAINT_POLICY_TYPE.value ==
+                      formValues?.policyType && (
+                      <Col xs={12} className="mb-4">
+                        <Row>
+                          <Col className="column-1-fixed text-end">
+                            <FormB.Label>Constraint Name</FormB.Label>
+                          </Col>
+                          <>
+                            <Col>
+                              <Field
+                                name={`${name}.constraintInfo.constraintName`}
+                                validate={required}
+                              >
+                                {({ input, meta }) => (
+                                  <>
+                                    <Select
+                                      inputId={`${name}-constraint-name`}
+                                      options={constraintSelectOptions}
+                                      value={input.value}
+                                      onChange={(option) =>
+                                        handleConstraintNameChange(
+                                          input,
+                                          name,
+                                          option
+                                        )
+                                      }
+                                      onBlur={input.onBlur}
+                                      placeholder="Select constraint"
+                                      tabSelectsValue={false}
+                                      isClearable
+                                      isDisabled={
+                                        constraintSelectOptions.length === 0
+                                      }
+                                    />
+                                    {meta.touched && meta.error && (
+                                      <span className="text-danger d-block 
mt-1">
+                                        {meta.error}
+                                      </span>
+                                    )}
+                                  </>
+                                )}
+                              </Field>
+                            </Col>
+                            <Col className="column-3-fixed"></Col>
+                          </>
+                        </Row>
+                      </Col>
+                    )}
                     {/* Permissions Section */}
                     <Col xs={12} className="mb-4">
                       <Row>
@@ -183,8 +282,12 @@ export default function PolicyPermissionItemVertical({
                           <FormB.Label>Permissions</FormB.Label>
                         </Col>
                         <Col>
-                          {/* Tag-based policy - use modal with table layout 
*/}
-                          {serviceCompDetails?.name === "tag" ? (
+                          {isConstraintPolicy && !rowConstraintName ? (
+                            <p className="text-muted mb-0">
+                              {constraintSelectFirstMsg}
+                            </p>
+                          ) : serviceCompDetails?.name === "tag" ? (
+                          /* Tag-based policy - use modal with table layout */
                             <Field name={`${name}.accesses`}>
                               {({ input, meta }) => (
                                 <>
@@ -265,7 +368,7 @@ export default function PolicyPermissionItemVertical({
                                   {/* Tag Permission Modal */}
                                   {showTagPermissionModalIndex === index && (
                                     <TagBasePermissionItem
-                                      options={accessTypeOptions}
+                                      options={permissionAccessOptions}
                                       inputVal={{
                                         ...input,
                                         onChange: (value) => {
@@ -298,8 +401,8 @@ export default function PolicyPermissionItemVertical({
                               {({ input, meta }) => {
                                 const currentValue = input.value || [];
                                 const allSelected =
-                                  accessTypeOptions.length > 0 &&
-                                  accessTypeOptions.every((access) =>
+                                  permissionAccessOptions.length > 0 &&
+                                  permissionAccessOptions.every((access) =>
                                     currentValue.some(
                                       (a) => a.type === access.value
                                     )
@@ -311,13 +414,12 @@ export default function PolicyPermissionItemVertical({
                                     input.onChange([]);
                                   } else {
                                     // Select all
-                                    const allAccesses = accessTypeOptions.map(
-                                      (a) => ({
+                                    const allAccesses =
+                                      permissionAccessOptions.map((a) => ({
                                         type: a.value,
                                         isAllowed: true,
                                         label: a.label
-                                      })
-                                    );
+                                      }));
                                     input.onChange(allAccesses);
                                   }
                                 };
@@ -327,7 +429,7 @@ export default function PolicyPermissionItemVertical({
                                     <Container fluid className="p-0">
                                       <div className="gx-3 gy-1 
permissions-checkboxes">
                                         {/* Select All Link - Only show if 
more than 1 option */}
-                                        {accessTypeOptions.length > 1 && (
+                                        {permissionAccessOptions.length > 1 && 
(
                                           <div className="mb-1">
                                             <FormB.Check
                                               type="checkbox"
@@ -339,7 +441,8 @@ export default function PolicyPermissionItemVertical({
                                             />
                                           </div>
                                         )}
-                                        {accessTypeOptions.map((access) => {
+                                        {permissionAccessOptions.map(
+                                          (access) => {
                                           const isChecked =
                                             Array.isArray(input.value) &&
                                             input.value.some(
@@ -382,7 +485,8 @@ export default function PolicyPermissionItemVertical({
                                               />
                                             </div>
                                           );
-                                        })}
+                                        }
+                                        )}
                                       </div>
                                     </Container>
                                     {meta.error && (
@@ -569,6 +673,78 @@ export default function PolicyPermissionItemVertical({
                       </Col>
                     )}
 
+                    {isConstraintPolicy && (
+                      <Col xs={12} className="mb-4">
+                        <Row>
+                          <Col className="column-1-fixed text-end">
+                            <FormB.Label>Parameters</FormB.Label>
+                          </Col>
+                          <Col>
+                            {!rowConstraintName ? (
+                              <p className="text-muted mb-0">
+                                {constraintSelectFirstMsg}
+                              </p>
+                            ) : (
+                              (rowConstraintDef?.parameters || []).map(
+                                (param) => (
+                                  <Row key={param.name} className="mb-3">
+                                    <Col sm={4} md={3} className="text-end">
+                                      <FormB.Label className="mb-0">
+                                        {param.label || param.name}
+                                        {param.mandatory ? (
+                                          <span className="required-mark">
+                                            *
+                                          </span>
+                                        ) : null}
+                                      </FormB.Label>
+                                    </Col>
+                                    <Col sm={8} md={6}>
+                                      <Field
+                                        
name={`${name}.constraintInfo.parameters.${param.name}`}
+                                        validate={
+                                          param.mandatory ? required : 
undefined
+                                        }
+                                      >
+                                        {({ input, meta }) => (
+                                          <>
+                                            <FormB.Control
+                                              {...input}
+                                              value={input.value ?? ""}
+                                              type={
+                                                param.type === "int"
+                                                  ? "number"
+                                                  : "text"
+                                              }
+                                              placeholder={
+                                                param.type === "user" ||
+                                                param.subType === "user" ||
+                                                param.type === "group" ||
+                                                param.type === "role"
+                                                  ? "Enter user, group, or 
role"
+                                                  : `Enter ${param.label || 
param.name}`
+                                              }
+                                              isInvalid={
+                                                meta.touched && meta.error
+                                              }
+                                            />
+                                            {meta.touched && meta.error && (
+                                              <FormB.Control.Feedback 
type="invalid">
+                                                {meta.error}
+                                              </FormB.Control.Feedback>
+                                            )}
+                                          </>
+                                        )}
+                                      </Field>
+                                    </Col>
+                                  </Row>
+                                )
+                              )
+                            )}
+                          </Col>
+                        </Row>
+                      </Col>
+                    )}
+
                     {/* Data Masking Option (for Masking policies) */}
                     {RangerPolicyType.RANGER_MASKING_POLICY_TYPE.value ==
                       formValues?.policyType && (
@@ -886,7 +1062,8 @@ export default function PolicyPermissionItemVertical({
                 </Row>
                 <hr />
               </React.Fragment>
-            ))}
+            );
+            })}
 
             {/* Add More Rules Button */}
             <Row className="my-3">
diff --git 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/ServiceStep.jsx
 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/ServiceStep.jsx
index 215545f88..8cbfd7e3c 100644
--- 
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/ServiceStep.jsx
+++ 
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/NewPolicyForm/PolicyCreateStepperStepComp/ServiceStep.jsx
@@ -30,7 +30,8 @@ import {
   isRenderRowFilter,
   getSelectBoxErrorStyles,
   isKeyAdmin,
-  isKMSAuditor
+  isKMSAuditor,
+  isRenderConstraint
 } from "Utils/XAUtils";
 
 const required = (value) => (value ? undefined : "Required");
@@ -287,8 +288,9 @@ const ServiceStep = ({
             {/* Policy Type */}
             {selectedServiceComponentDef &&
               (isRenderMasking(selectedServiceComponentDef.dataMaskDef) ||
-                isRenderRowFilter(
-                  selectedServiceComponentDef.rowFilterDef
+                isRenderRowFilter(selectedServiceComponentDef.rowFilterDef) ||
+                isRenderConstraint(
+                  selectedServiceComponentDef?.constraintDefs
                 )) && (
                 <FormB.Group as={Row} className="mb-4">
                   <FormB.Label column className="column-1-fixed text-end">
@@ -415,6 +417,46 @@ const ServiceStep = ({
                               </small>
                             </div>
                           )}
+
+                          {/* Constraint Policy - Value: 4 */}
+                          {isRenderConstraint(
+                            selectedServiceComponentDef?.constraintDefs
+                          ) && (
+                            <div
+                              className="card p-3 w-33"
+                              style={{
+                                cursor: "pointer",
+                                border:
+                                  input.value === 4
+                                    ? "2px solid #0b7fad"
+                                    : "1px solid #dee2e6"
+                              }}
+                              onClick={() => handlePolicyTypeChange(input, 4)}
+                            >
+                              <div className="form-check">
+                                <input
+                                  className="form-check-input"
+                                  type="radio"
+                                  name="policyType"
+                                  id="constraint"
+                                  value={4}
+                                  checked={input.value === 4}
+                                  onChange={() =>
+                                    handlePolicyTypeChange(input, 4)
+                                  }
+                                />
+                                <label
+                                  className="form-check-label"
+                                  htmlFor="constraint"
+                                >
+                                  <strong>Constraint</strong>
+                                </label>
+                              </div>
+                              <small className="text-muted mt-2">
+                                Applies constraints to the resource
+                              </small>
+                            </div>
+                          )}
                         </div>
                       )}
                     </Field>
diff --git 
a/security-admin/src/main/webapp/react-webapp/src/views/Resources/ResourceComp.jsx
 
b/security-admin/src/main/webapp/react-webapp/src/views/Resources/ResourceComp.jsx
index 119911325..8681bff1f 100644
--- 
a/security-admin/src/main/webapp/react-webapp/src/views/Resources/ResourceComp.jsx
+++ 
b/security-admin/src/main/webapp/react-webapp/src/views/Resources/ResourceComp.jsx
@@ -48,7 +48,7 @@ export default function ResourceComp(props) {
   const [rsrcState, setLoader] = useState({ loader: false, resourceKey: -1 });
   const toastId = useRef(null);
 
-  let resources = getResourcesDefVal(serviceCompDetails, policyType);
+  const resources = getResourcesDefVal(serviceCompDetails, policyType);
 
   useEffect(() => {
     if (rsrcState.loader) {

Reply via email to