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 0d17e7efd RANGER-5848: Add constraints to RangerAccessResult (#1320)
0d17e7efd is described below

commit 0d17e7efdd43756e7a3bdff9750fce2a3fba3dd8
Author: Vyom Mani Tiwari <[email protected]>
AuthorDate: Sat Oct 17 17:05:25 2026 +0530

    RANGER-5848: Add constraints to RangerAccessResult (#1320)
---
 .../plugin/policyengine/RangerAccessResult.java    |  73 ++++++++---
 .../policyengine/RangerConstraintResult.java       | 136 +++++++++++++++++++++
 .../ranger/plugin/service/RangerBasePlugin.java    |   4 +
 .../policyengine/TestRangerAccessResult.java       |  99 +++++++++++++++
 .../policyengine/TestRangerConstraintResult.java   |  98 +++++++++++++++
 5 files changed, 391 insertions(+), 19 deletions(-)

diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
index 8088b022f..76d942794 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
@@ -24,7 +24,10 @@
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.util.ServiceDefUtil;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -38,24 +41,25 @@ public class RangerAccessResult {
     private static final String KEY_ALLOWED_BY_DATASETS = "allowedByDatasets";
     private static final String KEY_ALLOWED_BY_PROJECTS = "allowedByProjects";
 
-    private final String              serviceName;
-    private final RangerServiceDef    serviceDef;
-    private final RangerAccessRequest request;
-
-    private final int                 policyType;  // Really, policy-type for 
audit purpose
-    private       boolean             isAccessDetermined;
-    private       boolean             isAllowed;
-    private       boolean             isAuditedDetermined;
-    private       boolean             isAudited;
-    private       long                auditPolicyId = -1;
-    private       String              auditLogId;
-    private       long                policyId      = -1;
-    private       int                 policyPriority;
-    private       String              zoneName;
-    private       Long                policyVersion;
-    private       long                evaluatedPoliciesCount;
-    private       String              reason;
-    private       Map<String, Object> additionalInfo;
+    private final String                       serviceName;
+    private final RangerServiceDef             serviceDef;
+    private final RangerAccessRequest          request;
+
+    private final int                          policyType;  // Really, 
policy-type for audit purpose
+    private       boolean                      isAccessDetermined;
+    private       boolean                      isAllowed;
+    private       boolean                      isAuditedDetermined;
+    private       boolean                      isAudited;
+    private       long                         auditPolicyId = -1;
+    private       String                       auditLogId;
+    private       long                         policyId      = -1;
+    private       int                          policyPriority;
+    private       String                       zoneName;
+    private       Long                         policyVersion;
+    private       long                         evaluatedPoliciesCount;
+    private       String                       reason;
+    private       Map<String, Object>          additionalInfo;
+    private       List<RangerConstraintResult> constraints;
 
     public RangerAccessResult(final int policyType, final String serviceName, 
final RangerServiceDef serviceDef, final RangerAccessRequest request) {
         this.serviceName            = serviceName;
@@ -83,6 +87,7 @@ public void setAccessResultFrom(final RangerAccessResult 
other) {
         this.evaluatedPoliciesCount = other.evaluatedPoliciesCount;
         this.reason                 = other.getReason();
         this.additionalInfo         = other.additionalInfo == null ? new 
HashMap<>() : new HashMap<>(other.additionalInfo);
+        this.constraints            = copyConstraints(other.constraints);
     }
 
     public void setAuditResultFrom(final RangerAccessResult other) {
@@ -138,6 +143,7 @@ public boolean getIsAllowed() {
     public void setIsAllowed(boolean isAllowed) {
         if (!isAllowed) {
             setIsAccessDetermined(true);
+            this.constraints = null;
         }
 
         this.isAllowed = isAllowed;
@@ -408,6 +414,29 @@ public void setAllowedByProjects(Set<String> projects) {
         }
     }
 
+    /**
+     * Constraints that matched this request. Empty when access is denied or 
nothing matched.
+     */
+    public List<RangerConstraintResult> getConstraints() {
+        return constraints == null ? Collections.emptyList() : constraints;
+    }
+
+    public void setConstraints(List<RangerConstraintResult> constraints) {
+        this.constraints = copyConstraints(constraints);
+    }
+
+    public void addConstraint(RangerConstraintResult constraint) {
+        if (constraint == null) {
+            return;
+        }
+
+        if (constraints == null) {
+            constraints = new ArrayList<>();
+        }
+
+        constraints.add(constraint);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -439,10 +468,16 @@ public StringBuilder toString(StringBuilder sb) {
                 
sb.append(entry.getKey()).append("=").append(entry.getValue()).append(", ");
             }
         }
-        sb.append("}");
+        sb.append("} ");
+
+        sb.append("constraints={").append(getConstraints()).append("}");
 
         sb.append("}");
 
         return sb;
     }
+
+    private static List<RangerConstraintResult> 
copyConstraints(List<RangerConstraintResult> constraints) {
+        return constraints == null || constraints.isEmpty() ? null : new 
ArrayList<>(constraints);
+    }
 }
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerConstraintResult.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerConstraintResult.java
new file mode 100644
index 000000000..7e7083fd4
--- /dev/null
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerConstraintResult.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+package org.apache.ranger.plugin.policyengine;
+
+import 
org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher;
+import 
org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class RangerConstraintResult {
+    public static final Comparator<RangerConstraintResult> 
CONSTRAINT_RESULT_COMPARATOR = new ConstraintResultComparator();
+
+    private final Map<String, Object> parameters;
+    private String                    constraintName;
+    private long                      policyId;
+    private Long                      policyVersion;
+    private int                       policyPriority;
+    private MatchType                 matchType;
+
+    public RangerConstraintResult(String constraintName, Map<String, Object> 
parameters, long policyId, Long policyVersion, int policyPriority, MatchType 
matchType) {
+        this.constraintName = constraintName;
+        this.parameters     = parameters == null ? new HashMap<>() : new 
HashMap<>(parameters);
+        this.policyId       = policyId;
+        this.policyVersion  = policyVersion;
+        this.policyPriority = policyPriority;
+        this.matchType      = matchType;
+    }
+
+    public String getConstraintName() {
+        return constraintName;
+    }
+
+    public Map<String, Object> getParameters() {
+        return parameters;
+    }
+
+    public long getPolicyId() {
+        return policyId;
+    }
+
+    public Long getPolicyVersion() {
+        return policyVersion;
+    }
+
+    public int getPolicyPriority() {
+        return policyPriority;
+    }
+
+    public MatchType getMatchType() {
+        return matchType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(constraintName, parameters, policyId, 
policyVersion, policyPriority, matchType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        RangerConstraintResult other = (RangerConstraintResult) obj;
+
+        return policyId == other.policyId &&
+                policyPriority == other.policyPriority &&
+                Objects.equals(policyVersion, other.policyVersion) &&
+                Objects.equals(constraintName, other.constraintName) &&
+                Objects.equals(matchType, other.matchType) &&
+                Objects.equals(parameters, other.parameters);
+    }
+
+    @Override
+    public String toString() {
+        return "RangerConstraintResult{constraintName=" + constraintName +
+                ", parameters=" + parameters +
+                ", policyId=" + policyId +
+                ", policyVersion=" + policyVersion +
+                ", policyPriority=" + policyPriority +
+                ", matchType=" + matchType +
+                "}";
+    }
+
+    /**
+     * Orders by constraint name, policy priority (higher first), match type, 
then policy id.
+     */
+    public static class ConstraintResultComparator implements 
Comparator<RangerConstraintResult>, Serializable {
+        @Override
+        public int compare(RangerConstraintResult me, RangerConstraintResult 
other) {
+            int ret = compareStrings(me.getConstraintName(), 
other.getConstraintName());
+
+            if (ret == 0) {
+                ret = Integer.compare(other.getPolicyPriority(), 
me.getPolicyPriority());
+            }
+
+            if (ret == 0) {
+                ret = 
RangerPolicyResourceMatcher.MATCH_TYPE_COMPARATOR.compare(me.getMatchType(), 
other.getMatchType());
+            }
+
+            if (ret == 0) {
+                ret = Long.compare(me.getPolicyId(), other.getPolicyId());
+            }
+
+            return ret;
+        }
+
+        private static int compareStrings(String me, String other) {
+            return me == null ? (other == null ? 0 : -1) : (other == null ? 1 
: me.compareTo(other));
+        }
+    }
+}
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
index f11f98265..686f5bf04 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
@@ -1423,6 +1423,10 @@ private void 
updateResultFromChainedResult(RangerAccessResult result, RangerAcce
             } else if (policyType == RangerPolicy.POLICY_TYPE_ROWFILTER) {
                 result.setFilterExpr(chainedResult.getFilterExpr());
             }
+
+            if (result.getIsAllowed()) {
+                result.setConstraints(chainedResult.getConstraints());
+            }
         }
 
         if (!result.getIsAuditedDetermined() && 
chainedResult.getIsAuditedDetermined()) {
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAccessResult.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAccessResult.java
new file mode 100644
index 000000000..8fcdbe628
--- /dev/null
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAccessResult.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+package org.apache.ranger.plugin.policyengine;
+
+import 
org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TestRangerAccessResult {
+    @Test
+    public void constraintsAreEmptyUntilOneIsAdded() {
+        RangerAccessResult result = newResult();
+
+        assertTrue(result.getConstraints().isEmpty());
+
+        result.addConstraint(null);
+        assertTrue(result.getConstraints().isEmpty());
+
+        RangerConstraintResult constraint = constraint(10L);
+
+        result.addConstraint(constraint);
+
+        assertEquals(Arrays.asList(constraint), result.getConstraints());
+    }
+
+    @Test
+    public void setConstraintsCopiesTheInputList() {
+        RangerAccessResult result = newResult();
+        List<RangerConstraintResult> constraints = new ArrayList<>();
+        RangerConstraintResult constraint = constraint(10L);
+
+        constraints.add(constraint);
+        result.setConstraints(constraints);
+        constraints.clear();
+
+        assertEquals(Arrays.asList(constraint), result.getConstraints());
+
+        result.setConstraints(new ArrayList<>());
+
+        assertTrue(result.getConstraints().isEmpty());
+    }
+
+    @Test
+    public void setAccessResultFromCopiesConstraints() {
+        RangerAccessResult source = newResult();
+        RangerAccessResult copied = newResult();
+        RangerConstraintResult constraint = constraint(10L);
+
+        source.addConstraint(constraint);
+        copied.setAccessResultFrom(source);
+
+        assertEquals(Arrays.asList(constraint), copied.getConstraints());
+    }
+
+    @Test
+    public void denyClearsConstraints() {
+        RangerAccessResult result = newResult();
+
+        result.addConstraint(constraint(10L));
+        result.setIsAllowed(false);
+
+        assertTrue(result.getConstraints().isEmpty());
+
+        result.addConstraint(constraint(11L));
+        result.setIsAllowed(true);
+
+        assertEquals(1, result.getConstraints().size());
+    }
+
+    private static RangerAccessResult newResult() {
+        return new RangerAccessResult(0, "agent-studio", null, null);
+    }
+
+    private static RangerConstraintResult constraint(long policyId) {
+        return new RangerConstraintResult("requireApproval", null, policyId, 
1L, 0, MatchType.SELF);
+    }
+}
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerConstraintResult.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerConstraintResult.java
new file mode 100644
index 000000000..26ead3d83
--- /dev/null
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerConstraintResult.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+package org.apache.ranger.plugin.policyengine;
+
+import 
org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TestRangerConstraintResult {
+    @Test
+    public void nullParametersBecomeAnEmptyMap() {
+        RangerConstraintResult result = result("requireApproval", null, 10L, 
3L, 0, MatchType.SELF);
+
+        assertNotNull(result.getParameters());
+        assertTrue(result.getParameters().isEmpty());
+    }
+
+    @Test
+    public void parametersAreCopiedFromTheInputMap() {
+        Map<String, Object> parameters = new HashMap<>();
+
+        parameters.put("approver", "alice");
+
+        RangerConstraintResult result = result("requireApproval", parameters, 
10L, 3L, 0, MatchType.SELF);
+
+        parameters.put("approver", "bob");
+        parameters.put("extra", "x");
+
+        assertEquals("alice", result.getParameters().get("approver"));
+        assertFalse(result.getParameters().containsKey("extra"));
+        assertEquals(1, result.getParameters().size());
+    }
+
+    @Test
+    public void equalsAndHashCodeIncludePolicyPriority() {
+        RangerConstraintResult result = result("requireApproval", 
mapOf("approver", "alice"), 10L, 3L, 0, MatchType.SELF);
+        RangerConstraintResult same   = result("requireApproval", 
mapOf("approver", "alice"), 10L, 3L, 0, MatchType.SELF);
+        RangerConstraintResult higher = result("requireApproval", 
mapOf("approver", "alice"), 10L, 3L, 1, MatchType.SELF);
+
+        assertEquals(result, same);
+        assertEquals(result.hashCode(), same.hashCode());
+        assertNotEquals(result, higher);
+        assertTrue(result.toString().contains("policyPriority=0"));
+        assertTrue(result.toString().contains("requireApproval"));
+    }
+
+    @Test
+    public void comparatorOrdersByNamePriorityMatchTypeAndPolicyId() {
+        RangerConstraintResult maxRows          = result("maxRows", null, 20L, 
1L, 1, MatchType.DESCENDANT);
+        RangerConstraintResult lowPriority      = result("requireApproval", 
null, 30L, 1L, 0, MatchType.DESCENDANT);
+        RangerConstraintResult selfMatch        = result("requireApproval", 
null, 40L, 1L, 1, MatchType.SELF);
+        RangerConstraintResult laterPolicy      = result("requireApproval", 
null, 12L, 1L, 1, MatchType.DESCENDANT);
+        RangerConstraintResult earlierPolicy    = result("requireApproval", 
null, 11L, 1L, 1, MatchType.DESCENDANT);
+        List<RangerConstraintResult> constraints = Arrays.asList(laterPolicy, 
selfMatch, maxRows, lowPriority, earlierPolicy);
+
+        constraints.sort(RangerConstraintResult.CONSTRAINT_RESULT_COMPARATOR);
+
+        assertEquals(Arrays.asList(maxRows, selfMatch, earlierPolicy, 
laterPolicy, lowPriority), constraints);
+    }
+
+    private static RangerConstraintResult result(String name, Map<String, 
Object> parameters, long policyId, Long policyVersion, int policyPriority, 
MatchType matchType) {
+        return new RangerConstraintResult(name, parameters, policyId, 
policyVersion, policyPriority, matchType);
+    }
+
+    private static Map<String, Object> mapOf(String key, String value) {
+        Map<String, Object> ret = new HashMap<>();
+
+        ret.put(key, value);
+
+        return ret;
+    }
+}

Reply via email to