From c07ddd71f6faee25059de244259cff2cb7656668 Mon Sep 17 00:00:00 2001 From: Nikhil P Bonte Date: Fri, 8 Mar 2024 20:22:02 +0530 Subject: [PATCH] added_list_values_support_similar_to_contains_adminUser --- .../validation/RangerServiceDefHelper.java | 4 + .../policyengine/RangerPolicyRepository.java | 64 +++--- .../policyengine/RangerResourceTrie.java | 5 + .../RangerDefaultPolicyResourceMatcher.java | 3 + .../RangerEntityResourceMatcher.java | 208 ++++++++++++++++++ .../resourcematcher/ResourceMatcher.java | 1 + .../service-defs/atlas-servicedef-atlas.json | 20 ++ .../authorizer/RangerAtlasAuthorizer.java | 10 + 8 files changed, 284 insertions(+), 31 deletions(-) create mode 100644 auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/RangerEntityResourceMatcher.java diff --git a/auth-agents-common/src/main/java/org/apache/atlas/plugin/model/validation/RangerServiceDefHelper.java b/auth-agents-common/src/main/java/org/apache/atlas/plugin/model/validation/RangerServiceDefHelper.java index ddc359b3cd4..fd811e159cb 100644 --- a/auth-agents-common/src/main/java/org/apache/atlas/plugin/model/validation/RangerServiceDefHelper.java +++ b/auth-agents-common/src/main/java/org/apache/atlas/plugin/model/validation/RangerServiceDefHelper.java @@ -533,6 +533,10 @@ Set> getHierarchies(DirectedGraph graph, Map getLikelyMatchPolicyEvaluators(Map serviceResourceMatchersForResource = trie.getEvaluatorsForResource(resource.getValue(resourceName), request.getResourceMatchingScope()); Set inheritedResourceMatchers = trie.getInheritedEvaluators(); - if (smallestList != null) { - if (CollectionUtils.isEmpty(inheritedResourceMatchers) && CollectionUtils.isEmpty(serviceResourceMatchersForResource)) { - smallestList = null; - } else if (CollectionUtils.isEmpty(inheritedResourceMatchers)) { - smallestList.retainAll(serviceResourceMatchersForResource); - } else if (CollectionUtils.isEmpty(serviceResourceMatchersForResource)) { - smallestList.retainAll(inheritedResourceMatchers); - } else { - Set smaller, bigger; - if (serviceResourceMatchersForResource.size() < inheritedResourceMatchers.size()) { - smaller = serviceResourceMatchersForResource; - bigger = inheritedResourceMatchers; - } else { - smaller = inheritedResourceMatchers; - bigger = serviceResourceMatchersForResource; - } - Set tmp = new HashSet<>(); - if (smallestList.size() < smaller.size()) { - smallestList.stream().filter(smaller::contains).forEach(tmp::add); - smallestList.stream().filter(bigger::contains).forEach(tmp::add); + if (serviceResourceMatchersForResource != null) { + if (smallestList != null) { + if (CollectionUtils.isEmpty(inheritedResourceMatchers) && CollectionUtils.isEmpty(serviceResourceMatchersForResource)) { + smallestList = null; + } else if (CollectionUtils.isEmpty(inheritedResourceMatchers)) { + smallestList.retainAll(serviceResourceMatchersForResource); + } else if (CollectionUtils.isEmpty(serviceResourceMatchersForResource)) { + smallestList.retainAll(inheritedResourceMatchers); } else { - smaller.stream().filter(smallestList::contains).forEach(tmp::add); - if (smallestList.size() < bigger.size()) { + Set smaller, bigger; + if (serviceResourceMatchersForResource.size() < inheritedResourceMatchers.size()) { + smaller = serviceResourceMatchersForResource; + bigger = inheritedResourceMatchers; + } else { + smaller = inheritedResourceMatchers; + bigger = serviceResourceMatchersForResource; + } + Set tmp = new HashSet<>(); + if (smallestList.size() < smaller.size()) { + smallestList.stream().filter(smaller::contains).forEach(tmp::add); smallestList.stream().filter(bigger::contains).forEach(tmp::add); } else { - bigger.stream().filter(smallestList::contains).forEach(tmp::add); + smaller.stream().filter(smallestList::contains).forEach(tmp::add); + if (smallestList.size() < bigger.size()) { + smallestList.stream().filter(bigger::contains).forEach(tmp::add); + } else { + bigger.stream().filter(smallestList::contains).forEach(tmp::add); + } } + smallestList = tmp; } - smallestList = tmp; - } - } else { - if (CollectionUtils.isEmpty(inheritedResourceMatchers) || CollectionUtils.isEmpty(serviceResourceMatchersForResource)) { - Set tmp = CollectionUtils.isEmpty(inheritedResourceMatchers) ? serviceResourceMatchersForResource : inheritedResourceMatchers; - smallestList = resourceKeys.size() == 1 || CollectionUtils.isEmpty(tmp) ? tmp : new HashSet<>(tmp); } else { - smallestList = new HashSet<>(serviceResourceMatchersForResource); - smallestList.addAll(inheritedResourceMatchers); + if (CollectionUtils.isEmpty(inheritedResourceMatchers) || CollectionUtils.isEmpty(serviceResourceMatchersForResource)) { + Set tmp = CollectionUtils.isEmpty(inheritedResourceMatchers) ? serviceResourceMatchersForResource : inheritedResourceMatchers; + smallestList = resourceKeys.size() == 1 || CollectionUtils.isEmpty(tmp) ? tmp : new HashSet<>(tmp); + } else { + smallestList = new HashSet<>(serviceResourceMatchersForResource); + smallestList.addAll(inheritedResourceMatchers); + } } } diff --git a/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyengine/RangerResourceTrie.java b/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyengine/RangerResourceTrie.java index ffcf768b19e..5cb7b70a249 100644 --- a/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyengine/RangerResourceTrie.java +++ b/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyengine/RangerResourceTrie.java @@ -402,7 +402,12 @@ private TrieNode buildTrie(RangerResourceDef resourceDef, List evaluators, ret.addWildcardEvaluator(evaluator); } else { if (CollectionUtils.isNotEmpty(policyResource.getValues())) { + boolean isEntityAttrs = resourceDef.getName().equals("entityAttr"); for (String resource : policyResource.getValues()) { + if (isEntityAttrs && !resource.equals("*")) { + resource = resource.split(":")[1]; + } + if (!isMultiThreaded) { insert(ret, resource, policyResource.getIsRecursive(), evaluator); } else { diff --git a/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java b/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java index b6f969ebf5f..77ae848de65 100644 --- a/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java +++ b/auth-agents-common/src/main/java/org/apache/atlas/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java @@ -91,6 +91,9 @@ public void setPolicyResources(Map policyResources @Override public void setPolicyResources(Map policyResources, String policyType) { this.policyResources = policyResources; + if (this.policyResources.keySet().contains("entity-type") && !this.policyResources.keySet().contains("entityAttr")) { + this.policyResources.put("entityAttr", new RangerPolicyResource("*")); + } this.policyType = policyType; } diff --git a/auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/RangerEntityResourceMatcher.java b/auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/RangerEntityResourceMatcher.java new file mode 100644 index 00000000000..14fe8f56ba9 --- /dev/null +++ b/auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/RangerEntityResourceMatcher.java @@ -0,0 +1,208 @@ +/* + * 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.atlas.plugin.resourcematcher; + + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + + +public class RangerEntityResourceMatcher extends RangerAbstractResourceMatcher { + private static final Log LOG = LogFactory.getLog(RangerEntityResourceMatcher.class); + + @Override + public void init() { + if(LOG.isDebugEnabled()) { + LOG.debug("==> RangerAbstractResourceMatcher.init()"); + } + + Map options = resourceDef != null ? resourceDef.getMatcherOptions() : null; + + optIgnoreCase = getOptionIgnoreCase(options); + optQuotedCaseSensitive = getOptionQuotedCaseSensitive(options); + optQuoteChars = getOptionQuoteChars(options); + optWildCard = getOptionWildCard(options); + + policyValues = new ArrayList<>(); + policyIsExcludes = policyResource != null && policyResource.getIsExcludes(); + + if (policyResource != null && policyResource.getValues() != null) { + for (String policyValue : policyResource.getValues()) { + if (StringUtils.isEmpty(policyValue)) { + continue; + } + policyValues.add(policyValue); + } + } + + optReplaceTokens = getOptionReplaceTokens(options); + + if(optReplaceTokens) { + startDelimiterChar = getOptionDelimiterStart(options); + endDelimiterChar = getOptionDelimiterEnd(options); + escapeChar = getOptionDelimiterEscape(options); + tokenPrefix = getOptionDelimiterPrefix(options); + + if(escapeChar == startDelimiterChar || escapeChar == endDelimiterChar || + tokenPrefix.indexOf(escapeChar) != -1 || tokenPrefix.indexOf(startDelimiterChar) != -1 || + tokenPrefix.indexOf(endDelimiterChar) != -1) { + String resouceName = resourceDef == null ? "" : resourceDef.getName(); + + String msg = "Invalid token-replacement parameters for resource '" + resouceName + "': { "; + msg += (OPTION_TOKEN_DELIMITER_START + "='" + startDelimiterChar + "'; "); + msg += (OPTION_TOKEN_DELIMITER_END + "='" + endDelimiterChar + "'; "); + msg += (OPTION_TOKEN_DELIMITER_ESCAPE + "='" + escapeChar + "'; "); + msg += (OPTION_TOKEN_DELIMITER_PREFIX + "='" + tokenPrefix + "' }. "); + msg += "Token replacement disabled"; + + LOG.error(msg); + + optReplaceTokens = false; + } + } + + resourceMatchers = buildResourceMatchers(); + isMatchAny = resourceMatchers == null || CollectionUtils.isEmpty(resourceMatchers.getResourceMatchers()); + + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerAbstractResourceMatcher.init()"); + } + } + + protected ResourceMatcherWrapper buildResourceMatchers() { + List resourceMatchers = new ArrayList<>(); + boolean needsDynamicEval = false; + + for (String policyValue : policyValues) { + ResourceMatcher matcher = getMatcher(policyValue); + + if (matcher != null) { + if (matcher.isMatchAny()) { + resourceMatchers.clear(); + break; + } + if (!needsDynamicEval && matcher.getNeedsDynamicEval()) { + needsDynamicEval = true; + } + resourceMatchers.add(matcher); + } + } + + Collections.sort(resourceMatchers, new ResourceMatcher.PriorityComparator()); + + return CollectionUtils.isNotEmpty(resourceMatchers) ? + new ResourceMatcherWrapper(needsDynamicEval, resourceMatchers) : null; + } + + @Override + public boolean isMatch(Object resource, Map evalContext) { + if(LOG.isDebugEnabled()) { + LOG.debug("==> RangerDefaultResourceMatcher.isMatch(" + resource + ", " + evalContext + ")"); + } + + boolean ret = false; + boolean allValuesRequested = isAllValuesRequested(resource); + + if(allValuesRequested || isMatchAny) { + ret = isMatchAny; + } else { + int resourceMatcherCount = resourceMatchers.getResourceMatchers().size(); + int matchedResourceMatcherCount = 0; + + for (ResourceMatcher resourceMatcher : resourceMatchers.getResourceMatchers()) { + Object value = ((Map) resource).get(resourceMatcher.attrName); + + if (value instanceof String) { + boolean result = resourceMatcher.isMatch((String) value, evalContext); + if (result) { + matchedResourceMatcherCount++; + } + } else if (value instanceof Collection) { + Collection collValue = (Collection) value; + + /*ret = resourceMatcher.isMatchAny(collValue, evalContext); + if (ret) { + break; + }*/ + + boolean result = resourceMatcher.isMatchAny(collValue, evalContext); + if (result) { + matchedResourceMatcherCount++; + } + } + } + + if (matchedResourceMatcherCount == resourceMatcherCount) { + ret = true; //all resources for entityAttr matched + } + } + + ret = applyExcludes(allValuesRequested, ret); + + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerDefaultResourceMatcher.isMatch(" + resource + ", " + evalContext + "): " + ret); + } + + return ret; + } + + ResourceMatcher getMatcher(String policyValueRaw) { + final ResourceMatcher ret; + + if (policyValueRaw.equals("*")){ + return super.getMatcher(policyValueRaw); + } + + String[] splitted = policyValueRaw.split(":"); + String attrName = splitted[0]; + String policyValue = splitted[1]; + + switch (attrName) { + case "certificateStatus": + ret = super.getMatcher(policyValue); break; + + + + default: + ret = super.getMatcher(policyValue); + } + ret.attrName = attrName; + + return ret; + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerDefaultResourceMatcher={"); + + super.toString(sb); + + sb.append("}"); + + return sb; + } +} diff --git a/auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/ResourceMatcher.java b/auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/ResourceMatcher.java index f23667a5f1b..97518aeb3e5 100644 --- a/auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/ResourceMatcher.java +++ b/auth-agents-common/src/main/java/org/apache/atlas/plugin/resourcematcher/ResourceMatcher.java @@ -33,6 +33,7 @@ abstract class ResourceMatcher { private static final Log LOG = LogFactory.getLog(ResourceMatcher.class); protected final String value; + protected String attrName; protected StringTokenReplacer tokenReplacer; static final int DYNAMIC_EVALUATION_PENALTY = 8; diff --git a/auth-agents-common/src/main/resources/service-defs/atlas-servicedef-atlas.json b/auth-agents-common/src/main/resources/service-defs/atlas-servicedef-atlas.json index 1ce05fbb66b..fe94bc31e4f 100644 --- a/auth-agents-common/src/main/resources/service-defs/atlas-servicedef-atlas.json +++ b/auth-agents-common/src/main/resources/service-defs/atlas-servicedef-atlas.json @@ -316,6 +316,26 @@ "entity-update-classification", "entity-remove-classification" ] + }, + { + "itemId": 17, + "name": "entityAttr", + "type": "string", + "level": 40, + "parent": "entity", + "mandatory": false, + "isValidLeaf": true, + "lookupSupported": true, + "recursiveSupported": true, + "excludesSupported": true, + "matcher": "org.apache.atlas.plugin.resourcematcher.RangerEntityResourceMatcher", + "matcherOptions": { + "wildCard": "true", + "ignoreCase": "false" + }, + "label": "Entity", + "description": "Entity", + "accessTypeRestrictions": ["entity-read", "entity-create", "entity-update", "entity-delete"] } ], "accessTypes": [ diff --git a/auth-plugin-atlas/src/main/java/org/apache/atlas/authorization/atlas/authorizer/RangerAtlasAuthorizer.java b/auth-plugin-atlas/src/main/java/org/apache/atlas/authorization/atlas/authorizer/RangerAtlasAuthorizer.java index b97777bc38a..d1c4385eeb5 100644 --- a/auth-plugin-atlas/src/main/java/org/apache/atlas/authorization/atlas/authorizer/RangerAtlasAuthorizer.java +++ b/auth-plugin-atlas/src/main/java/org/apache/atlas/authorization/atlas/authorizer/RangerAtlasAuthorizer.java @@ -61,6 +61,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static org.apache.atlas.authorization.atlas.authorizer.RangerAtlasAuthorizerUtil.*; import static org.apache.atlas.authorize.AtlasAuthorizationUtils.getCurrentUserGroups; @@ -667,6 +668,15 @@ private boolean isAccessAllowed(AtlasEntityAccessRequest request, RangerAtlasAud rangerResource.setValue(RESOURCE_ENTITY_TYPE, entityTypes); rangerResource.setValue(RESOURCE_ENTITY_ID, entityId); + //Set attributes = request.getEntity().getAttributes().entrySet().stream().map(x -> x.getKey() + ":" + x.getValue()).collect(Collectors.toSet()); + //rangerResource.setValue("entityAttr", attributes); + + rangerResource.setValue("entityAttr", request.getEntity().getAttributes()); + + //rangerResource.setValue("certificateStatus", request.getEntity().getAttribute("certificateStatus")); + //rangerResource.setValue("__typeName", request.getEntity().getTypeName()); + //rangerResource.setValue("adminUsers", request.getEntity().getAttribute("adminUsers")); + //rangerResource.setValue("connectionQualifiedName", "connectionQualifiedName:" + (String) request.getEntity().getAttribute("connectionQualifiedName")); rangerResource.setOwnerUser(ownerUser); rangerRequest.setAccessType(action); rangerRequest.setAction(action);