org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher.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.resourcematcher;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase;
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.util.ServiceDefUtil;

public abstract class RangerAbstractResourceMatcher implements RangerResourceMatcher {
    private static final Log LOG = LogFactory.getLog(RangerAbstractResourceMatcher.class);

    public final static String WILDCARD_ASTERISK = "*";

    public final static String OPTION_IGNORE_CASE = "ignoreCase";
    public final static String OPTION_WILD_CARD = "wildCard";
    public final static String OPTION_REPLACE_TOKENS = "replaceTokens";
    public final static String OPTION_TOKEN_DELIMITER_START = "tokenDelimiterStart";
    public final static String OPTION_TOKEN_DELIMITER_END = "tokenDelimiterEnd";
    public final static String OPTION_TOKEN_DELIMITER_ESCAPE = "tokenDelimiterEscape";
    public final static String OPTION_TOKEN_DELIMITER_PREFIX = "tokenDelimiterPrefix";

    protected RangerResourceDef resourceDef = null;
    protected RangerPolicyResource policyResource = null;

    protected boolean optIgnoreCase = false;
    protected boolean optWildCard = false;

    protected List<String> policyValues = null;
    protected boolean policyIsExcludes = false;
    protected boolean isMatchAny = false;
    protected ResourceMatcherWrapper resourceMatchers = null;

    protected boolean optReplaceTokens = false;
    protected char startDelimiterChar = '{';
    protected char endDelimiterChar = '}';
    protected char escapeChar = '\\';
    protected String tokenPrefix = "";

    @Override
    public void setResourceDef(RangerResourceDef resourceDef) {
        this.resourceDef = resourceDef;
    }

    @Override
    public void setPolicyResource(RangerPolicyResource policyResource) {
        this.policyResource = policyResource;
    }

    @Override
    public void init() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("==> RangerAbstractResourceMatcher.init()");
        }

        Map<String, String> options = resourceDef != null ? resourceDef.getMatcherOptions() : null;

        optIgnoreCase = getOptionIgnoreCase(options);
        optWildCard = getOptionWildCard(options);

        policyValues = new ArrayList<String>();
        policyIsExcludes = policyResource == null ? false : 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()");
        }
    }

    @Override
    public boolean isMatchAny() {
        return isMatchAny;
    }

    public boolean getNeedsDynamicEval() {
        return resourceMatchers != null && resourceMatchers.getNeedsDynamicEval();
    }

    public static boolean getOptionIgnoreCase(Map<String, String> options) {
        return ServiceDefUtil.getBooleanOption(options, OPTION_IGNORE_CASE, true);
    }

    public static boolean getOptionWildCard(Map<String, String> options) {
        return ServiceDefUtil.getBooleanOption(options, OPTION_WILD_CARD, true);
    }

    public static boolean getOptionReplaceTokens(Map<String, String> options) {
        return ServiceDefUtil.getBooleanOption(options, OPTION_REPLACE_TOKENS, true);
    }

    public static char getOptionDelimiterStart(Map<String, String> options) {
        return ServiceDefUtil.getCharOption(options, OPTION_TOKEN_DELIMITER_START, '{');
    }

    public static char getOptionDelimiterEnd(Map<String, String> options) {
        return ServiceDefUtil.getCharOption(options, OPTION_TOKEN_DELIMITER_END, '}');
    }

    public static char getOptionDelimiterEscape(Map<String, String> options) {
        return ServiceDefUtil.getCharOption(options, OPTION_TOKEN_DELIMITER_ESCAPE, '\\');
    }

    public static String getOptionDelimiterPrefix(Map<String, String> options) {
        return ServiceDefUtil.getOption(options, OPTION_TOKEN_DELIMITER_PREFIX, "");
    }

    protected ResourceMatcherWrapper buildResourceMatchers() {
        List<ResourceMatcher> resourceMatchers = new ArrayList<ResourceMatcher>();
        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);

        return CollectionUtils.isNotEmpty(resourceMatchers)
                ? new ResourceMatcherWrapper(needsDynamicEval, resourceMatchers)
                : null;
    }

    @Override
    public boolean isCompleteMatch(String resource, Map<String, Object> evalContext) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("==> RangerAbstractResourceMatcher.isCompleteMatch(" + resource + ", " + evalContext + ")");
        }

        boolean ret = false;

        if (CollectionUtils.isEmpty(policyValues)) {
            ret = StringUtils.isEmpty(resource);
        } else if (policyValues.size() == 1) {
            String policyValue = policyValues.get(0);

            if (isMatchAny) {
                ret = StringUtils.containsOnly(resource, WILDCARD_ASTERISK);
            } else {
                ret = optIgnoreCase ? StringUtils.equalsIgnoreCase(resource, policyValue)
                        : StringUtils.equals(resource, policyValue);
            }

            if (policyIsExcludes) {
                ret = !ret;
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("<== RangerAbstractResourceMatcher.isCompleteMatch(" + resource + ", " + evalContext + "): "
                    + ret);
        }

        return ret;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();

        toString(sb);

        return sb.toString();
    }

    public StringBuilder toString(StringBuilder sb) {
        sb.append("RangerAbstractResourceMatcher={");

        sb.append("resourceDef={");
        if (resourceDef != null) {
            resourceDef.toString(sb);
        }
        sb.append("} ");
        sb.append("policyResource={");
        if (policyResource != null) {
            policyResource.toString(sb);
        }
        sb.append("} ");
        sb.append("optIgnoreCase={").append(optIgnoreCase).append("} ");
        sb.append("optWildCard={").append(optWildCard).append("} ");

        sb.append("policyValues={");
        if (policyValues != null) {
            for (String value : policyValues) {
                sb.append(value).append(",");
            }
        }
        sb.append("} ");

        sb.append("policyIsExcludes={").append(policyIsExcludes).append("} ");
        sb.append("isMatchAny={").append(isMatchAny).append("} ");

        sb.append("options={");
        if (resourceDef != null && resourceDef.getMatcherOptions() != null) {
            for (Map.Entry<String, String> e : resourceDef.getMatcherOptions().entrySet()) {
                sb.append(e.getKey()).append("=").append(e.getValue()).append(';');
            }
        }
        sb.append("} ");

        sb.append("}");

        return sb;
    }

    boolean isAllValuesRequested(String resource) {
        boolean result = StringUtils.isEmpty(resource) || WILDCARD_ASTERISK.equals(resource);
        if (LOG.isDebugEnabled()) {
            LOG.debug("isAllValuesRequested(" + resource + "): " + result);
        }
        return result;
    }

    /**
     * The only case where excludes flag does NOT change the result is the following:
     * - Resource denotes all possible values (i.e. resource in (null, "", "*")
     * - where as policy does not allow all possible values (i.e. policy.values().contains("*")
     *
      */
    public boolean applyExcludes(boolean allValuesRequested, boolean resultWithoutExcludes) {
        if (!policyIsExcludes)
            return resultWithoutExcludes; // not an excludes policy!
        if (allValuesRequested && !isMatchAny)
            return resultWithoutExcludes; // one case where excludes has no effect
        return !resultWithoutExcludes; // all other cases flip it
    }

    ResourceMatcher getMatcher(String policyValue) {
        final int len = policyValue != null ? policyValue.length() : 0;

        if (len == 0) {
            return null;
        }

        final ResourceMatcher ret;

        int wildcardStartIdx = -1;
        int wildcardEndIdx = -1;
        boolean needWildcardMatch = false;

        // If optWildcard is true
        //   If ('?' found or non-contiguous '*'s found in policyValue)
        //      needWildcardMatch = true
        //     End
        //
        //     wildcardStartIdx is set to index of first '*' in policyValue or -1 if '*' is not found in policyValue, and
        //     wildcardEndIdx is set to index of last '*' in policyValue or -1 if '*' is not found in policyValue
        // Else
        //     needWildcardMatch is set to false
        // End
        if (optWildCard) {
            for (int i = 0; i < len; i++) {
                final char c = policyValue.charAt(i);

                if (c == '?') {
                    needWildcardMatch = true;
                    break;
                } else if (c == '*') {
                    if (wildcardEndIdx == -1 || wildcardEndIdx == (i - 1)) {
                        wildcardEndIdx = i;
                        if (wildcardStartIdx == -1) {
                            wildcardStartIdx = i;
                        }
                    } else {
                        needWildcardMatch = true;
                        break;
                    }
                }
            }
        }

        if (needWildcardMatch) {
            ret = optIgnoreCase ? new CaseInsensitiveWildcardMatcher(policyValue)
                    : new CaseSensitiveWildcardMatcher(policyValue);
        } else if (wildcardStartIdx == -1) {
            ret = optIgnoreCase ? new CaseInsensitiveStringMatcher(policyValue)
                    : new CaseSensitiveStringMatcher(policyValue);
        } else if (wildcardStartIdx == 0) {
            String matchStr = policyValue.substring(wildcardEndIdx + 1);
            ret = optIgnoreCase ? new CaseInsensitiveEndsWithMatcher(matchStr)
                    : new CaseSensitiveEndsWithMatcher(matchStr);
        } else {
            String matchStr = policyValue.substring(0, wildcardStartIdx);
            ret = optIgnoreCase ? new CaseInsensitiveStartsWithMatcher(matchStr)
                    : new CaseSensitiveStartsWithMatcher(matchStr);
        }

        if (optReplaceTokens) {
            ret.setDelimiters(startDelimiterChar, endDelimiterChar, escapeChar, tokenPrefix);
        }

        return ret;
    }
}

final class CaseSensitiveStringMatcher extends ResourceMatcher {
    CaseSensitiveStringMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return StringUtils.equals(resourceValue, getExpandedValue(evalContext));
    }

    int getPriority() {
        return 1 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class CaseInsensitiveStringMatcher extends ResourceMatcher {
    CaseInsensitiveStringMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return StringUtils.equalsIgnoreCase(resourceValue, getExpandedValue(evalContext));
    }

    int getPriority() {
        return 2 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class CaseSensitiveStartsWithMatcher extends ResourceMatcher {
    CaseSensitiveStartsWithMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return StringUtils.startsWith(resourceValue, getExpandedValue(evalContext));
    }

    int getPriority() {
        return 3 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class CaseInsensitiveStartsWithMatcher extends ResourceMatcher {
    CaseInsensitiveStartsWithMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return StringUtils.startsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
    }

    int getPriority() {
        return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class CaseSensitiveEndsWithMatcher extends ResourceMatcher {
    CaseSensitiveEndsWithMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return StringUtils.endsWith(resourceValue, getExpandedValue(evalContext));
    }

    int getPriority() {
        return 3 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class CaseInsensitiveEndsWithMatcher extends ResourceMatcher {
    CaseInsensitiveEndsWithMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return StringUtils.endsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
    }

    int getPriority() {
        return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class CaseSensitiveWildcardMatcher extends ResourceMatcher {
    CaseSensitiveWildcardMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), IOCase.SENSITIVE);
    }

    int getPriority() {
        return 5 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class CaseInsensitiveWildcardMatcher extends ResourceMatcher {
    CaseInsensitiveWildcardMatcher(String value) {
        super(value);
    }

    @Override
    boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
        return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), IOCase.INSENSITIVE);
    }

    int getPriority() {
        return 6 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
    }
}

final class ResourceMatcherWrapper {
    private final boolean needsDynamicEval;
    private final List<ResourceMatcher> resourceMatchers;

    ResourceMatcherWrapper() {
        this(false, null);
    }

    ResourceMatcherWrapper(boolean needsDynamicEval, List<ResourceMatcher> resourceMatchers) {
        this.needsDynamicEval = needsDynamicEval;
        this.resourceMatchers = resourceMatchers;
    }

    boolean getNeedsDynamicEval() {
        return needsDynamicEval;
    }

    List<ResourceMatcher> getResourceMatchers() {
        return resourceMatchers;
    }
}