Java tutorial
/* * 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.policyresourcematcher; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.List; 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 org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher; import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; import com.google.common.collect.Sets; public class RangerDefaultPolicyResourceMatcher implements RangerPolicyResourceMatcher { private static final Log LOG = LogFactory.getLog(RangerDefaultPolicyResourceMatcher.class); protected RangerServiceDef serviceDef = null; protected RangerPolicy policy = null; protected Map<String, RangerPolicyResource> policyResources = null; private Map<String, RangerResourceMatcher> matchers = null; private boolean needsDynamicEval = false; private List<RangerResourceDef> firstValidResourceDefHierarchy; /* * For hive resource policy: * lastNonAnyMatcherIndex will be set to * 0 : if all matchers in policy are '*'; such as database=*, table=*, column=* * 1 : database=hr, table=*, column=* * 2 : database=<any>, table=employee, column=* * 3 : database=<any>, table=<any>, column=ssn */ private int lastNonAnyMatcherIndex = 0; @Override public void setServiceDef(RangerServiceDef serviceDef) { this.serviceDef = serviceDef; } @Override public void setPolicy(RangerPolicy policy) { this.policy = policy; setPolicyResources(policy == null ? null : policy.getResources()); } @Override public void setPolicyResources(Map<String, RangerPolicyResource> policyResources) { this.policyResources = policyResources; } @Override public boolean getNeedsDynamicEval() { return needsDynamicEval; } @Override public void init() { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.init()"); } String errorText = ""; if (policyResources != null && policyResources.size() > 0 && serviceDef != null) { Set<String> policyResourceKeySet = policyResources.keySet(); RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef, false); int policyType = policy != null && policy.getPolicyType() != null ? policy.getPolicyType() : RangerPolicy.POLICY_TYPE_ACCESS; Set<List<RangerResourceDef>> validResourceHierarchies = serviceDefHelper .getResourceHierarchies(policyType); for (List<RangerResourceDef> validResourceHierarchy : validResourceHierarchies) { Set<String> resourceDefNameSet = serviceDefHelper.getAllResourceNames(validResourceHierarchy); if ((Sets.difference(policyResourceKeySet, resourceDefNameSet)).isEmpty()) { firstValidResourceDefHierarchy = validResourceHierarchy; break; } } if (firstValidResourceDefHierarchy != null) { List<String> resourceDefNameOrderedList = serviceDefHelper .getAllResourceNamesOrdered(firstValidResourceDefHierarchy); boolean foundGapsInResourceSpecs = false; boolean skipped = false; for (String resourceDefName : resourceDefNameOrderedList) { RangerPolicyResource policyResource = policyResources.get(resourceDefName); if (policyResource == null) { skipped = true; } else if (skipped) { foundGapsInResourceSpecs = true; break; } } if (foundGapsInResourceSpecs) { errorText = "policyResources does not specify contiguous sequence in any valid resourcedef hiearchy."; if (LOG.isDebugEnabled()) { LOG.debug( "RangerDefaultPolicyResourceMatcher.init() failed: Gaps found in policyResources, internal error, skipping.."); } firstValidResourceDefHierarchy = null; } else { matchers = new HashMap<String, RangerResourceMatcher>(); for (RangerResourceDef resourceDef : firstValidResourceDefHierarchy) { String resourceName = resourceDef.getName(); RangerPolicyResource policyResource = policyResources.get(resourceName); if (policyResource != null) { RangerResourceMatcher matcher = createResourceMatcher(resourceDef, policyResource); if (matcher != null) { if (!needsDynamicEval && matcher.getNeedsDynamicEval()) { needsDynamicEval = true; } matchers.put(resourceName, matcher); if (!matcher.isMatchAny()) { lastNonAnyMatcherIndex = matchers.size(); } } else { LOG.error("failed to find matcher for resource " + resourceName); } } else { if (LOG.isDebugEnabled()) { LOG.debug("RangerDefaultPolicyResourceMatcher.init() - no matcher created for " + resourceName + ". Continuing ..."); } } } } } else { errorText = "policyResources elements are not part of any valid resourcedef hierarchy."; } } else { errorText = " policyResources is null or empty, or serviceDef is null."; } if (matchers == null) { Set<String> policyResourceKeys = policyResources == null ? null : policyResources.keySet(); StringBuilder sb = new StringBuilder(); if (CollectionUtils.isNotEmpty(policyResourceKeys)) { for (String policyResourceKeyName : policyResourceKeys) { sb.append(" ").append(policyResourceKeyName).append(" "); } } String keysString = sb.toString(); String serviceDefName = serviceDef == null ? "" : serviceDef.getName(); String validHierarchy = ""; if (serviceDef != null && CollectionUtils.isNotEmpty(firstValidResourceDefHierarchy)) { RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef, false); List<String> resourceDefNameOrderedList = serviceDefHelper .getAllResourceNamesOrdered(firstValidResourceDefHierarchy); for (String resourceDefName : resourceDefNameOrderedList) { validHierarchy += " " + resourceDefName + " "; } } LOG.warn("RangerDefaultPolicyResourceMatcher.init() failed: " + errorText + " (serviceDef=" + serviceDefName + ", policyResourceKeys=" + keysString + ", validHierarchy=" + validHierarchy + ")"); } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyResourceMatcher.init()"); } } @Override public RangerServiceDef getServiceDef() { return serviceDef; } @Override public RangerResourceMatcher getResourceMatcher(String resourceName) { return matchers != null ? matchers.get(resourceName) : null; } @Override public boolean isMatch(Map<String, RangerPolicyResource> resources, Map<String, Object> evalContext) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.isMatch(" + resources + ", " + evalContext + ")"); } boolean ret = false; if (serviceDef != null && serviceDef.getResources() != null) { Collection<String> resourceKeys = resources == null ? null : resources.keySet(); Collection<String> policyKeys = matchers == null ? null : matchers.keySet(); boolean keysMatch = CollectionUtils.isEmpty(resourceKeys) || (policyKeys != null && policyKeys.containsAll(resourceKeys)); if (keysMatch) { for (RangerResourceDef resourceDef : serviceDef.getResources()) { String resourceName = resourceDef.getName(); RangerPolicyResource resourceValues = resources == null ? null : resources.get(resourceName); RangerResourceMatcher matcher = matchers == null ? null : matchers.get(resourceName); // when no value exists for a resourceName, consider it a match only if: policy doesn't have a matcher OR matcher allows no-value resource if (resourceValues == null || CollectionUtils.isEmpty(resourceValues.getValues())) { ret = matcher == null || matcher.isMatch(null, null); } else if (matcher != null) { for (String resourceValue : resourceValues.getValues()) { ret = matcher.isMatch(resourceValue, evalContext); if (!ret) { break; } } } if (!ret) { break; } } } else { if (LOG.isDebugEnabled()) { LOG.debug("isMatch(): keysMatch=false. resourceKeys=" + resourceKeys + "; policyKeys=" + policyKeys); } } } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyResourceMatcher.isMatch(" + resources + ", " + evalContext + "): " + ret); } return ret; } @Override public boolean isCompleteMatch(RangerAccessResource resource, Map<String, Object> evalContext) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.isCompleteMatch(" + resource + ", " + evalContext + ")"); } boolean ret = false; if (serviceDef != null && serviceDef.getResources() != null) { Collection<String> resourceKeys = resource == null ? null : resource.getKeys(); Collection<String> policyKeys = matchers == null ? null : matchers.keySet(); boolean keysMatch = false; if (resourceKeys != null && policyKeys != null) { keysMatch = CollectionUtils.isEqualCollection(resourceKeys, policyKeys); } if (keysMatch) { for (RangerResourceDef resourceDef : serviceDef.getResources()) { String resourceName = resourceDef.getName(); String resourceValue = resource == null ? null : resource.getValue(resourceName); RangerResourceMatcher matcher = matchers == null ? null : matchers.get(resourceName); if (StringUtils.isEmpty(resourceValue)) { ret = matcher == null || matcher.isCompleteMatch(resourceValue, evalContext); } else { ret = matcher != null && matcher.isCompleteMatch(resourceValue, evalContext); } if (!ret) { break; } } } else { if (LOG.isDebugEnabled()) { LOG.debug("isCompleteMatch(): keysMatch=false. resourceKeys=" + resourceKeys + "; policyKeys=" + policyKeys); } } } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyResourceMatcher.isCompleteMatch(" + resource + ", " + evalContext + "): " + ret); } return ret; } @Override public boolean isCompleteMatch(Map<String, RangerPolicyResource> resources, Map<String, Object> evalContext) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.isCompleteMatch(" + resources + ", " + evalContext + ")"); } boolean ret = false; if (serviceDef != null && serviceDef.getResources() != null) { Collection<String> resourceKeys = resources == null ? null : resources.keySet(); Collection<String> policyKeys = matchers == null ? null : matchers.keySet(); boolean keysMatch = false; if (resourceKeys != null && policyKeys != null) { keysMatch = CollectionUtils.isEqualCollection(resourceKeys, policyKeys); } if (keysMatch) { for (RangerResourceDef resourceDef : serviceDef.getResources()) { String resourceName = resourceDef.getName(); RangerPolicyResource resourceValues = resources == null ? null : resources.get(resourceName); RangerPolicyResource policyValues = policyResources == null ? null : policyResources.get(resourceName); if (resourceValues == null || CollectionUtils.isEmpty(resourceValues.getValues())) { ret = (policyValues == null || CollectionUtils.isEmpty(policyValues.getValues())); } else if (policyValues != null && CollectionUtils.isNotEmpty(policyValues.getValues())) { ret = CollectionUtils.isEqualCollection(resourceValues.getValues(), policyValues.getValues()); } if (!ret) { break; } } } else { if (LOG.isDebugEnabled()) { LOG.debug("isCompleteMatch(): keysMatch=false. resourceKeys=" + resourceKeys + "; policyKeys=" + policyKeys); } } } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyResourceMatcher.isCompleteMatch(" + resources + ", " + evalContext + "): " + ret); } return ret; } @Override public boolean isMatch(RangerAccessResource resource, Map<String, Object> evalContext) { return isMatch(resource, MatchScope.SELF_OR_ANCESTOR, evalContext); } @Override public boolean isMatch(RangerAccessResource resource, MatchScope scope, Map<String, Object> evalContext) { final boolean ret; MatchType matchType = getMatchType(resource, evalContext); switch (scope) { case SELF_OR_ANCESTOR_OR_DESCENDANT: { ret = matchType != MatchType.NONE; break; } case SELF: { ret = matchType == MatchType.SELF; break; } case SELF_OR_DESCENDANT: { ret = matchType == MatchType.SELF || matchType == MatchType.DESCENDANT; break; } case SELF_OR_ANCESTOR: { ret = matchType == MatchType.SELF || matchType == MatchType.ANCESTOR; break; } case DESCENDANT: { ret = matchType == MatchType.DESCENDANT; break; } case ANCESTOR: { ret = matchType == MatchType.ANCESTOR; break; } default: ret = matchType != MatchType.NONE; break; } return ret; } @Override public MatchType getMatchType(RangerAccessResource resource, Map<String, Object> evalContext) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.getMatchType(" + resource + evalContext + ")"); } int matchersSize = matchers == null ? 0 : matchers.size(); int resourceKeysSize = resource == null || resource.getKeys() == null ? 0 : resource.getKeys().size(); MatchType ret = MatchType.NONE; if (!isValid(resource)) { ret = MatchType.NONE; } else if (matchersSize == 0 || lastNonAnyMatcherIndex == 0) { ret = resourceKeysSize == 0 ? MatchType.SELF : MatchType.ANCESTOR; } else if (resourceKeysSize == 0) { ret = MatchType.DESCENDANT; } else { int index = 0; for (RangerResourceDef resourceDef : firstValidResourceDefHierarchy) { String resourceName = resourceDef.getName(); RangerResourceMatcher matcher = matchers.get(resourceName); String resourceValue = resource.getValue(resourceName); if (resourceValue != null) { if (matcher != null) { index++; if (matcher.isMatch(resourceValue, evalContext)) { ret = index == resourceKeysSize && matcher.isMatchAny() ? MatchType.ANCESTOR : MatchType.SELF; } else { ret = MatchType.NONE; break; } } else { // More resource-levels than matchers ret = MatchType.ANCESTOR; break; } } else { if (matcher != null) { // More matchers than resource-levels if (index >= lastNonAnyMatcherIndex) { // All AnyMatch matchers after this ret = MatchType.ANCESTOR; } else { ret = MatchType.DESCENDANT; } } break; } } } if (LOG.isDebugEnabled()) { LOG.debug( "<== RangerDefaultPolicyResourceMatcher.getMatchType(" + resource + evalContext + "): " + ret); } return ret; } private boolean isValid(RangerAccessResource resource) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.isValid(" + resource + ")"); } boolean ret = true; boolean skipped = false; if (matchers != null && resource != null && resource.getKeys() != null) { if (matchers.keySet().containsAll(resource.getKeys()) || resource.getKeys().containsAll(matchers.keySet())) { for (RangerResourceDef resourceDef : firstValidResourceDefHierarchy) { String resourceName = resourceDef.getName(); String resourceValue = resource.getValue(resourceName); if (resourceValue == null) { if (!skipped) { skipped = true; } } else { if (skipped) { ret = false; break; } } } } else { ret = false; } } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyResourceMatcher.isValid(" + resource + "): " + ret); } return ret; } @Override public String toString() { StringBuilder sb = new StringBuilder(); toString(sb); return sb.toString(); } @Override public StringBuilder toString(StringBuilder sb) { sb.append("RangerDefaultPolicyResourceMatcher={"); sb.append("matchers={"); if (matchers != null) { for (RangerResourceMatcher matcher : matchers.values()) { sb.append("{").append(matcher).append("} "); } } sb.append("} "); sb.append("}"); return sb; } protected static RangerResourceMatcher createResourceMatcher(RangerResourceDef resourceDef, RangerPolicyResource resource) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.createResourceMatcher(" + resourceDef + ", " + resource + ")"); } RangerResourceMatcher ret = null; if (resourceDef != null) { String resName = resourceDef.getName(); String clsName = resourceDef.getMatcher(); if (!StringUtils.isEmpty(clsName)) { try { @SuppressWarnings("unchecked") Class<RangerResourceMatcher> matcherClass = (Class<RangerResourceMatcher>) Class .forName(clsName); ret = matcherClass.newInstance(); } catch (Exception excp) { LOG.error("failed to instantiate resource matcher '" + clsName + "' for '" + resName + "'. Default resource matcher will be used", excp); } } if (ret == null) { ret = new RangerDefaultResourceMatcher(); } if (ret != null) { ret.setResourceDef(resourceDef); ret.setPolicyResource(resource); ret.init(); } } else { LOG.error("RangerDefaultPolicyResourceMatcher: RangerResourceDef is null"); } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyResourceMatcher.createResourceMatcher(" + resourceDef + ", " + resource + "): " + ret); } return ret; } }