blob: 5e0b54c3d8f5ccb4ae8c6e6ef0e0df9da882ae54 [file] [log] [blame]
/*
* 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.Iterator;
import java.util.Map;
import java.util.Set;
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.RangerPolicyResource;
import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
import org.apache.ranger.plugin.model.RangerServiceDef;
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 Map<String, RangerPolicyResource> policyResources = null;
private Map<String, RangerResourceMatcher> matchers = null;
@Override
public void setServiceDef(RangerServiceDef serviceDef) {
this.serviceDef = serviceDef;
}
@Override
public void setPolicyResources(Map<String, RangerPolicyResource> policyResources) {
this.policyResources = policyResources;
}
@Override
public void init() {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerDefaultPolicyResourceMatcher.init()");
}
this.matchers = new HashMap<String, RangerResourceMatcher>();
if(policyResources != null && serviceDef != null) {
for(RangerResourceDef resourceDef : serviceDef.getResources()) {
String resourceName = resourceDef.getName();
RangerPolicyResource policyResource = policyResources.get(resourceName);
if(policyResource != null) {
RangerResourceMatcher matcher = createResourceMatcher(resourceDef, policyResource);
if(matcher != null) {
matchers.put(resourceName, matcher);
} else {
LOG.error("failed to find matcher for resource " + resourceName);
}
}
}
}
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerDefaultPolicyResourceMatcher.init()");
}
}
@Override
public RangerResourceMatcher getResourceMatcher(String resourceName) {
return matchers != null ? matchers.get(resourceName) : null;
}
@Override
public boolean isMatch(RangerAccessResource resource) {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerDefaultPolicyResourceMatcher.isMatch(" + resource + ")");
}
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 = CollectionUtils.isEmpty(resourceKeys) || (policyKeys != null && policyKeys.containsAll(resourceKeys));
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);
// 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(StringUtils.isEmpty(resourceValue)) {
ret = matcher == null || matcher.isMatch(resourceValue);
} else {
ret = matcher != null && matcher.isMatch(resourceValue);
}
if(! ret) {
break;
}
}
} else {
if(LOG.isDebugEnabled()) {
LOG.debug("isMatch(): keysMatch=false. isMatch=" + resourceKeys + "; policyKeys=" + policyKeys);
}
}
}
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerDefaultPolicyResourceMatcher.isMatch(" + resource + "): " + ret);
}
return ret;
}
@Override
public boolean isMatch(Map<String, RangerPolicyResource> resources) {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerDefaultPolicyResourceMatcher.isMatch(" + resources + ")");
}
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);
} else if(matcher != null) {
for(String resourceValue : resourceValues.getValues()) {
ret = matcher.isMatch(resourceValue);
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 + "): " + ret);
}
return ret;
}
@Override
public boolean isSingleAndExactMatch(RangerAccessResource resource) {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerDefaultPolicyResourceMatcher.isSingleAndExactMatch(" + resource + ")");
}
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.isSingleAndExactMatch(resourceValue);
} else {
ret = matcher != null && matcher.isSingleAndExactMatch(resourceValue);
}
if(! ret) {
break;
}
}
} else {
if(LOG.isDebugEnabled()) {
LOG.debug("isSingleAndExactMatch(): keysMatch=false. resourceKeys=" + resourceKeys + "; policyKeys=" + policyKeys);
}
}
}
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerDefaultPolicyResourceMatcher.isSingleAndExactMatch(" + resource + "): " + ret);
}
return ret;
}
@Override
public boolean isHeadMatch(RangerAccessResource resource) {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerDefaultPolicyResourceMatcher.matchResourceHead(" + resource + ")");
}
boolean ret;
if (resource == null || CollectionUtils.isEmpty(resource.getKeys())) { // sanity-check, firewalling
LOG.debug("isHeadMatch: resource was null/empty!");
ret = true; // null resource matches anything
} else if (serviceDef == null) { // sanity-check, firewalling
LOG.debug("isHeadMatch: service-def was null!");
ret = false; // null policy can never match a non-empty resource
} else if (policyResources == null) {
LOG.debug("isHeadMatch: policyResources were null!");
ret = false; // null policy can never match a non-empty resource
} else if (matchers == null || matchers.size() != policyResources.size()) { // sanity-check, firewalling
LOG.debug("isHeadMatch: matchers could be found for some of the policy resources");
ret = false; // empty policy can never match a non-empty resources and can't be evaluated meaningfully in matchers are absent
} else {
if (!Sets.difference(resource.getKeys(), matchers.keySet()).isEmpty()) { // e.g. avoid using udf policy for resource that has more than db specified and vice-versa
LOG.debug("isHeadMatch: resource/policy resource-keys mismatch. policy is incompatible with resource; can't match.");
ret = false;
} else {
Set<String> policyResourceNames = matchers.keySet();
boolean skipped = false;
boolean matched = true;
Iterator<RangerResourceDef> iterator = serviceDef.getResources().iterator();
while (iterator.hasNext() && matched) {
RangerResourceDef resourceDef = iterator.next();
String resourceName = resourceDef.getName();
// we only work with resources that are relevant to this policy
if (policyResourceNames.contains(resourceName)) {
String resourceValue = resource.getValue(resourceName);
if (StringUtils.isEmpty(resourceValue)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Skipping matching for " + resourceName + " since it is null/empty on resource");
}
skipped = true; // once we skip a level all lower levels must be skippable, too
} else if (skipped == true) {
LOG.debug("isHeadMatch: found a lower level resource when a higer level resource was absent!");
matched = false;
} else if (!matchers.get(resourceName).isMatch(resourceValue)) {
if (LOG.isDebugEnabled()) {
LOG.debug("isHeadMatch: matcher for " + resourceName + " failed");
}
matched = false;
}
}
}
ret = matched;
}
}
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerDefaultPolicyResourceMatcher.matchResourceHead(" + 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;
}
}