From 11d825abc2c6c9b5a62882a4a83d5b0ce077fdf4 Mon Sep 17 00:00:00 2001 From: Vyom Mani Tiwari Date: Fri, 16 Oct 2026 10:38:07 +0530 Subject: [PATCH 1/2] RANGER-5842: Evaluate constraint policies in the policy engine and return applicable constraints in the access result --- .../validation/RangerServiceDefHelper.java | 76 +++ .../policyengine/RangerPolicyEngineImpl.java | 30 + .../policyengine/RangerPolicyRepository.java | 135 +++- .../RangerAbstractPolicyEvaluator.java | 20 + .../RangerConstraintPolicyItemEvaluator.java | 26 + ...rDefaultConstraintPolicyItemEvaluator.java | 60 ++ .../RangerDefaultPolicyEvaluator.java | 89 ++- .../RangerOptimizedPolicyEvaluator.java | 1 + .../RangerPolicyEvaluator.java | 7 + .../RangerPolicyItemEvaluator.java | 1 + .../RangerDefaultPolicyResourceMatcher.java | 32 +- .../plugin/util/RangerPolicyDeltaUtil.java | 2 +- .../TestRangerServiceDefHelper.java | 50 ++ .../plugin/policyengine/TestPolicyEngine.java | 27 + .../util/TestRangerPolicyDeltaUtil.java | 5 + .../test_policyengine_constraints.json | 595 ++++++++++++++++++ ...test_policyengine_constraints_no_defs.json | 83 +++ 17 files changed, 1209 insertions(+), 30 deletions(-) create mode 100644 agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerConstraintPolicyItemEvaluator.java create mode 100644 agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultConstraintPolicyItemEvaluator.java create mode 100644 agents-common/src/test/resources/policyengine/test_policyengine_constraints.json create mode 100644 agents-common/src/test/resources/policyengine/test_policyengine_constraints_no_defs.json diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java index 55945092bb..00acd5a05c 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java @@ -30,6 +30,7 @@ import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher; import org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher; @@ -39,6 +40,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -316,6 +318,80 @@ public Set> getResourceHierarchies(Integer policyType, C return ret; } + /** + * Resource hierarchy for one constraint. Names on that constraint def are ordered by resource level. + * A def that lists no resources uses the access hierarchy. A hierarchy is returned only when it contains every key. + */ + public Set> getResourceHierarchiesForConstraint(String constraint, Collection keys) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> getResourceHierarchiesForConstraint(constraint={}, keys={})", constraint, StringUtils.join(keys, ",")); + } + + Set> ret = new HashSet<>(); + RangerConstraintDef constraintDef = getConstraintDef(constraint); + + if (constraintDef == null) { + ret = Collections.emptySet(); + } else if (CollectionUtils.isEmpty(constraintDef.getResources())) { + ret.addAll(getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS, keys)); + } else { + List hierarchy = buildConstraintHierarchy(constraintDef.getResources()); + + if (CollectionUtils.isNotEmpty(hierarchy) && hierarchyHasAllResources(hierarchy, keys)) { + ret.add(hierarchy); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== getResourceHierarchiesForConstraint(constraint={}, keys={}): {}", constraint, StringUtils.join(keys, ","), StringUtils.join(ret, "")); + } + + return ret; + } + + private RangerConstraintDef getConstraintDef(String constraint) { + RangerConstraintDef ret = null; + + if (StringUtils.isNotEmpty(constraint) && CollectionUtils.isNotEmpty(delegate.servicedef.getConstraintDefs())) { + for (RangerConstraintDef constraintDef : delegate.servicedef.getConstraintDefs()) { + if (constraintDef != null && StringUtils.equals(constraint, constraintDef.getName())) { + ret = constraintDef; + + break; + } + } + } + + return ret; + } + + private List buildConstraintHierarchy(List resourceNames) { + Map resourceDefs = new HashMap<>(); + + if (CollectionUtils.isNotEmpty(delegate.servicedef.getResources())) { + for (RangerResourceDef resourceDef : delegate.servicedef.getResources()) { + if (resourceDef != null && resourceDef.getName() != null) { + resourceDefs.putIfAbsent(resourceDef.getName(), resourceDef); + } + } + } + + List ret = new ArrayList<>(); + Set added = new HashSet<>(); + + for (String resourceName : resourceNames) { + RangerResourceDef resourceDef = resourceDefs.get(resourceName); + + if (resourceDef != null && added.add(resourceName)) { + ret.add(resourceDef); + } + } + + ret.sort(Comparator.comparing(RangerResourceDef::getLevel, Comparator.nullsLast(Comparator.naturalOrder()))); + + return ret; + } + public boolean hierarchyHasAllResources(List hierarchy, Collection resourceNames) { if (LOG.isDebugEnabled()) { LOG.debug("==> hierarchyHasAllResources(hierarchy={}, resourceNames={})", StringUtils.join(hierarchy, ","), StringUtils.join(resourceNames, ",")); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java index e40b72e928..3a202d37dc 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java @@ -621,11 +621,41 @@ private RangerAccessResult zoneAwareAccessEvaluationWithNoAudit(RangerAccessRequ evaluateInlinePolicy(request, ret); + if (policyType == RangerPolicy.POLICY_TYPE_ACCESS && ret != null && ret.getIsAllowed()) { + evaluateConstraintPolicies(request, ret); + } + LOG.debug("<== RangerPolicyEngineImpl.zoneAwareAccessEvaluationWithNoAudit({}, policyType ={}): {}", request, policyType, ret); return ret; } + private void evaluateConstraintPolicies(RangerAccessRequest request, RangerAccessResult result) { + LOG.debug("==> RangerPolicyEngineImpl.evaluateConstraintPolicies({}, {})", request, result); + + RangerPolicyRepository repository = policyEngine.getRepositoryForZone(result.getZoneName()); + + if (repository == null || repository.getConstraintPolicyEvaluatorCount() == 0) { + LOG.debug("<== RangerPolicyEngineImpl.evaluateConstraintPolicies({}, {}): no constraint policies", request, result); + + return; + } + + List evaluators = repository.getLikelyMatchPolicyEvaluators(request, RangerPolicy.POLICY_TYPE_CONSTRAINTS); + + if (CollectionUtils.isNotEmpty(evaluators)) { + Date accessTime = request.getAccessTime() != null ? request.getAccessTime() : new Date(); + + for (RangerPolicyEvaluator evaluator : evaluators) { + if (evaluator.isApplicable(accessTime)) { + evaluator.evaluateConstraints(request, result); + } + } + } + + LOG.debug("<== RangerPolicyEngineImpl.evaluateConstraintPolicies({}, {}): constraints={}", request, result, result.getConstraints()); + } + private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, int policyType, String zoneName, RangerPolicyRepository policyRepository, RangerPolicyRepository tagPolicyRepository) { LOG.debug("==> RangerPolicyEngineImpl.evaluatePoliciesNoAudit({}, policyType={}, zoneName={})", request, policyType, zoneName); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java index 1f208a0e9d..b257858009 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java @@ -89,6 +89,7 @@ public class RangerPolicyRepository { private final Map> policyResourceTrie; private final Map> dataMaskResourceTrie; private final Map> rowFilterResourceTrie; + private final Map> constraintResourceTrie; private final Map> auditFilterResourceTrie; private final List auditPolicyEvaluators; private final List policies; @@ -96,26 +97,28 @@ public class RangerPolicyRepository { private List policyEvaluators; private List dataMaskPolicyEvaluators; private List rowFilterPolicyEvaluators; + private List constraintPolicyEvaluators; private Map policyEvaluatorsMap; private boolean isContextEnrichersShared; private boolean isPreCleaned; RangerPolicyRepository(final RangerPolicyRepository other, final List deltas, long policyVersion) { - this.serviceName = other.serviceName; - this.zoneName = other.zoneName; - this.appId = other.appId; - this.options = other.options; - this.pluginContext = other.pluginContext; - this.serviceDef = other.serviceDef; - this.policies = new ArrayList<>(other.policies); - this.policyEvaluators = new ArrayList<>(other.policyEvaluators); - this.dataMaskPolicyEvaluators = new ArrayList<>(other.dataMaskPolicyEvaluators); - this.rowFilterPolicyEvaluators = new ArrayList<>(other.rowFilterPolicyEvaluators); - this.auditPolicyEvaluators = new ArrayList<>(other.auditPolicyEvaluators); - this.auditModeEnum = other.auditModeEnum; - this.componentServiceName = other.componentServiceName; - this.componentServiceDef = other.componentServiceDef; - this.policyEvaluatorsMap = new HashMap<>(other.policyEvaluatorsMap); + this.serviceName = other.serviceName; + this.zoneName = other.zoneName; + this.appId = other.appId; + this.options = other.options; + this.pluginContext = other.pluginContext; + this.serviceDef = other.serviceDef; + this.policies = new ArrayList<>(other.policies); + this.policyEvaluators = new ArrayList<>(other.policyEvaluators); + this.dataMaskPolicyEvaluators = new ArrayList<>(other.dataMaskPolicyEvaluators); + this.rowFilterPolicyEvaluators = new ArrayList<>(other.rowFilterPolicyEvaluators); + this.auditPolicyEvaluators = new ArrayList<>(other.auditPolicyEvaluators); + this.constraintPolicyEvaluators = new ArrayList<>(other.constraintPolicyEvaluators); + this.auditModeEnum = other.auditModeEnum; + this.componentServiceName = other.componentServiceName; + this.componentServiceDef = other.componentServiceDef; + this.policyEvaluatorsMap = new HashMap<>(other.policyEvaluatorsMap); if (other.policyResourceTrie != null) { this.policyResourceTrie = new HashMap<>(); @@ -147,6 +150,16 @@ public class RangerPolicyRepository { this.rowFilterResourceTrie = null; } + if (other.constraintResourceTrie != null) { + this.constraintResourceTrie = new HashMap<>(); + + for (Map.Entry> entry : other.constraintResourceTrie.entrySet()) { + constraintResourceTrie.put(entry.getKey(), new RangerResourceTrie<>(entry.getValue())); + } + } else { + this.constraintResourceTrie = null; + } + if (other.auditFilterResourceTrie != null) { this.auditFilterResourceTrie = new HashMap<>(); @@ -244,11 +257,13 @@ public RangerPolicyRepository(ServicePolicies servicePolicies, RangerPluginConte policyResourceTrie = null; dataMaskResourceTrie = null; rowFilterResourceTrie = null; + constraintResourceTrie = null; auditFilterResourceTrie = null; } else { policyResourceTrie = createResourceTrieMap(policyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); dataMaskResourceTrie = createResourceTrieMap(dataMaskPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); rowFilterResourceTrie = createResourceTrieMap(rowFilterPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + constraintResourceTrie = createResourceTrieMap(constraintPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); auditFilterResourceTrie = createResourceTrieMap(auditPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); } } @@ -295,11 +310,13 @@ public RangerPolicyRepository(ServicePolicies servicePolicies, RangerPluginConte policyResourceTrie = null; dataMaskResourceTrie = null; rowFilterResourceTrie = null; + constraintResourceTrie = null; auditFilterResourceTrie = null; } else { policyResourceTrie = createResourceTrieMap(policyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); dataMaskResourceTrie = createResourceTrieMap(dataMaskPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); rowFilterResourceTrie = createResourceTrieMap(rowFilterPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + constraintResourceTrie = createResourceTrieMap(constraintPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); auditFilterResourceTrie = createResourceTrieMap(auditPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); } } @@ -364,6 +381,16 @@ public StringBuilder toString(StringBuilder sb) { } sb.append("} "); + sb.append("constraintPolicyEvaluators={"); + if (this.constraintPolicyEvaluators != null) { + for (RangerPolicyEvaluator policyEvaluator : constraintPolicyEvaluators) { + if (policyEvaluator != null) { + sb.append(policyEvaluator).append(" "); + } + } + } + sb.append("} "); + sb.append("auditPolicyEvaluators={"); if (this.auditPolicyEvaluators != null) { for (RangerPolicyEvaluator policyEvaluator : auditPolicyEvaluators) { @@ -413,6 +440,10 @@ public int getRowFilterPolicyEvaluatorCount() { return rowFilterPolicyEvaluators.size(); } + public int getConstraintPolicyEvaluatorCount() { + return constraintPolicyEvaluators.size(); + } + public int getAuditPolicyEvaluatorCount() { return auditPolicyEvaluators.size(); } @@ -441,6 +472,8 @@ public List getLikelyMatchPolicyEvaluators(RangerAccessRe return getLikelyMatchRowFilterPolicyEvaluators(request); case RangerPolicy.POLICY_TYPE_AUDIT: return getLikelyMatchAuditPolicyEvaluators(request); + case RangerPolicy.POLICY_TYPE_CONSTRAINTS: + return getLikelyMatchConstraintPolicyEvaluators(request); default: return Collections.emptyList(); } @@ -511,6 +544,10 @@ void reorderPolicyEvaluators() { rowFilterPolicyEvaluators = getReorderedPolicyEvaluators(rowFilterPolicyEvaluators); } + if (constraintResourceTrie == null) { + constraintPolicyEvaluators = getReorderedPolicyEvaluators(constraintPolicyEvaluators); + } + LOG.debug("<== reorderEvaluators()"); } @@ -567,6 +604,8 @@ List getPolicyEvaluators(int policyType) { return getDataMaskPolicyEvaluators(); case RangerPolicy.POLICY_TYPE_ROWFILTER: return getRowFilterPolicyEvaluators(); + case RangerPolicy.POLICY_TYPE_CONSTRAINTS: + return getConstraintPolicyEvaluators(); default: return getPolicyEvaluators(); } @@ -584,6 +623,10 @@ List getAuditPolicyEvaluators() { return auditPolicyEvaluators; } + List getConstraintPolicyEvaluators() { + return constraintPolicyEvaluators; + } + String getAppId() { return appId; } @@ -663,6 +706,9 @@ Map> getTrie(final int case RangerPolicy.POLICY_TYPE_ROWFILTER: ret = rowFilterResourceTrie; break; + case RangerPolicy.POLICY_TYPE_CONSTRAINTS: + ret = constraintResourceTrie; + break; case RangerPolicy.POLICY_TYPE_AUDIT: ret = auditFilterResourceTrie; break; @@ -734,6 +780,13 @@ private List getLikelyMatchRowFilterPolicyEvaluators(Rang return rowFilterResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getRowFilterPolicyEvaluators() : getLikelyMatchPolicyEvaluators(rowFilterResourceTrie, request); } + private List getLikelyMatchConstraintPolicyEvaluators(RangerAccessRequest request) { + RangerAccessResource resource = request.getResource(); + String resourceStr = resource == null ? null : resource.getAsString(); + + return constraintResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getConstraintPolicyEvaluators() : getLikelyMatchPolicyEvaluators(constraintResourceTrie, request); + } + private List getLikelyMatchPolicyEvaluators(Map> resourceTrie, RangerAccessRequest request) { List ret = Collections.emptyList(); RangerAccessResource resource = request.getResource(); @@ -920,9 +973,10 @@ private static boolean skipBuildingPolicyEvaluator(RangerPolicy policy, RangerPo } private void init(RangerPolicyEngineOptions options) { - List policyEvaluators = new ArrayList<>(); - List dataMaskPolicyEvaluators = new ArrayList<>(); - List rowFilterPolicyEvaluators = new ArrayList<>(); + List policyEvaluators = new ArrayList<>(); + List dataMaskPolicyEvaluators = new ArrayList<>(); + List rowFilterPolicyEvaluators = new ArrayList<>(); + List constraintPolicyEvaluators = new ArrayList<>(); for (RangerPolicy policy : policies) { if (skipBuildingPolicyEvaluator(policy, options)) { @@ -938,13 +992,15 @@ private void init(RangerPolicyEngineOptions options) { dataMaskPolicyEvaluators.add(evaluator); } else if (policy.getPolicyType() == RangerPolicy.POLICY_TYPE_ROWFILTER) { rowFilterPolicyEvaluators.add(evaluator); + } else if (policy.getPolicyType() == RangerPolicy.POLICY_TYPE_CONSTRAINTS) { + constraintPolicyEvaluators.add(evaluator); } else { LOG.warn("RangerPolicyEngine: ignoring policy id={} - invalid policyType '{}'", policy.getId(), policy.getPolicyType()); } } } - LOG.info("This policy engine contains {} policy evaluators", (policyEvaluators.size() + dataMaskPolicyEvaluators.size() + rowFilterPolicyEvaluators.size())); + LOG.info("This policy engine contains {} policy evaluators", (policyEvaluators.size() + dataMaskPolicyEvaluators.size() + rowFilterPolicyEvaluators.size() + constraintPolicyEvaluators.size())); policyEvaluators.sort(RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR); this.policyEvaluators = policyEvaluators; @@ -954,6 +1010,9 @@ private void init(RangerPolicyEngineOptions options) { rowFilterPolicyEvaluators.sort(RangerPolicyEvaluator.NAME_COMPARATOR); this.rowFilterPolicyEvaluators = rowFilterPolicyEvaluators; + // Constraint matches are all returned. Do not sort them by policy priority. + this.constraintPolicyEvaluators = constraintPolicyEvaluators; + this.policyEvaluatorsMap = createPolicyEvaluatorsMap(); if (LOG.isDebugEnabled()) { @@ -984,6 +1043,15 @@ private void init(RangerPolicyEngineOptions options) { LOG.debug("rowFilter policy evaluation order: #{} - policy id={}; name={}; evalOrder={}", (++order), policy.getId(), policy.getName(), policyEvaluator.getEvalOrder()); } + LOG.debug("constraint policy evaluation order: {} policies", this.constraintPolicyEvaluators.size()); + + order = 0; + for (RangerPolicyEvaluator policyEvaluator : this.constraintPolicyEvaluators) { + RangerPolicy policy = policyEvaluator.getPolicy(); + + LOG.debug("constraint policy evaluation order: #{} - policy id={}; name={}; evalOrder={}", (++order), policy.getId(), policy.getName(), policyEvaluator.getEvalOrder()); + } + LOG.debug("audit policy evaluation order: {} policies", ((this.auditPolicyEvaluators != null) ? this.auditPolicyEvaluators.size() : 0)); if (this.auditPolicyEvaluators != null) { order = 0; @@ -1166,6 +1234,7 @@ private boolean scrubPolicy(RangerPolicy policy) { scrubPolicyItems(policyId, policy.getDenyExceptions()); scrubPolicyItems(policyId, policy.getRowFilterPolicyItems()); scrubPolicyItems(policyId, policy.getDataMaskPolicyItems()); + scrubPolicyItems(policyId, policy.getConstraintPolicyItems()); LOG.debug("<== RangerPolicyRepository.scrubPolicy({}): {}", policy, altered); @@ -1175,6 +1244,12 @@ private boolean scrubPolicy(RangerPolicy policy) { private void scrubPolicyItems(final Long policyId, final List policyItems) { LOG.debug("==> RangerPolicyRepository.scrubPolicyItems({})", policyId); + if (policyItems == null) { + LOG.debug("<== RangerPolicyRepository.scrubPolicyItems({})", policyId); + + return; + } + for (RangerPolicyItem policyItem : policyItems) { removeNulls(policyItem.getUsers(), policyId, policyItem); removeNulls(policyItem.getGroups(), policyId, policyItem); @@ -1289,6 +1364,9 @@ private Map createPolicyEvaluatorsMap() { for (RangerPolicyEvaluator evaluator : getRowFilterPolicyEvaluators()) { tmpPolicyEvaluatorMap.put(evaluator.getPolicy().getId(), evaluator); } + for (RangerPolicyEvaluator evaluator : getConstraintPolicyEvaluators()) { + tmpPolicyEvaluatorMap.put(evaluator.getPolicy().getId(), evaluator); + } return tmpPolicyEvaluatorMap; } @@ -1311,6 +1389,8 @@ private RangerPolicyEvaluator addPolicy(RangerPolicy policy) { dataMaskPolicyEvaluators.add(ret); } else if (policy.getPolicyType() == RangerPolicy.POLICY_TYPE_ROWFILTER) { rowFilterPolicyEvaluators.add(ret); + } else if (policy.getPolicyType() == RangerPolicy.POLICY_TYPE_CONSTRAINTS) { + constraintPolicyEvaluators.add(ret); } else { LOG.warn("RangerPolicyEngine: ignoring policy id={} - invalid policyType '{}'", policy.getId(), policy.getPolicyType()); } @@ -1355,6 +1435,8 @@ private void deletePolicyEvaluator(RangerPolicyEvaluator evaluator) { evaluators = this.dataMaskPolicyEvaluators; } else if (policyType == RangerPolicy.POLICY_TYPE_ROWFILTER) { evaluators = this.rowFilterPolicyEvaluators; + } else if (policyType == RangerPolicy.POLICY_TYPE_CONSTRAINTS) { + evaluators = this.constraintPolicyEvaluators; } else { LOG.error("Unknown policyType:[{}]", policyType); } @@ -1423,7 +1505,8 @@ private RangerPolicyEvaluator update(final RangerPolicyDelta delta, final Ranger } private void updateResourceTrie(List deltas) { - boolean[] flags = new boolean[RangerPolicy.POLICY_TYPES.length]; + boolean[] flags = new boolean[RangerPolicy.POLICY_TYPES.length]; + boolean constraintTrieUpdated = false; for (RangerPolicyDelta delta : deltas) { final Integer changeType = delta.getChangeType(); @@ -1492,7 +1575,11 @@ private void updateResourceTrie(List deltas) { break; } - flags[policyType] = true; + if (policyType == RangerPolicy.POLICY_TYPE_CONSTRAINTS) { + constraintTrieUpdated = true; + } else { + flags[policyType] = true; + } } } @@ -1508,6 +1595,12 @@ private void updateResourceTrie(List deltas) { } } + if (constraintTrieUpdated && constraintResourceTrie != null) { + for (Map.Entry> entry : constraintResourceTrie.entrySet()) { + entry.getValue().wrapUpUpdate(); + } + } + if (auditFilterResourceTrie != null) { for (Map.Entry> entry : auditFilterResourceTrie.entrySet()) { entry.getValue().wrapUpUpdate(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java index 7c45f6dfbe..4a23ef9229 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java @@ -525,6 +525,20 @@ private Map getPolicyResourcesWithMacrosReplaced(M return ret; } + private Set getConstraintNames() { + Set ret = new HashSet<>(); + + if (policy != null && CollectionUtils.isNotEmpty(policy.getConstraintPolicyItems())) { + for (RangerPolicy.RangerConstraintPolicyItem item : policy.getConstraintPolicyItems()) { + if (item != null && item.getConstraintInfo() != null && StringUtils.isNotEmpty(item.getConstraintInfo().getConstraintName())) { + ret.add(item.getConstraintInfo().getConstraintName()); + } + } + } + + return ret; + } + public class RangerDefaultPolicyResourceEvaluator implements RangerPolicyResourceEvaluator { private final long id; private final Map resource; @@ -539,6 +553,11 @@ public RangerDefaultPolicyResourceEvaluator(long id, Map allowExceptionEvaluators; private List denyExceptionEvaluators; private int customConditionsCount; - private List dataMaskEvaluators; - private List rowFilterEvaluators; + private List dataMaskEvaluators; + private List rowFilterEvaluators; + private List constraintEvaluators; private List conditionEvaluators; private String perfTag; private PolicyACLSummary aclSummary; @@ -137,9 +139,10 @@ public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyE denyExceptionEvaluators = Collections.emptyList(); } - dataMaskEvaluators = createDataMaskPolicyItemEvaluators(policy, serviceDef, options, policy.getDataMaskPolicyItems()); - rowFilterEvaluators = createRowFilterPolicyItemEvaluators(policy, serviceDef, options, policy.getRowFilterPolicyItems()); - conditionEvaluators = createPolicyConditionEvaluators(policy, serviceDef, options); + dataMaskEvaluators = createDataMaskPolicyItemEvaluators(policy, serviceDef, options, policy.getDataMaskPolicyItems()); + rowFilterEvaluators = createRowFilterPolicyItemEvaluators(policy, serviceDef, options, policy.getRowFilterPolicyItems()); + constraintEvaluators = createConstraintPolicyItemEvaluators(policy, serviceDef, options); + conditionEvaluators = createPolicyConditionEvaluators(policy, serviceDef, options); } else { validityScheduleEvaluators = Collections.emptyList(); allowEvaluators = Collections.emptyList(); @@ -148,6 +151,7 @@ public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyE denyExceptionEvaluators = Collections.emptyList(); dataMaskEvaluators = Collections.emptyList(); rowFilterEvaluators = Collections.emptyList(); + constraintEvaluators = Collections.emptyList(); conditionEvaluators = Collections.emptyList(); } @@ -157,9 +161,10 @@ public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyE allowExceptionEvaluators.sort(comparator); denyExceptionEvaluators.sort(comparator); - /* dataMask, rowFilter policyItems must be evaulated in the order given in the policy; hence no sort + /* dataMask, rowFilter and constraint policyItems must be evaulated in the order given in the policy; hence no sort Collections.sort(dataMaskEvaluators); Collections.sort(rowFilterEvaluators); + Collections.sort(constraintEvaluators); */ RangerPerfTracer.log(perf); @@ -290,6 +295,38 @@ public void evaluate(RangerAccessRequest request, RangerAccessResult result) { LOG.debug("<== RangerDefaultPolicyEvaluator.evaluate(policyId={}, {}, {})", getPolicyId(), request, result); } + @Override + public void evaluateConstraints(RangerAccessRequest request, RangerAccessResult result) { + LOG.debug("==> RangerDefaultPolicyEvaluator.evaluateConstraints(policyId={}, {}, {})", getPolicyId(), request, result); + + if (request == null || result == null || getPolicyType() != RangerPolicy.POLICY_TYPE_CONSTRAINTS || CollectionUtils.isEmpty(constraintEvaluators)) { + LOG.debug("<== RangerDefaultPolicyEvaluator.evaluateConstraints(policyId={}): not a constraint policy", getPolicyId()); + + return; + } + + for (RangerPolicyResourceEvaluator resourceEvaluator : getResourceEvaluators()) { + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); + MatchType matchType = resourceMatcher != null ? resourceMatcher.getMatchType(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()) : MatchType.NONE; + + if (!isConstraintResourceMatch(request, matchType) || !matchPolicyCustomConditions(request)) { + continue; + } + + // Match request.getAccessType() only. Multi-access and _any requests are not expanded here. + for (RangerConstraintPolicyItemEvaluator itemEvaluator : constraintEvaluators) { + if (itemEvaluator.isMatch(request)) { + itemEvaluator.collectConstraint(result, matchType); + } + } + + // One matching resource spec is enough. Later specs would record the same items again. + break; + } + + LOG.debug("<== RangerDefaultPolicyEvaluator.evaluateConstraints(policyId={}, {})", getPolicyId(), result); + } + @Override public boolean isMatch(RangerAccessResource resource, Map evalContext) { LOG.debug("==> RangerDefaultPolicyEvaluator.isMatch(policy-id={}, {}, {})", getPolicyId(), resource, evalContext); @@ -1295,6 +1332,46 @@ private List createRowFilterPolicyItemEvalua return ret; } + private List createConstraintPolicyItemEvaluators(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { + List ret; + Integer policyType = policy.getPolicyType(); + + if (policyType != null && policyType == RangerPolicy.POLICY_TYPE_CONSTRAINTS && CollectionUtils.isNotEmpty(policy.getConstraintPolicyItems())) { + ret = new ArrayList<>(); + + int policyItemCounter = 1; + + for (RangerConstraintPolicyItem policyItem : policy.getConstraintPolicyItems()) { + RangerDefaultConstraintPolicyItemEvaluator itemEvaluator = new RangerDefaultConstraintPolicyItemEvaluator(serviceDef, policy, policyItem, policyItemCounter++, options); + + itemEvaluator.init(); + + ret.add(itemEvaluator); + + if (CollectionUtils.isNotEmpty(itemEvaluator.getConditionEvaluators())) { + customConditionsCount += itemEvaluator.getConditionEvaluators().size(); + } + } + } else { + ret = Collections.emptyList(); + } + + return ret; + } + + private boolean isConstraintResourceMatch(RangerAccessRequest request, MatchType matchType) { + final boolean ret; + final ResourceMatchingScope resourceMatchingScope = request.getResourceMatchingScope() != null ? request.getResourceMatchingScope() : ResourceMatchingScope.SELF; + + if (request.isAccessTypeAny() || resourceMatchingScope == ResourceMatchingScope.SELF_OR_DESCENDANTS) { + ret = matchType == MatchType.SELF || matchType == MatchType.SELF_AND_ALL_DESCENDANTS || matchType == MatchType.DESCENDANT || (matchType == MatchType.ANCESTOR && request instanceof RangerTagAccessRequest); + } else { + ret = matchType == MatchType.SELF || matchType == MatchType.SELF_AND_ALL_DESCENDANTS || (matchType == MatchType.ANCESTOR && request instanceof RangerTagAccessRequest); + } + + return ret; + } + private boolean isPolicyItemTypeEnabled(RangerServiceDef serviceDef, int policyItemType) { boolean ret = true; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java index 4c6424c507..aefffba6eb 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java @@ -83,6 +83,7 @@ public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyE preprocessPolicyItems(policy.getDenyExceptions()); preprocessPolicyItems(policy.getDataMaskPolicyItems()); preprocessPolicyItems(policy.getRowFilterPolicyItems()); + preprocessPolicyItems(policy.getConstraintPolicyItems()); hasAllPerms = checkIfHasAllPerms(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java index 8bad4cb7f8..db28c8d5ac 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java @@ -128,6 +128,13 @@ static boolean containsAny(Collection coll1, Collection coll2) { void evaluate(RangerAccessRequest request, RangerAccessResult result); + /** + * Append every constraint item that matches the request. The default does nothing, so access, + * mask, row-filter, and audit evaluators do not change an access result. + */ + default void evaluateConstraints(RangerAccessRequest request, RangerAccessResult result) { + } + void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, Set targetAccessTypes, MatchType matchType, PolicyEngine policyEngine); boolean isMatch(RangerAccessResource resource, Map evalContext); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java index d79ac1ead2..092da27342 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java @@ -36,6 +36,7 @@ public interface RangerPolicyItemEvaluator { int POLICY_ITEM_TYPE_DENY_EXCEPTIONS = 3; int POLICY_ITEM_TYPE_DATAMASK = 4; int POLICY_ITEM_TYPE_ROWFILTER = 5; + int POLICY_ITEM_TYPE_CONSTRAINT = 6; void init(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java index 9292ce8bec..7b55debc91 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java @@ -40,6 +40,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -55,6 +56,7 @@ public class RangerDefaultPolicyResourceMatcher implements RangerPolicyResourceM protected RangerServiceDef serviceDef; protected int policyType; protected Map policyResources; + private Set constraintNames; private Map allMatchers; private boolean needsDynamicEval; @@ -567,7 +569,7 @@ public void init() { if (policyResources != null && !policyResources.isEmpty() && serviceDef != null) { serviceDefHelper = serviceDefHelper == null ? new RangerServiceDefHelper(serviceDef, false) : serviceDefHelper; - Set> resourceHierarchies = serviceDefHelper.getResourceHierarchies(policyType, policyResources.keySet()); + Set> resourceHierarchies = getResourceHierarchies(policyResources.keySet()); int validHierarchiesCount = 0; for (List resourceHierarchy : resourceHierarchies) { @@ -684,6 +686,14 @@ public void setPolicyResources(Map policyResources this.policyType = policyType; } + public void setConstraintNames(Collection constraintNames) { + this.constraintNames = CollectionUtils.isEmpty(constraintNames) ? Collections.emptySet() : new HashSet<>(constraintNames); + } + + public Set getConstraintNames() { + return constraintNames == null ? Collections.emptySet() : constraintNames; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -695,7 +705,7 @@ private List getMatchingHierarchy(Set resourceKeys) { List ret = null; if (CollectionUtils.isNotEmpty(resourceKeys) && serviceDefHelper != null) { - Set> resourceHierarchies = serviceDefHelper.getResourceHierarchies(policyType, resourceKeys); + Set> resourceHierarchies = getResourceHierarchies(resourceKeys); // pick the shortest hierarchy for (List resourceHierarchy : resourceHierarchies) { @@ -715,6 +725,24 @@ private List getMatchingHierarchy(Set resourceKeys) { return ret; } + private Set> getResourceHierarchies(Collection keys) { + final Set> ret; + + if (policyType == RangerPolicy.POLICY_TYPE_CONSTRAINTS) { + ret = new HashSet<>(); + + if (serviceDefHelper != null) { + for (String constraintName : getConstraintNames()) { + ret.addAll(serviceDefHelper.getResourceHierarchiesForConstraint(constraintName, keys)); + } + } + } else { + ret = serviceDefHelper.getResourceHierarchies(policyType, keys); + } + + return ret; + } + private List getMatchingHierarchy(RangerAccessResource resource) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.getMatchingHierarchy({})", resource); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java index 29d7b30552..e69e4c1c5e 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java @@ -149,7 +149,7 @@ public static boolean isValidDeltas(List deltas, String compo if (serviceType == null || (!serviceType.equals(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME) && !serviceType.equals(componentServiceType))) { isValid = false; - } else if (policyType == null || (policyType != RangerPolicy.POLICY_TYPE_ACCESS && policyType != RangerPolicy.POLICY_TYPE_DATAMASK && policyType != RangerPolicy.POLICY_TYPE_ROWFILTER)) { + } else if (policyType == null || (policyType != RangerPolicy.POLICY_TYPE_ACCESS && policyType != RangerPolicy.POLICY_TYPE_DATAMASK && policyType != RangerPolicy.POLICY_TYPE_ROWFILTER && policyType != RangerPolicy.POLICY_TYPE_CONSTRAINTS)) { isValid = false; } } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java index 918b67777f..6e22c3140a 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java @@ -24,6 +24,7 @@ import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerRowFilterDef; @@ -132,6 +133,55 @@ public void test_getResourceHierarchies() { assertTrue(expectedHierarchies.isEmpty()); } + @Test + public void test_getResourceHierarchiesForConstraint() { + RangerServiceDef serviceDef = mock(RangerServiceDef.class); + when(serviceDef.getName()).thenReturn("constraint-hierarchies"); + when(serviceDef.getUpdateTime()).thenReturn(getNow()); + + RangerResourceDef database = createRealResourceDef("Database", null, 1, null); + RangerResourceDef udf = createRealResourceDef("UDF", "Database", 2, null); + RangerResourceDef table = createRealResourceDef("Table", "Database", 2, null); + RangerResourceDef column = createRealResourceDef("Column", "Table", 3, true); + RangerResourceDef tableAttribute = createRealResourceDef("Table-Attribute", "Table", 3, true); + + when(serviceDef.getResources()).thenReturn(Lists.newArrayList(column, database, table, tableAttribute, udf)); + when(serviceDef.getAccessTypes()).thenReturn(new ArrayList<>()); + + RangerConstraintDef maxRows = new RangerConstraintDef(); + maxRows.setName("maxRows"); + maxRows.setResources(Arrays.asList("Table", "Database")); + + RangerConstraintDef requireApproval = new RangerConstraintDef(); + requireApproval.setName("requireApproval"); + requireApproval.setResources(Collections.singletonList("UDF")); + + RangerConstraintDef unrestricted = new RangerConstraintDef(); + unrestricted.setName("unrestricted"); + + when(serviceDef.getConstraintDefs()).thenReturn(Arrays.asList(maxRows, requireApproval, unrestricted)); + + RangerServiceDefHelper helper = new RangerServiceDefHelper(serviceDef); + List tableKeys = Arrays.asList("Table"); + + Set> maxRowsHierarchies = helper.getResourceHierarchiesForConstraint("maxRows", tableKeys); + + assertEquals(1, maxRowsHierarchies.size()); + assertEquals(Arrays.asList("Database", "Table"), helper.getAllResourceNamesOrdered(maxRowsHierarchies.iterator().next())); + assertTrue(helper.getResourceHierarchiesForConstraint("maxRows", Arrays.asList("Database", "Column")).isEmpty()); + + Set> approvalHierarchies = helper.getResourceHierarchiesForConstraint("requireApproval", Collections.singletonList("UDF")); + + assertEquals(1, approvalHierarchies.size()); + assertEquals(Collections.singletonList("UDF"), helper.getAllResourceNamesOrdered(approvalHierarchies.iterator().next())); + assertTrue(helper.getResourceHierarchiesForConstraint("requireApproval", Arrays.asList("Database", "UDF")).isEmpty()); + + assertEquals(helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS, Arrays.asList("Database", "Table")), helper.getResourceHierarchiesForConstraint("unrestricted", Arrays.asList("Database", "Table"))); + assertTrue(helper.getResourceHierarchiesForConstraint("missing", tableKeys).isEmpty()); + assertTrue(helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_CONSTRAINTS).isEmpty()); + assertTrue(helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_CONSTRAINTS, tableKeys).isEmpty()); + } + @Test public final void test_isResourceGraphValid_detectCycle() { /* diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java index 74504c2429..a589ef5742 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java @@ -69,6 +69,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -678,6 +679,20 @@ public void testMultipleAccessAuthorization() { runTestsFromResourceFiles(resourceFiles); } + @Test + public void testPolicyEngine_constraints() { + String[] resourceFiles = {"/policyengine/test_policyengine_constraints.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testPolicyEngine_constraints_without_constraint_defs() { + String[] resourceFiles = {"/policyengine/test_policyengine_constraints_no_defs.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + @Test public void testValiditySchedulerInvalid() { String resourceName = "/policyengine/validityscheduler/test-validity-schedules-invalid.json"; @@ -914,6 +929,7 @@ private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerServiceDef assertNotNull(result, "result was null! - " + test.name); assertEquals(expected.getIsAllowed(), result.getIsAllowed(), "isAllowed mismatched! - " + test.name); assertEquals(expected.getIsAudited(), result.getIsAudited(), "isAudited mismatched! - " + test.name); + assertEquals(sortedConstraints(expected.getConstraints()), sortedConstraints(result.getConstraints()), "constraints mismatched! - " + test.name); } if (test.dataMaskResult != null) { @@ -957,6 +973,17 @@ private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerServiceDef }); } + private static List sortedConstraints(List constraints) { + List ret = new ArrayList<>(); + + if (constraints != null) { + ret.addAll(constraints); + ret.sort(Comparator.comparingLong(RangerConstraintResult::getPolicyId).thenComparing(constraintResult -> constraintResult.getConstraintName() == null ? "" : constraintResult.getConstraintName())); + } + + return ret; + } + private void setPluginConfig(RangerPluginConfig conf, String suffix, Set value) { conf.set(conf.getPropertyPrefix() + suffix, CollectionUtils.isNotEmpty(value) ? StringUtils.join(value, ',') : ""); } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/TestRangerPolicyDeltaUtil.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/TestRangerPolicyDeltaUtil.java index 58f59e6afc..fc022f0392 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/util/TestRangerPolicyDeltaUtil.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/TestRangerPolicyDeltaUtil.java @@ -121,6 +121,11 @@ public void test02_isValidDeltas_variousInvalidReasons() { d5.setChangeType(RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE); d5.setPolicy(policy(7L, svc, RangerPolicy.POLICY_TYPE_ACCESS)); assertTrue(RangerPolicyDeltaUtil.isValidDeltas(Collections.singletonList(d5), svc)); + + RangerPolicyDelta d6 = new RangerPolicyDelta(); + d6.setChangeType(RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE); + d6.setPolicy(policy(8L, svc, RangerPolicy.POLICY_TYPE_CONSTRAINTS)); + assertTrue(RangerPolicyDeltaUtil.isValidDeltas(Collections.singletonList(d6), svc)); } @Test diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_constraints.json b/agents-common/src/test/resources/policyengine/test_policyengine_constraints.json new file mode 100644 index 0000000000..8a353576f8 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_constraints.json @@ -0,0 +1,595 @@ +{ + "serviceName": "agent-studio", + "serviceDef": { + "name": "agent-studio", + "resources": [ + { + "name": "tool", + "type": "string", + "level": 10, + "mandatory": true, + "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": {"wildCard": true, "ignoreCase": true}, + "label": "Tool", + "description": "Agent tool" + } + ], + "accessTypes": [ + {"name": "execute", "label": "Execute"}, + {"name": "select", "label": "Select"} + ], + "policyConditions": [ + { + "itemId": 1, + "name": "expression", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions": {"engineName": "JavaScript"}, + "label": "Expression", + "description": "Boolean expression" + } + ], + "constraintDefs": [ + { + "itemId": 1, + "name": "requireApproval", + "label": "Require approval", + "resources": ["tool"], + "accessTypes": ["execute"], + "parameters": [ + {"itemId": 1, "name": "approver", "type": "user", "mandatory": true} + ] + }, + { + "itemId": 2, + "name": "maxRows", + "label": "Max rows", + "resources": ["tool"], + "accessTypes": ["execute"], + "parameters": [ + {"itemId": 1, "name": "rowCount", "type": "int", "mandatory": true} + ] + } + ] + }, + "policies": [ + { + "id": 1, + "version": 1, + "name": "allow-execute-and-select", + "isEnabled": true, + "isAuditEnabled": true, + "isDenyAllElse": false, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "policyItems": [ + { + "accesses": [ + {"type": "execute", "isAllowed": true}, + {"type": "select", "isAllowed": true} + ], + "users": [], + "groups": ["public"], + "delegateAdmin": false + } + ] + }, + { + "id": 2, + "version": 2, + "name": "deny-carol", + "isEnabled": true, + "isAuditEnabled": true, + "isDenyAllElse": false, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "policyItems": [], + "denyPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["carol"], + "groups": [], + "delegateAdmin": false + } + ] + }, + { + "id": 3, + "version": 1, + "name": "allow-lookup", + "isEnabled": true, + "isAuditEnabled": true, + "isDenyAllElse": false, + "policyPriority": 0, + "resources": {"tool": {"values": ["lookup"]}}, + "policyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": [], + "groups": ["public"], + "delegateAdmin": false + } + ] + }, + { + "id": 10, + "version": 3, + "name": "constraint-eve", + "policyType": 4, + "isEnabled": true, + "isAuditEnabled": true, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["eve"], + "groups": [], + "delegateAdmin": false, + "constraintInfo": { + "constraintName": "requireApproval", + "parameters": {"approver": "eve-approver"} + } + } + ] + }, + { + "id": 11, + "version": 4, + "name": "constraint-alice-approval", + "policyType": 4, + "isEnabled": true, + "isAuditEnabled": true, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["alice"], + "groups": [], + "delegateAdmin": false, + "constraintInfo": { + "constraintName": "requireApproval", + "parameters": {"approver": "finance-approvers"} + } + } + ] + }, + { + "id": 12, + "version": 5, + "name": "constraint-finance-maxrows", + "policyType": 4, + "isEnabled": true, + "isAuditEnabled": true, + "policyPriority": 1, + "resources": {"tool": {"values": ["transfer"]}}, + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": [], + "groups": ["finance"], + "delegateAdmin": false, + "constraintInfo": { + "constraintName": "maxRows", + "parameters": {"rowCount": 100} + } + } + ] + }, + { + "id": 13, + "version": 6, + "name": "constraint-bob-amount", + "policyType": 4, + "isEnabled": true, + "isAuditEnabled": true, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["bob"], + "groups": [], + "delegateAdmin": false, + "conditions": [ + {"type": "expression", "values": ["ATTR.trx_amount > 10000"]} + ], + "constraintInfo": { + "constraintName": "requireApproval", + "parameters": {"approver": "bob-approver"} + } + } + ] + }, + { + "id": 14, + "version": 7, + "name": "constraint-carol-not-returned-on-deny", + "policyType": 4, + "isEnabled": true, + "isAuditEnabled": true, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["carol"], + "groups": [], + "delegateAdmin": false, + "constraintInfo": { + "constraintName": "requireApproval", + "parameters": {"approver": "carol-approver"} + } + } + ] + }, + { + "id": 15, + "version": 8, + "name": "constraint-iris-disabled", + "policyType": 4, + "isEnabled": false, + "isAuditEnabled": true, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["iris"], + "groups": [], + "delegateAdmin": false, + "constraintInfo": { + "constraintName": "requireApproval", + "parameters": {"approver": "disabled-approver"} + } + } + ] + }, + { + "id": 16, + "version": 9, + "name": "constraint-frank-schedule", + "policyType": 4, + "isEnabled": true, + "isAuditEnabled": true, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "validitySchedules": [ + { + "startTime": "2018/01/01 00:00:00", + "endTime": "2018/12/31 23:59:59", + "timeZone": "GMT" + } + ], + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["frank"], + "groups": [], + "delegateAdmin": false, + "constraintInfo": { + "constraintName": "requireApproval", + "parameters": {"approver": "frank-approver"} + } + } + ] + } + ], + "tests": [ + { + "name": "no matching constraint", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "dave", + "userGroups": [], + "requestData": "execute transfer" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "one matching constraint", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "eve", + "userGroups": [], + "requestData": "execute transfer" + }, + "result": { + "isAllowed": true, + "isAudited": true, + "constraints": [ + { + "constraintName": "requireApproval", + "parameters": {"approver": "eve-approver"}, + "policyId": 10, + "policyVersion": 3, + "matchType": "SELF" + } + ] + } + }, + { + "name": "multiple constraints from different policies", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "alice", + "userGroups": ["finance"], + "requestData": "execute transfer" + }, + "result": { + "isAllowed": true, + "isAudited": true, + "constraints": [ + { + "constraintName": "requireApproval", + "parameters": {"approver": "finance-approvers"}, + "policyId": 11, + "policyVersion": 4, + "matchType": "SELF" + }, + { + "constraintName": "maxRows", + "parameters": {"rowCount": 100}, + "policyId": 12, + "policyVersion": 5, + "policyPriority": 1, + "matchType": "SELF" + } + ] + } + }, + { + "name": "constraint with ATTR condition matches", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "bob", + "userGroups": [], + "requestData": "execute transfer", + "context": {"_ATTR_JSON": "{\"trx_amount\":50000}"} + }, + "result": { + "isAllowed": true, + "isAudited": true, + "constraints": [ + { + "constraintName": "requireApproval", + "parameters": {"approver": "bob-approver"}, + "policyId": 13, + "policyVersion": 6, + "matchType": "SELF" + } + ] + } + }, + { + "name": "ATTR condition does not match", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "bob", + "userGroups": [], + "requestData": "execute transfer", + "context": {"_ATTR_JSON": "{\"trx_amount\":10000}"} + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "deny returns no constraints", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "carol", + "userGroups": [], + "requestData": "execute transfer" + }, + "result": {"isAllowed": false, "isAudited": true, "constraints": []} + }, + { + "name": "access type does not match", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "select", + "user": "eve", + "userGroups": [], + "requestData": "select transfer" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "different tool does not match", + "request": { + "resource": {"elements": {"tool": "lookup"}}, + "accessType": "execute", + "user": "eve", + "userGroups": [], + "requestData": "execute lookup" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "condition attribute missing", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "bob", + "userGroups": [], + "requestData": "execute transfer" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "disabled constraint policy is ignored", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "iris", + "userGroups": [], + "requestData": "execute transfer" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "constraint policy is inside its validity schedule", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "frank", + "userGroups": [], + "accessTime": "20180615-12:00:00.000-0800", + "requestData": "execute transfer" + }, + "result": { + "isAllowed": true, + "isAudited": true, + "constraints": [ + { + "constraintName": "requireApproval", + "parameters": {"approver": "frank-approver"}, + "policyId": 16, + "policyVersion": 9, + "matchType": "SELF" + } + ] + } + }, + { + "name": "constraint policy is outside its validity schedule", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "frank", + "userGroups": [], + "accessTime": "20261015-12:00:00.000-0800", + "requestData": "execute transfer" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "main access type matches when several types are requested", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "eve", + "userGroups": [], + "requestData": "execute transfer", + "context": {"ALLACCESSTYPES": ["execute", "select"]} + }, + "result": { + "isAllowed": true, + "isAudited": true, + "constraints": [ + { + "constraintName": "requireApproval", + "parameters": {"approver": "eve-approver"}, + "policyId": 10, + "policyVersion": 3, + "matchType": "SELF" + } + ] + } + }, + { + "name": "other access type in the same request is not checked", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "select", + "user": "eve", + "userGroups": [], + "requestData": "select transfer", + "context": {"ALLACCESSTYPES": ["execute", "select"]} + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "any access does not match a typed constraint", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "_any", + "user": "eve", + "userGroups": [], + "requestData": "any transfer" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + } + ], + "updatedPolicies": { + "policyDeltas": [ + { + "changeType": 2, + "policy": { + "id": 10, + "version": 3, + "policyType": 4, + "serviceType": "agent-studio" + } + }, + { + "changeType": 0, + "policy": { + "id": 20, + "version": 1, + "serviceType": "agent-studio", + "name": "constraint-gina", + "policyType": 4, + "isEnabled": true, + "isAuditEnabled": true, + "policyPriority": 0, + "resources": {"tool": {"values": ["transfer"]}}, + "constraintPolicyItems": [ + { + "accesses": [{"type": "execute", "isAllowed": true}], + "users": ["gina"], + "groups": [], + "delegateAdmin": false, + "constraintInfo": { + "constraintName": "requireApproval", + "parameters": {"approver": "gina-approver"} + } + } + ] + } + } + ] + }, + "updatedTests": [ + { + "name": "deleted constraint policy no longer matches", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "eve", + "userGroups": [], + "requestData": "execute transfer" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "constraint policy added by a delta matches", + "request": { + "resource": {"elements": {"tool": "transfer"}}, + "accessType": "execute", + "user": "gina", + "userGroups": [], + "requestData": "execute transfer" + }, + "result": { + "isAllowed": true, + "isAudited": true, + "constraints": [ + { + "constraintName": "requireApproval", + "parameters": {"approver": "gina-approver"}, + "policyId": 20, + "policyVersion": 1, + "matchType": "SELF" + } + ] + } + } + ] +} diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_constraints_no_defs.json b/agents-common/src/test/resources/policyengine/test_policyengine_constraints_no_defs.json new file mode 100644 index 0000000000..5d42b1800e --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_constraints_no_defs.json @@ -0,0 +1,83 @@ +{ + "serviceName": "hdfsdev", + "serviceDef": { + "name": "hdfs", + "resources": [ + { + "name": "path", + "type": "path", + "level": 1, + "mandatory": true, + "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher", + "matcherOptions": {"wildCard": true, "ignoreCase": true}, + "label": "Resource Path", + "description": "HDFS file or directory path" + } + ], + "accessTypes": [ + {"name": "read", "label": "Read"}, + {"name": "write", "label": "Write"}, + {"name": "execute", "label": "Execute"} + ] + }, + "policies": [ + { + "id": 1, + "name": "allow-read-public", + "isEnabled": true, + "isAuditEnabled": true, + "isDenyAllElse": false, + "resources": {"path": {"values": ["/data"], "isRecursive": true}}, + "policyItems": [ + { + "accesses": [{"type": "read", "isAllowed": true}], + "users": [], + "groups": ["public"], + "delegateAdmin": false + } + ] + }, + { + "id": 2, + "name": "deny-read-user2", + "isEnabled": true, + "isAuditEnabled": true, + "isDenyAllElse": false, + "resources": {"path": {"values": ["/data"], "isRecursive": true}}, + "policyItems": [], + "denyPolicyItems": [ + { + "accesses": [{"type": "read", "isAllowed": true}], + "users": ["user2"], + "groups": [], + "delegateAdmin": false + } + ] + } + ], + "tests": [ + { + "name": "allow unchanged when service has no constraintDefs", + "request": { + "resource": {"elements": {"path": "/data/file"}}, + "accessType": "read", + "user": "user1", + "userGroups": [], + "requestData": "read /data/file" + }, + "result": {"isAllowed": true, "isAudited": true, "constraints": []} + }, + { + "name": "deny unchanged when service has no constraintDefs", + "request": { + "resource": {"elements": {"path": "/data/file"}}, + "accessType": "read", + "user": "user2", + "userGroups": [], + "requestData": "read /data/file" + }, + "result": {"isAllowed": false, "isAudited": true, "constraints": []} + } + ] +} From 971b423c89c680b2c106ad9c4b62fa8f26423d6a Mon Sep 17 00:00:00 2001 From: Vyom Mani Tiwari Date: Sun, 18 Oct 2026 08:48:00 +0530 Subject: [PATCH 2/2] RANGER-5842: Derive constraint hierarchies from access-hierarchy prefixes --- .../validation/RangerServiceDefHelper.java | 51 +++++++------------ .../TestRangerServiceDefHelper.java | 33 ++++++++---- 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java index 00acd5a05c..56583f6cf7 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java @@ -40,7 +40,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -319,8 +318,9 @@ public Set> getResourceHierarchies(Integer policyType, C } /** - * Resource hierarchy for one constraint. Names on that constraint def are ordered by resource level. - * A def that lists no resources uses the access hierarchy. A hierarchy is returned only when it contains every key. + * Resource hierarchies for one constraint. Each access hierarchy contributes its longest prefix + * whose resources are all named on the constraint def. A def that lists no resources uses the access hierarchy. + * A prefix is returned only when it contains every key. */ public Set> getResourceHierarchiesForConstraint(String constraint, Collection keys) { if (LOG.isDebugEnabled()) { @@ -335,10 +335,22 @@ public Set> getResourceHierarchiesForConstraint(String c } else if (CollectionUtils.isEmpty(constraintDef.getResources())) { ret.addAll(getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS, keys)); } else { - List hierarchy = buildConstraintHierarchy(constraintDef.getResources()); + Set names = new HashSet<>(constraintDef.getResources()); - if (CollectionUtils.isNotEmpty(hierarchy) && hierarchyHasAllResources(hierarchy, keys)) { - ret.add(hierarchy); + for (List hierarchy : getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS)) { + List prefix = new ArrayList<>(); + + for (RangerResourceDef resourceDef : hierarchy) { + if (!names.contains(resourceDef.getName())) { + break; + } + + prefix.add(resourceDef); + } + + if (CollectionUtils.isNotEmpty(prefix) && hierarchyHasAllResources(prefix, keys)) { + ret.add(prefix); + } } } @@ -365,33 +377,6 @@ private RangerConstraintDef getConstraintDef(String constraint) { return ret; } - private List buildConstraintHierarchy(List resourceNames) { - Map resourceDefs = new HashMap<>(); - - if (CollectionUtils.isNotEmpty(delegate.servicedef.getResources())) { - for (RangerResourceDef resourceDef : delegate.servicedef.getResources()) { - if (resourceDef != null && resourceDef.getName() != null) { - resourceDefs.putIfAbsent(resourceDef.getName(), resourceDef); - } - } - } - - List ret = new ArrayList<>(); - Set added = new HashSet<>(); - - for (String resourceName : resourceNames) { - RangerResourceDef resourceDef = resourceDefs.get(resourceName); - - if (resourceDef != null && added.add(resourceName)) { - ret.add(resourceDef); - } - } - - ret.sort(Comparator.comparing(RangerResourceDef::getLevel, Comparator.nullsLast(Comparator.naturalOrder()))); - - return ret; - } - public boolean hierarchyHasAllResources(List hierarchy, Collection resourceNames) { if (LOG.isDebugEnabled()) { LOG.debug("==> hierarchyHasAllResources(hierarchy={}, resourceNames={})", StringUtils.join(hierarchy, ","), StringUtils.join(resourceNames, ",")); diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java index 6e22c3140a..f20cd0b309 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java @@ -152,29 +152,30 @@ public void test_getResourceHierarchiesForConstraint() { maxRows.setName("maxRows"); maxRows.setResources(Arrays.asList("Table", "Database")); - RangerConstraintDef requireApproval = new RangerConstraintDef(); - requireApproval.setName("requireApproval"); - requireApproval.setResources(Collections.singletonList("UDF")); + RangerConstraintDef sibling = new RangerConstraintDef(); + sibling.setName("sibling"); + sibling.setResources(Arrays.asList("Database", "Table", "UDF")); + + RangerConstraintDef udfOnly = new RangerConstraintDef(); + udfOnly.setName("udfOnly"); + udfOnly.setResources(Collections.singletonList("UDF")); RangerConstraintDef unrestricted = new RangerConstraintDef(); unrestricted.setName("unrestricted"); - when(serviceDef.getConstraintDefs()).thenReturn(Arrays.asList(maxRows, requireApproval, unrestricted)); + when(serviceDef.getConstraintDefs()).thenReturn(Arrays.asList(maxRows, sibling, udfOnly, unrestricted)); RangerServiceDefHelper helper = new RangerServiceDefHelper(serviceDef); List tableKeys = Arrays.asList("Table"); Set> maxRowsHierarchies = helper.getResourceHierarchiesForConstraint("maxRows", tableKeys); - assertEquals(1, maxRowsHierarchies.size()); - assertEquals(Arrays.asList("Database", "Table"), helper.getAllResourceNamesOrdered(maxRowsHierarchies.iterator().next())); + assertEquals(Collections.singleton(Arrays.asList("Database", "Table")), hierarchyNames(helper, maxRowsHierarchies)); assertTrue(helper.getResourceHierarchiesForConstraint("maxRows", Arrays.asList("Database", "Column")).isEmpty()); - Set> approvalHierarchies = helper.getResourceHierarchiesForConstraint("requireApproval", Collections.singletonList("UDF")); - - assertEquals(1, approvalHierarchies.size()); - assertEquals(Collections.singletonList("UDF"), helper.getAllResourceNamesOrdered(approvalHierarchies.iterator().next())); - assertTrue(helper.getResourceHierarchiesForConstraint("requireApproval", Arrays.asList("Database", "UDF")).isEmpty()); + assertEquals(Collections.singleton(Arrays.asList("Database", "UDF")), hierarchyNames(helper, helper.getResourceHierarchiesForConstraint("sibling", Arrays.asList("Database", "UDF")))); + assertEquals(Collections.singleton(Arrays.asList("Database", "Table")), hierarchyNames(helper, helper.getResourceHierarchiesForConstraint("sibling", Arrays.asList("Database", "Table")))); + assertTrue(helper.getResourceHierarchiesForConstraint("udfOnly", Arrays.asList("Database", "UDF")).isEmpty()); assertEquals(helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS, Arrays.asList("Database", "Table")), helper.getResourceHierarchiesForConstraint("unrestricted", Arrays.asList("Database", "Table"))); assertTrue(helper.getResourceHierarchiesForConstraint("missing", tableKeys).isEmpty()); @@ -1008,6 +1009,16 @@ private RangerResourceDef createMockResourceDef(String name, String parent, Bool return resourceDef; } + private Set> hierarchyNames(RangerServiceDefHelper helper, Set> hierarchies) { + Set> ret = new HashSet<>(); + + for (List hierarchy : hierarchies) { + ret.add(helper.getAllResourceNamesOrdered(hierarchy)); + } + + return ret; + } + private RangerResourceDef createRealResourceDef(String name, String parent, int level, Boolean isValidLeaf) { RangerResourceDef r = new RangerResourceDef(); r.setName(name);