org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher.java

Source

/*
 * 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;
    }

}