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

vyommani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new 986c5b610 RANGER-5838: Add constraintDefs to RangerServiceDef and 
constraint po… (#1309)
986c5b610 is described below

commit 986c5b610f422620356185a3973852525acafc9a
Author: Vyom Mani Tiwari <[email protected]>
AuthorDate: Fri Oct 16 15:48:53 2026 +0530

    RANGER-5838: Add constraintDefs to RangerServiceDef and constraint po… 
(#1309)
    
    * RANGER-5838: Add constraintDefs to RangerServiceDef and constraint policy 
model to RangerPolicy
    
    * Update RangerServiceDef.java: simplified new equals() methods
    
    * added subtype to RangerConstraintParamDef
    
    * fix checkstyle indentation
    
    ---------
    
    Co-authored-by: Madhan Neethiraj <[email protected]>
---
 .../apache/ranger/plugin/model/RangerPolicy.java   | 242 +++++++++++
 .../ranger/plugin/model/RangerServiceDef.java      | 475 +++++++++++++++++++++
 .../ranger/plugin/model/TestRangerPolicy.java      | 110 +++++
 .../ranger/plugin/model/TestRangerServiceDef.java  | 165 +++++++
 4 files changed, 992 insertions(+)

diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java
index 8b8f3d2ef..ff5604a53 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java
@@ -31,8 +31,10 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 @JsonAutoDetect(fieldVisibility = Visibility.ANY)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
@@ -44,6 +46,7 @@ public class RangerPolicy extends RangerBaseModelObject 
implements java.io.Seria
     public static final int POLICY_TYPE_DATAMASK  = 1;
     public static final int POLICY_TYPE_ROWFILTER = 2;
     public static final int POLICY_TYPE_AUDIT     = 3;
+    public static final int POLICY_TYPE_CONSTRAINTS = 4;
 
     public static final int[] POLICY_TYPES = new int[] {
             POLICY_TYPE_ACCESS,
@@ -79,6 +82,7 @@ public class RangerPolicy extends RangerBaseModelObject 
implements java.io.Seria
     private List<RangerPolicyItem>                  denyExceptions;
     private List<RangerDataMaskPolicyItem>          dataMaskPolicyItems;
     private List<RangerRowFilterPolicyItem>         rowFilterPolicyItems;
+    private List<RangerConstraintPolicyItem>        constraintPolicyItems;
     private String                                  serviceType;
     private Map<String, Object>                     options;
     private List<RangerValiditySchedule>            validitySchedules;
@@ -128,6 +132,7 @@ public RangerPolicy(String service, String name, Integer 
policyType, Integer pol
         setDenyExceptions(null);
         setDataMaskPolicyItems(null);
         setRowFilterPolicyItems(null);
+        setConstraintPolicyItems(null);
         setOptions(options);
         setValiditySchedules(validitySchedules);
         setPolicyLabels(policyLables);
@@ -158,6 +163,7 @@ public void updateFrom(RangerPolicy other) {
         setDenyExceptions(other.getDenyExceptions());
         setDataMaskPolicyItems(other.getDataMaskPolicyItems());
         setRowFilterPolicyItems(other.getRowFilterPolicyItems());
+        setConstraintPolicyItems(other.getConstraintPolicyItems());
         setServiceType(other.getServiceType());
         setOptions(other.getOptions());
         setValiditySchedules(other.getValiditySchedules());
@@ -434,6 +440,20 @@ public boolean 
addRowFilterPolicyItem(RangerRowFilterPolicyItem policyItem) {
         return rowFilterPolicyItems.add(policyItem);
     }
 
+    public List<RangerConstraintPolicyItem> getConstraintPolicyItems() {
+        return constraintPolicyItems;
+    }
+
+    public void setConstraintPolicyItems(List<RangerConstraintPolicyItem> 
constraintPolicyItems) {
+        this.constraintPolicyItems = nullSafeList(constraintPolicyItems);
+    }
+
+    public boolean addConstraintPolicyItem(RangerConstraintPolicyItem 
policyItem) {
+        constraintPolicyItems = getUpdatableList(constraintPolicyItems);
+
+        return constraintPolicyItems.add(policyItem);
+    }
+
     public Map<String, Object> getOptions() {
         return options;
     }
@@ -555,6 +575,12 @@ public void dedupStrings(Map<String, String> strTbl) {
                 policyItem.dedupStrings(strTbl);
             }
         }
+
+        if (CollectionUtils.isNotEmpty(constraintPolicyItems)) {
+            for (RangerPolicyItem policyItem : constraintPolicyItems) {
+                policyItem.dedupStrings(strTbl);
+            }
+        }
     }
 
     @Override
@@ -682,6 +708,16 @@ public StringBuilder toString(StringBuilder sb) {
         }
         sb.append("} ");
 
+        sb.append("constraintPolicyItems={");
+        if (constraintPolicyItems != null) {
+            for (RangerConstraintPolicyItem constraintPolicyItem : 
constraintPolicyItems) {
+                if (constraintPolicyItem != null) {
+                    constraintPolicyItem.toString(sb);
+                }
+            }
+        }
+        sb.append("} ");
+
         sb.append("options={");
         if (options != null) {
             for (Map.Entry<String, Object> e : options.entrySet()) {
@@ -1442,6 +1478,96 @@ public String toString() {
         }
     }
 
+    @JsonAutoDetect(fieldVisibility = Visibility.ANY)
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class RangerConstraintPolicyItem extends RangerPolicyItem 
implements java.io.Serializable {
+        private static final long serialVersionUID = 1L;
+
+        private RangerPolicyItemConstraintInfo constraintInfo;
+
+        public RangerConstraintPolicyItem() {
+            this(null, null, null, null, null, null, null);
+        }
+
+        public RangerConstraintPolicyItem(RangerPolicyItemConstraintInfo 
constraintInfo, List<RangerPolicyItemAccess> accesses, List<String> users, 
List<String> groups, List<String> roles, List<RangerPolicyItemCondition> 
conditions, Boolean delegateAdmin) {
+            super(accesses, users, groups, roles, conditions, delegateAdmin);
+
+            setConstraintInfo(constraintInfo);
+        }
+
+        public RangerPolicyItemConstraintInfo getConstraintInfo() {
+            return constraintInfo;
+        }
+
+        public void setConstraintInfo(RangerPolicyItemConstraintInfo 
constraintInfo) {
+            this.constraintInfo = constraintInfo == null ? new 
RangerPolicyItemConstraintInfo() : constraintInfo;
+        }
+
+        @Override
+        public void dedupStrings(Map<String, String> strTbl) {
+            super.dedupStrings(strTbl);
+
+            if (constraintInfo != null) {
+                constraintInfo.dedupStrings(strTbl);
+            }
+        }
+
+        @Override
+        public StringBuilder toString(StringBuilder sb) {
+            sb.append("RangerConstraintPolicyItem={");
+
+            super.toString(sb);
+
+            sb.append("constraintInfo={");
+            if (constraintInfo != null) {
+                constraintInfo.toString(sb);
+            }
+            sb.append("} ");
+
+            sb.append("}");
+
+            return sb;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(), constraintInfo);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+
+            if (this == obj) {
+                return true;
+            }
+
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+
+            RangerConstraintPolicyItem other = (RangerConstraintPolicyItem) 
obj;
+
+            if (constraintInfo == null) {
+                return other.constraintInfo == null;
+            } else {
+                return constraintInfo.equals(other.constraintInfo);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            toString(sb);
+
+            return sb.toString();
+        }
+    }
+
     @JsonAutoDetect(fieldVisibility = Visibility.ANY)
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     @JsonIgnoreProperties(ignoreUnknown = true)
@@ -1874,4 +2000,120 @@ public StringBuilder toString(StringBuilder sb) {
             return sb;
         }
     }
+
+    @JsonAutoDetect(fieldVisibility = Visibility.ANY)
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class RangerPolicyItemConstraintInfo implements 
java.io.Serializable {
+        private static final long serialVersionUID = 1L;
+
+        private String              constraintName;
+        private Map<String, Object> parameters;
+
+        public RangerPolicyItemConstraintInfo() {
+            this(null, null);
+        }
+
+        public RangerPolicyItemConstraintInfo(String constraintName, 
Map<String, Object> parameters) {
+            setConstraintName(constraintName);
+            setParameters(parameters);
+        }
+
+        public RangerPolicyItemConstraintInfo(RangerPolicyItemConstraintInfo 
that) {
+            setConstraintName(that.constraintName);
+            setParameters(that.parameters == null ? null : new 
HashMap<>(that.parameters));
+        }
+
+        public String getConstraintName() {
+            return constraintName;
+        }
+
+        public void setConstraintName(String constraintName) {
+            this.constraintName = constraintName;
+        }
+
+        public Map<String, Object> getParameters() {
+            return parameters;
+        }
+
+        public void setParameters(Map<String, Object> parameters) {
+            this.parameters = parameters == null ? new HashMap<>() : 
parameters;
+        }
+
+        public void dedupStrings(Map<String, String> strTbl) {
+            constraintName = StringUtil.dedupString(constraintName, strTbl);
+
+            if (MapUtils.isNotEmpty(parameters)) {
+                Map<String, Object> updated = new HashMap<>(parameters.size());
+
+                for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+                    Object value = entry.getValue();
+
+                    if (value instanceof String) {
+                        value = StringUtil.dedupString((String) value, strTbl);
+                    }
+
+                    updated.put(StringUtil.dedupString(entry.getKey(), 
strTbl), value);
+                }
+
+                parameters = updated;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(constraintName, parameters);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+
+            if (obj == null) {
+                return false;
+            }
+
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+
+            RangerPolicyItemConstraintInfo other = 
(RangerPolicyItemConstraintInfo) obj;
+
+            if (constraintName == null) {
+                if (other.constraintName != null) {
+                    return false;
+                }
+            } else if (!constraintName.equals(other.constraintName)) {
+                return false;
+            }
+
+            if (parameters == null) {
+                return other.parameters == null;
+            } else {
+                return parameters.equals(other.parameters);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            toString(sb);
+
+            return sb.toString();
+        }
+
+        public StringBuilder toString(StringBuilder sb) {
+            sb.append("RangerPolicyItemConstraintInfo={");
+
+            sb.append("constraintName={").append(constraintName).append("} ");
+            sb.append("parameters={").append(parameters).append("} ");
+
+            sb.append("}");
+
+            return sb;
+        }
+    }
 }
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java
index 4cc01fb77..a7a90dc63 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java
@@ -35,6 +35,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 @JsonAutoDetect(fieldVisibility = Visibility.ANY)
@@ -66,6 +67,7 @@ public class RangerServiceDef extends RangerBaseModelObject 
implements java.io.S
     private List<RangerEnumDef>            enums;
     private RangerDataMaskDef              dataMaskDef;
     private RangerRowFilterDef             rowFilterDef;
+    private List<RangerConstraintDef>      constraintDefs;
     private List<RangerAccessTypeDef>      markerAccessTypes; // read-only
 
     private transient volatile boolean normalized;
@@ -109,6 +111,7 @@ public RangerServiceDef(String name, String implClass, 
String label, String desc
         setEnums(enums);
         setDataMaskDef(dataMaskDef);
         setRowFilterDef(rowFilterDef);
+        setConstraintDefs(null);
         setMarkerAccessTypes(null);
     }
 
@@ -145,6 +148,7 @@ public void updateFrom(RangerServiceDef other) {
         setEnums(other.getEnums());
         setDataMaskDef(other.getDataMaskDef());
         setRowFilterDef(other.getRowFilterDef());
+        setConstraintDefs(other.getConstraintDefs());
         setMarkerAccessTypes(other.getMarkerAccessTypes());
     }
 
@@ -465,6 +469,26 @@ public void setRowFilterDef(RangerRowFilterDef 
rowFilterDef) {
         clearNormalized();
     }
 
+    public List<RangerConstraintDef> getConstraintDefs() {
+        return constraintDefs;
+    }
+
+    public void setConstraintDefs(List<RangerConstraintDef> constraintDefs) {
+        if (this.constraintDefs == null) {
+            this.constraintDefs = new ArrayList<>();
+        }
+
+        if (this.constraintDefs == constraintDefs) {
+            return;
+        }
+
+        this.constraintDefs.clear();
+
+        if (constraintDefs != null) {
+            this.constraintDefs.addAll(constraintDefs);
+        }
+    }
+
     public List<RangerAccessTypeDef> getMarkerAccessTypes() {
         return markerAccessTypes;
     }
@@ -803,6 +827,12 @@ public void dedupStrings(Map<String, String> strTbl) {
             rowFilterDef.dedupStrings(strTbl);
         }
 
+        if (constraintDefs != null) {
+            for (RangerConstraintDef constraintDef : constraintDefs) {
+                constraintDef.dedupStrings(strTbl);
+            }
+        }
+
         if (markerAccessTypes != null) {
             for (RangerAccessTypeDef accessType : markerAccessTypes) {
                 accessType.dedupStrings(strTbl);
@@ -912,6 +942,16 @@ public StringBuilder toString(StringBuilder sb) {
         }
         sb.append("} ");
 
+        sb.append("constraintDefs={");
+        if (constraintDefs != null) {
+            for (RangerConstraintDef constraintDef : constraintDefs) {
+                if (constraintDef != null) {
+                    constraintDef.toString(sb);
+                }
+            }
+        }
+        sb.append("} ");
+
         sb.append("markerAccessTypes={");
         if (markerAccessTypes != null) {
             for (RangerAccessTypeDef accessType : markerAccessTypes) {
@@ -3742,4 +3782,439 @@ public String toString() {
             return sb.toString();
         }
     }
+
+    @JsonAutoDetect(fieldVisibility = Visibility.ANY)
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class RangerConstraintDef implements java.io.Serializable {
+        private static final long serialVersionUID = 1L;
+
+        private Long                           itemId;
+        private String                         name;
+        private String                         label;
+        private String                         description;
+        private String                         rbKeyLabel;
+        private String                         rbKeyDescription;
+        private List<String>                   resources;
+        private List<String>                   accessTypes;
+        private List<RangerConstraintParamDef> parameters;
+        private Map<String, String>            options;
+
+        public RangerConstraintDef() {
+            setResources(null);
+            setAccessTypes(null);
+            setParameters(null);
+            setOptions(null);
+        }
+
+        public RangerConstraintDef(RangerConstraintDef other) {
+            setItemId(other.getItemId());
+            setName(other.getName());
+            setLabel(other.getLabel());
+            setDescription(other.getDescription());
+            setRbKeyLabel(other.getRbKeyLabel());
+            setRbKeyDescription(other.getRbKeyDescription());
+            setResources(other.getResources());
+            setAccessTypes(other.getAccessTypes());
+            setParameters(other.getParameters());
+            setOptions(other.getOptions() == null ? null : new 
HashMap<>(other.getOptions()));
+        }
+
+        public Long getItemId() {
+            return itemId;
+        }
+
+        public void setItemId(Long itemId) {
+            this.itemId = itemId;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+
+        public void setLabel(String label) {
+            this.label = label;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public void setDescription(String description) {
+            this.description = description;
+        }
+
+        public String getRbKeyLabel() {
+            return rbKeyLabel;
+        }
+
+        public void setRbKeyLabel(String rbKeyLabel) {
+            this.rbKeyLabel = rbKeyLabel;
+        }
+
+        public String getRbKeyDescription() {
+            return rbKeyDescription;
+        }
+
+        public void setRbKeyDescription(String rbKeyDescription) {
+            this.rbKeyDescription = rbKeyDescription;
+        }
+
+        public List<String> getResources() {
+            return resources;
+        }
+
+        public void setResources(List<String> resources) {
+            if (this.resources == null) {
+                this.resources = new ArrayList<>();
+            }
+
+            if (this.resources == resources) {
+                return;
+            }
+
+            this.resources.clear();
+
+            if (resources != null) {
+                this.resources.addAll(resources);
+            }
+        }
+
+        public List<String> getAccessTypes() {
+            return accessTypes;
+        }
+
+        public void setAccessTypes(List<String> accessTypes) {
+            if (this.accessTypes == null) {
+                this.accessTypes = new ArrayList<>();
+            }
+
+            if (this.accessTypes == accessTypes) {
+                return;
+            }
+
+            this.accessTypes.clear();
+
+            if (accessTypes != null) {
+                this.accessTypes.addAll(accessTypes);
+            }
+        }
+
+        public List<RangerConstraintParamDef> getParameters() {
+            return parameters;
+        }
+
+        public void setParameters(List<RangerConstraintParamDef> parameters) {
+            if (this.parameters == null) {
+                this.parameters = new ArrayList<>();
+            }
+
+            if (this.parameters == parameters) {
+                return;
+            }
+
+            this.parameters.clear();
+
+            if (parameters != null) {
+                this.parameters.addAll(parameters);
+            }
+        }
+
+        public Map<String, String> getOptions() {
+            return options;
+        }
+
+        public void setOptions(Map<String, String> options) {
+            this.options = options == null ? new HashMap<>() : options;
+        }
+
+        public void dedupStrings(Map<String, String> strTbl) {
+            name             = StringUtil.dedupString(name, strTbl);
+            label            = StringUtil.dedupString(label, strTbl);
+            description      = StringUtil.dedupString(description, strTbl);
+            rbKeyLabel       = StringUtil.dedupString(rbKeyLabel, strTbl);
+            rbKeyDescription = StringUtil.dedupString(rbKeyDescription, 
strTbl);
+            resources        = StringUtil.dedupStringsList(resources, strTbl);
+            accessTypes      = StringUtil.dedupStringsList(accessTypes, 
strTbl);
+            options          = StringUtil.dedupStringsMap(options, strTbl);
+
+            if (parameters != null) {
+                for (RangerConstraintParamDef parameter : parameters) {
+                    parameter.dedupStrings(strTbl);
+                }
+            }
+        }
+
+        public StringBuilder toString(StringBuilder sb) {
+            sb.append("RangerConstraintDef={");
+            sb.append("itemId={").append(itemId).append("} ");
+            sb.append("name={").append(name).append("} ");
+            sb.append("label={").append(label).append("} ");
+            sb.append("description={").append(description).append("} ");
+            sb.append("rbKeyLabel={").append(rbKeyLabel).append("} ");
+            sb.append("rbKeyDescription={").append(rbKeyDescription).append("} 
");
+
+            sb.append("resources={");
+            if (resources != null) {
+                for (String resource : resources) {
+                    if (resource != null) {
+                        sb.append(resource).append(" ");
+                    }
+                }
+            }
+            sb.append("} ");
+
+            sb.append("accessTypes={");
+            if (accessTypes != null) {
+                for (String accessType : accessTypes) {
+                    if (accessType != null) {
+                        sb.append(accessType).append(" ");
+                    }
+                }
+            }
+            sb.append("} ");
+
+            sb.append("parameters={");
+            if (parameters != null) {
+                for (RangerConstraintParamDef parameter : parameters) {
+                    if (parameter != null) {
+                        parameter.toString(sb);
+                    }
+                }
+            }
+            sb.append("} ");
+
+            sb.append("options={").append(options).append("} ");
+            sb.append("}");
+
+            return sb;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(itemId, name, label, description, rbKeyLabel, 
rbKeyDescription, resources, accessTypes, parameters, options);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            } else if (obj == null) {
+                return false;
+            } else if (getClass() != obj.getClass()) {
+                return false;
+            }
+
+            RangerConstraintDef other = (RangerConstraintDef) obj;
+
+            return Objects.equals(itemId, other.itemId) &&
+                    Objects.equals(name, other.name) &&
+                    Objects.equals(label, other.label) &&
+                    Objects.equals(description, other.description) &&
+                    Objects.equals(rbKeyLabel, other.rbKeyLabel) &&
+                    Objects.equals(rbKeyDescription, other.rbKeyDescription) &&
+                    Objects.equals(resources, other.resources) &&
+                    Objects.equals(accessTypes, other.accessTypes) &&
+                    Objects.equals(parameters, other.parameters) &&
+                    Objects.equals(options, other.options);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            toString(sb);
+
+            return sb.toString();
+        }
+    }
+
+    @JsonAutoDetect(fieldVisibility = Visibility.ANY)
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class RangerConstraintParamDef implements 
java.io.Serializable {
+        private static final long serialVersionUID = 1L;
+
+        private Long    itemId;
+        private String  name;
+        private String  label;
+        private String  description;
+        private String  type;
+        private String  subType;
+        private Boolean mandatory;
+        private String  defaultValue;
+        private String  rbKeyLabel;
+        private String  rbKeyDescription;
+
+        public RangerConstraintParamDef() {
+            setMandatory(null);
+        }
+
+        public RangerConstraintParamDef(RangerConstraintParamDef other) {
+            setItemId(other.getItemId());
+            setName(other.getName());
+            setLabel(other.getLabel());
+            setDescription(other.getDescription());
+            setType(other.getType());
+            setSubType(other.getSubType());
+            setMandatory(other.getMandatory());
+            setDefaultValue(other.getDefaultValue());
+            setRbKeyLabel(other.getRbKeyLabel());
+            setRbKeyDescription(other.getRbKeyDescription());
+        }
+
+        public Long getItemId() {
+            return itemId;
+        }
+
+        public void setItemId(Long itemId) {
+            this.itemId = itemId;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+
+        public void setLabel(String label) {
+            this.label = label;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public void setDescription(String description) {
+            this.description = description;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public void setType(String type) {
+            this.type = type;
+        }
+
+        public String getSubType() {
+            return subType;
+        }
+
+        public void setSubType(String subType) {
+            this.subType = subType;
+        }
+
+        public Boolean getMandatory() {
+            return mandatory;
+        }
+
+        public void setMandatory(Boolean mandatory) {
+            this.mandatory = mandatory == null ? Boolean.FALSE : mandatory;
+        }
+
+        public String getDefaultValue() {
+            return defaultValue;
+        }
+
+        public void setDefaultValue(String defaultValue) {
+            this.defaultValue = defaultValue;
+        }
+
+        public String getRbKeyLabel() {
+            return rbKeyLabel;
+        }
+
+        public void setRbKeyLabel(String rbKeyLabel) {
+            this.rbKeyLabel = rbKeyLabel;
+        }
+
+        public String getRbKeyDescription() {
+            return rbKeyDescription;
+        }
+
+        public void setRbKeyDescription(String rbKeyDescription) {
+            this.rbKeyDescription = rbKeyDescription;
+        }
+
+        public void dedupStrings(Map<String, String> strTbl) {
+            name             = StringUtil.dedupString(name, strTbl);
+            label            = StringUtil.dedupString(label, strTbl);
+            description      = StringUtil.dedupString(description, strTbl);
+            type             = StringUtil.dedupString(type, strTbl);
+            subType          = StringUtil.dedupString(subType, strTbl);
+            defaultValue     = StringUtil.dedupString(defaultValue, strTbl);
+            rbKeyLabel       = StringUtil.dedupString(rbKeyLabel, strTbl);
+            rbKeyDescription = StringUtil.dedupString(rbKeyDescription, 
strTbl);
+        }
+
+        public StringBuilder toString(StringBuilder sb) {
+            sb.append("RangerConstraintParamDef={");
+            sb.append("itemId={").append(itemId).append("} ");
+            sb.append("name={").append(name).append("} ");
+            sb.append("label={").append(label).append("} ");
+            sb.append("description={").append(description).append("} ");
+            sb.append("type={").append(type).append("} ");
+            sb.append("subType={").append(subType).append("} ");
+            sb.append("mandatory={").append(mandatory).append("} ");
+            sb.append("defaultValue={").append(defaultValue).append("} ");
+            sb.append("rbKeyLabel={").append(rbKeyLabel).append("} ");
+            sb.append("rbKeyDescription={").append(rbKeyDescription).append("} 
");
+            sb.append("}");
+
+            return sb;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(itemId, name, label, description, type, 
subType, mandatory, defaultValue, rbKeyLabel, rbKeyDescription);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            } else if (obj == null) {
+                return false;
+            } else if (getClass() != obj.getClass()) {
+                return false;
+            }
+
+            RangerConstraintParamDef other = (RangerConstraintParamDef) obj;
+
+            return Objects.equals(itemId, other.itemId) &&
+                    Objects.equals(name, other.name) &&
+                    Objects.equals(label, other.label) &&
+                    Objects.equals(description, other.description) &&
+                    Objects.equals(type, other.type) &&
+                    Objects.equals(subType, other.subType) &&
+                    Objects.equals(mandatory, other.mandatory) &&
+                    Objects.equals(defaultValue, other.defaultValue) &&
+                    Objects.equals(rbKeyLabel, other.rbKeyLabel) &&
+                    Objects.equals(rbKeyDescription, other.rbKeyDescription);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            toString(sb);
+
+            return sb.toString();
+        }
+    }
 }
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java
index 467fc0f34..36e44a0dd 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java
@@ -19,10 +19,15 @@
 
 package org.apache.ranger.plugin.model;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.ranger.authorization.utils.JsonUtils;
+import org.apache.ranger.authorization.utils.StringUtil;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerConstraintPolicyItem;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition;
+import 
org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemConstraintInfo;
 import 
org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo;
 import 
org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
@@ -234,7 +239,9 @@ public void test09_PolicySettersAndNullSafeSupplierV2() {
         // switch supplier to V2 and verify empty collections are immutable 
empty
         
RangerBaseModelObject.setNullSafeSupplier(RangerBaseModelObject.NULL_SAFE_SUPPLIER_V2);
         p.setPolicyItems(null);
+        p.setConstraintPolicyItems(null);
         Assertions.assertEquals(0, p.getPolicyItems().size());
+        Assertions.assertEquals(0, p.getConstraintPolicyItems().size());
         // revert to default
         
RangerBaseModelObject.setNullSafeSupplier((RangerBaseModelObject.NullSafeSupplier)
 null);
     }
@@ -282,6 +289,109 @@ public void test12_NestedEqualsNullPaths() {
         Assertions.assertEquals(rf1, rf2);
     }
 
+    @Test
+    public void test13_ConstraintPolicyItemsAndJson() throws Exception {
+        Assertions.assertEquals(4, RangerPolicy.POLICY_TYPE_CONSTRAINTS);
+        Assertions.assertEquals(3, RangerPolicy.POLICY_TYPES.length);
+        for (int policyType : RangerPolicy.POLICY_TYPES) {
+            Assertions.assertNotEquals(RangerPolicy.POLICY_TYPE_CONSTRAINTS, 
policyType);
+        }
+
+        Map<String, Object> parameters = new LinkedHashMap<>();
+        parameters.put("approver", "alice");
+        parameters.put("rowCount", 100);
+
+        RangerPolicyItemConstraintInfo info = new 
RangerPolicyItemConstraintInfo("requireApproval", parameters);
+        RangerConstraintPolicyItem item = new RangerConstraintPolicyItem(info, 
Collections.emptyList(), Collections.singletonList("bob"), 
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), 
Boolean.FALSE);
+
+        Assertions.assertEquals("requireApproval", 
item.getConstraintInfo().getConstraintName());
+        Assertions.assertEquals("alice", 
item.getConstraintInfo().getParameters().get("approver"));
+        Assertions.assertEquals(Boolean.FALSE, item.getDelegateAdmin());
+        Assertions.assertTrue(item.toString().contains("constraintInfo={"));
+        Assertions.assertTrue(item.toString().contains("requireApproval"));
+
+        item.setConstraintInfo(null);
+        Assertions.assertNotNull(item.getConstraintInfo());
+        
Assertions.assertTrue(item.getConstraintInfo().getParameters().isEmpty());
+        item.setConstraintInfo(info);
+
+        RangerConstraintPolicyItem copy = new RangerConstraintPolicyItem(new 
RangerPolicyItemConstraintInfo(item.getConstraintInfo()), item.getAccesses(), 
item.getUsers(), item.getGroups(), item.getRoles(), item.getConditions(), 
item.getDelegateAdmin());
+        Assertions.assertEquals(item, copy);
+        Assertions.assertEquals(item.hashCode(), copy.hashCode());
+
+        RangerPolicyItemConstraintInfo otherInfo = new 
RangerPolicyItemConstraintInfo("maxRows", parameters);
+        RangerConstraintPolicyItem otherItem = new 
RangerConstraintPolicyItem(otherInfo, item.getAccesses(), item.getUsers(), 
item.getGroups(), item.getRoles(), item.getConditions(), 
item.getDelegateAdmin());
+        Assertions.assertNotEquals(item, otherItem);
+
+        RangerPolicy policy = new RangerPolicy();
+        policy.setName("approve-transfer");
+        policy.setPolicyType(RangerPolicy.POLICY_TYPE_CONSTRAINTS);
+        policy.setConstraintPolicyItems(null);
+        Assertions.assertEquals(0, policy.getConstraintPolicyItems().size());
+        Assertions.assertTrue(policy.addConstraintPolicyItem(item));
+        Assertions.assertEquals(1, policy.getConstraintPolicyItems().size());
+
+        RangerPolicy updated = new RangerPolicy();
+        updated.updateFrom(policy);
+        Assertions.assertEquals(policy.getConstraintPolicyItems(), 
updated.getConstraintPolicyItems());
+        
Assertions.assertTrue(updated.toString().contains("constraintPolicyItems={"));
+
+        ObjectMapper mapper = JsonUtils.getMapper();
+        RangerPolicy accessPolicy = new RangerPolicy();
+        accessPolicy.setName("plain-access");
+        accessPolicy.setPolicyType(RangerPolicy.POLICY_TYPE_ACCESS);
+        
Assertions.assertFalse(mapper.writeValueAsString(accessPolicy).contains("constraintPolicyItems"));
+
+        String json = "{"
+                + "\"name\":\"approve-transfer\","
+                + "\"policyType\":4,"
+                + "\"unknownField\":\"ignore-me\","
+                + "\"constraintPolicyItems\":[{"
+                + "\"users\":[\"bob\"],"
+                + "\"delegateAdmin\":false,"
+                + "\"constraintInfo\":{"
+                + "\"constraintName\":\"requireApproval\","
+                + "\"parameters\":{\"approver\":\"alice\",\"rowCount\":100},"
+                + "\"unknownInfo\":\"ignore\""
+                + "}}]"
+                + "}";
+        RangerPolicy parsed = mapper.readValue(json, RangerPolicy.class);
+        Assertions.assertEquals(RangerPolicy.POLICY_TYPE_CONSTRAINTS, 
parsed.getPolicyType().intValue());
+        Assertions.assertEquals(1, parsed.getConstraintPolicyItems().size());
+
+        RangerPolicyItemConstraintInfo parsedInfo = 
parsed.getConstraintPolicyItems().get(0).getConstraintInfo();
+        Assertions.assertEquals("requireApproval", 
parsedInfo.getConstraintName());
+        Assertions.assertEquals("alice", 
parsedInfo.getParameters().get("approver"));
+        Assertions.assertEquals(Integer.valueOf(100), 
parsedInfo.getParameters().get("rowCount"));
+        Assertions.assertEquals(Collections.singletonList("bob"), 
parsed.getConstraintPolicyItems().get(0).getUsers());
+
+        RangerPolicy roundTrip = 
mapper.readValue(mapper.writeValueAsString(parsed), RangerPolicy.class);
+        Assertions.assertEquals(parsed.getConstraintPolicyItems(), 
roundTrip.getConstraintPolicyItems());
+
+        Map<String, String> strTbl = new HashMap<>();
+        String canonicalName = StringUtil.dedupString(new 
String("requireApproval"), strTbl);
+        String canonicalKey = StringUtil.dedupString(new String("approver"), 
strTbl);
+        String canonicalValue = StringUtil.dedupString(new String("alice"), 
strTbl);
+        parsedInfo.setConstraintName(new String("requireApproval"));
+        Map<String, Object> fresh = new HashMap<>();
+        fresh.put(new String("approver"), new String("alice"));
+        fresh.put("rowCount", 100);
+        parsedInfo.setParameters(fresh);
+        parsed.getConstraintPolicyItems().get(0).dedupStrings(strTbl);
+        Assertions.assertSame(canonicalName, parsedInfo.getConstraintName());
+        Assertions.assertEquals(Integer.valueOf(100), 
parsedInfo.getParameters().get("rowCount"));
+
+        boolean foundApprover = false;
+        for (Map.Entry<String, Object> entry : 
parsedInfo.getParameters().entrySet()) {
+            if ("approver".equals(entry.getKey())) {
+                Assertions.assertSame(canonicalKey, entry.getKey());
+                Assertions.assertSame(canonicalValue, entry.getValue());
+                foundApprover = true;
+            }
+        }
+        Assertions.assertTrue(foundApprover);
+    }
+
     private <T> List<T> getList(T value) {
         List<T> ret = new ArrayList<>();
 
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerServiceDef.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerServiceDef.java
index 408b18b01..c36eb6232 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerServiceDef.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerServiceDef.java
@@ -19,7 +19,11 @@
 
 package org.apache.ranger.plugin.model;
 
+import org.apache.ranger.authorization.utils.JsonUtils;
+import org.apache.ranger.authorization.utils.StringUtil;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef;
+import 
org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintParamDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef;
 import 
org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef;
@@ -345,4 +349,165 @@ public void test13_EnumDefSetElementsSameRefAndDedup() {
         String s = enumDef.toString();
         Assertions.assertTrue(s.contains("RangerEnumDef={"));
     }
+
+    @Test
+    public void test14_ConstraintDefsJsonRoundTrip() throws Exception {
+        RangerServiceDef empty = 
JsonUtils.getMapper().readValue("{\"name\":\"hive\"}", RangerServiceDef.class);
+        Assertions.assertEquals("hive", empty.getName());
+        Assertions.assertNotNull(empty.getDataMaskDef());
+        Assertions.assertNotNull(empty.getRowFilterDef());
+        Assertions.assertNotNull(empty.getConstraintDefs());
+        Assertions.assertTrue(empty.getConstraintDefs().isEmpty());
+        
Assertions.assertFalse(JsonUtils.getMapper().writeValueAsString(empty).contains("constraintDefs"));
+
+        RangerConstraintParamDef param = new RangerConstraintParamDef();
+        param.setItemId(1L);
+        param.setName("approver");
+        param.setLabel("Approver");
+        param.setDescription("User who must approve");
+        param.setType("user");
+        param.setSubType(null);
+        Assertions.assertNull(param.getSubType());
+        param.setMandatory(null);
+        Assertions.assertEquals(Boolean.FALSE, param.getMandatory());
+        param.setMandatory(Boolean.TRUE);
+        param.setDefaultValue("alice");
+        param.setRbKeyLabel("rb.approver");
+        param.setRbKeyDescription("rb.approver.desc");
+
+        RangerConstraintParamDef paramCopy = new 
RangerConstraintParamDef(param);
+        Assertions.assertEquals(param, paramCopy);
+        Assertions.assertEquals(param.hashCode(), paramCopy.hashCode());
+        Assertions.assertNull(paramCopy.getSubType());
+
+        RangerConstraintParamDef enumParam = new 
RangerConstraintParamDef(param);
+        enumParam.setName("maskExpression");
+        enumParam.setType("enum");
+        enumParam.setSubType("MaskTypes");
+        RangerConstraintParamDef enumCopy = new 
RangerConstraintParamDef(enumParam);
+        Assertions.assertEquals(enumParam, enumCopy);
+        Assertions.assertEquals(enumParam.hashCode(), enumCopy.hashCode());
+        Assertions.assertEquals("MaskTypes", enumCopy.getSubType());
+        Assertions.assertNotEquals(param, enumParam);
+        RangerConstraintParamDef parsedEnum = 
JsonUtils.getMapper().readValue("{\"type\":\"enum\",\"subType\":\"MaskTypes\"}",
 RangerConstraintParamDef.class);
+        Assertions.assertEquals("enum", parsedEnum.getType());
+        Assertions.assertEquals("MaskTypes", parsedEnum.getSubType());
+        
Assertions.assertTrue(JsonUtils.getMapper().writeValueAsString(enumParam).contains("\"subType\":\"MaskTypes\""));
+
+        RangerConstraintDef constraintDef = new RangerConstraintDef();
+        constraintDef.setItemId(1L);
+        constraintDef.setName("requireApproval");
+        constraintDef.setLabel("Require approval");
+        constraintDef.setDescription("Pause until approved");
+        constraintDef.setRbKeyLabel("rb.requireApproval");
+        constraintDef.setRbKeyDescription("rb.requireApproval.desc");
+        constraintDef.setResources(null);
+        Assertions.assertNotNull(constraintDef.getResources());
+        Assertions.assertTrue(constraintDef.getResources().isEmpty());
+        constraintDef.setResources(Arrays.asList("tool"));
+        constraintDef.setAccessTypes(Arrays.asList("execute"));
+        List<RangerConstraintParamDef> params = new ArrayList<>();
+        params.add(param);
+        constraintDef.setParameters(params);
+        constraintDef.setOptions(null);
+        Assertions.assertNotNull(constraintDef.getOptions());
+        Map<String, String> options = new HashMap<>();
+        options.put("scope", "tool");
+        constraintDef.setOptions(options);
+
+        
Assertions.assertTrue(constraintDef.toString().contains("RangerConstraintDef={"));
+        
Assertions.assertTrue(param.toString().contains("RangerConstraintParamDef={"));
+
+        RangerConstraintDef defCopy = new RangerConstraintDef(constraintDef);
+        Assertions.assertEquals(constraintDef, defCopy);
+        Assertions.assertEquals(constraintDef.hashCode(), defCopy.hashCode());
+
+        RangerConstraintDef different = new RangerConstraintDef(constraintDef);
+        different.setName("maxRows");
+        Assertions.assertNotEquals(constraintDef, different);
+
+        RangerServiceDef def = new RangerServiceDef();
+        def.setName("agent-studio");
+        def.setConstraintDefs(null);
+        Assertions.assertTrue(def.getConstraintDefs().isEmpty());
+        List<RangerConstraintDef> defs = new ArrayList<>();
+        defs.add(constraintDef);
+        def.setConstraintDefs(defs);
+        Assertions.assertEquals(1, def.getConstraintDefs().size());
+
+        List<RangerConstraintDef> sameRef = def.getConstraintDefs();
+        sameRef.add(new RangerConstraintDef());
+        def.setConstraintDefs(sameRef);
+        Assertions.assertEquals(2, def.getConstraintDefs().size());
+        List<RangerConstraintDef> reset = new ArrayList<>();
+        reset.add(constraintDef);
+        def.setConstraintDefs(reset);
+
+        RangerServiceDef copied = new RangerServiceDef();
+        copied.updateFrom(def);
+        Assertions.assertEquals(def.getName(), copied.getName());
+        Assertions.assertEquals(def.getConstraintDefs(), 
copied.getConstraintDefs());
+        Assertions.assertEquals(def.getDataMaskDef(), copied.getDataMaskDef());
+        Assertions.assertEquals(def.getRowFilterDef(), 
copied.getRowFilterDef());
+
+        def.normalize();
+        Assertions.assertEquals("requireApproval", 
def.getConstraintDefs().get(0).getName());
+        Assertions.assertEquals(Arrays.asList("tool"), 
def.getConstraintDefs().get(0).getResources());
+
+        String json = "{"
+                + "\"name\":\"agent-studio\","
+                + "\"unknownTop\":\"x\","
+                + "\"constraintDefs\":[{"
+                + "\"itemId\":1,"
+                + "\"name\":\"requireApproval\","
+                + "\"label\":\"Require approval\","
+                + "\"description\":\"Pause until approved\","
+                + "\"rbKeyLabel\":\"rb.requireApproval\","
+                + "\"rbKeyDescription\":\"rb.requireApproval.desc\","
+                + "\"resources\":[\"tool\"],"
+                + "\"accessTypes\":[\"execute\"],"
+                + "\"unknownDef\":\"x\","
+                + "\"parameters\":[{"
+                + "\"itemId\":1,"
+                + "\"name\":\"approver\","
+                + "\"label\":\"Approver\","
+                + "\"description\":\"User who must approve\","
+                + "\"type\":\"user\","
+                + "\"mandatory\":true,"
+                + "\"defaultValue\":\"alice\","
+                + "\"rbKeyLabel\":\"rb.approver\","
+                + "\"rbKeyDescription\":\"rb.approver.desc\","
+                + "\"unknownParam\":\"x\""
+                + "}],"
+                + "\"options\":{\"scope\":\"tool\"}"
+                + "}]"
+                + "}";
+        RangerServiceDef parsed = JsonUtils.getMapper().readValue(json, 
RangerServiceDef.class);
+        Assertions.assertEquals("agent-studio", parsed.getName());
+        Assertions.assertEquals(1, parsed.getConstraintDefs().size());
+        RangerConstraintDef parsedDef = parsed.getConstraintDefs().get(0);
+        Assertions.assertEquals(constraintDef, parsedDef);
+        Assertions.assertEquals(Boolean.TRUE, 
parsedDef.getParameters().get(0).getMandatory());
+        Assertions.assertEquals("alice", 
parsedDef.getParameters().get(0).getDefaultValue());
+        Assertions.assertNull(parsedDef.getParameters().get(0).getSubType());
+        Assertions.assertNotNull(parsed.getDataMaskDef());
+        Assertions.assertNotNull(parsed.getRowFilterDef());
+
+        Map<String, String> tbl = new HashMap<>();
+        String canonicalResource = StringUtil.dedupString(new String("tool"), 
tbl);
+        String canonicalName = StringUtil.dedupString(new 
String("requireApproval"), tbl);
+        String canonicalType    = StringUtil.dedupString(new String("user"), 
tbl);
+        String canonicalSubType = StringUtil.dedupString(new 
String("MaskTypes"), tbl);
+        parsedDef.setName(new String("requireApproval"));
+        List<String> resources = new ArrayList<>();
+        resources.add(new String("tool"));
+        parsedDef.setResources(resources);
+        parsedDef.getParameters().get(0).setType(new String("user"));
+        parsedDef.getParameters().get(0).setSubType(new String("MaskTypes"));
+        parsedDef.dedupStrings(tbl);
+        Assertions.assertSame(canonicalName, parsedDef.getName());
+        Assertions.assertSame(canonicalResource, 
parsedDef.getResources().get(0));
+        Assertions.assertSame(canonicalType, 
parsedDef.getParameters().get(0).getType());
+        Assertions.assertSame(canonicalSubType, 
parsedDef.getParameters().get(0).getSubType());
+    }
 }

Reply via email to