RANGER-4328: updated resource matcher handling of SELF_OR_PREFIX scope
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java
index cb8d46a..5eee8d1 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java
@@ -30,15 +30,18 @@
import org.apache.commons.lang.StringUtils;
import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType;
import org.apache.ranger.plugin.util.ServiceDefUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType.*;
public abstract class RangerAbstractResourceMatcher implements RangerResourceMatcher {
private static final Logger LOG = LoggerFactory.getLogger(RangerAbstractResourceMatcher.class);
- public final static String WILDCARD_ASTERISK = "*";
+ public final static String WILDCARD_ASTERISK = "*";
+ public final static String WILDCARD_QUESTION_MARK = "?";
public final static String OPTION_IGNORE_CASE = "ignoreCase";
public final static String OPTION_QUOTED_CASE_SENSITIVE = "quotedCaseSensitive";
@@ -324,6 +327,12 @@ public boolean applyExcludes(boolean allValuesRequested, boolean resultWithoutEx
return !resultWithoutExcludes; // all other cases flip it
}
+ public ResourceElementMatchType applyExcludes(boolean allValuesRequested, ResourceElementMatchType resultWithoutExcludes) {
+ if (!policyIsExcludes) return resultWithoutExcludes; // not an excludes policy!
+ if (allValuesRequested && !isMatchAny) return resultWithoutExcludes; // one case where excludes has no effect
+ return resultWithoutExcludes == NONE ? SELF : NONE; // all other cases flip it
+ }
+
ResourceMatcher getMatcher(String policyValue) {
final int len = policyValue != null ? policyValue.length() : 0;
@@ -391,18 +400,8 @@ ResourceMatcher getMatcher(String policyValue) {
}
abstract class AbstractStringResourceMatcher extends ResourceMatcher {
- private final boolean isCaseSensitive;
-
- protected AbstractStringResourceMatcher(String value, Map<String, String> options, boolean isCaseSensitive) {
+ protected AbstractStringResourceMatcher(String value, Map<String, String> options) {
super(value, options);
-
- this.isCaseSensitive = isCaseSensitive;
- }
-
- @Override
- public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
- return isCaseSensitive ? StringUtils.startsWith(getExpandedValue(evalContext), resourceValue)
- : StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
}
@Override
@@ -413,23 +412,35 @@ public boolean isChildMatch(String resourceValue, Map<String, Object> evalContex
final class CaseSensitiveStringMatcher extends AbstractStringResourceMatcher {
CaseSensitiveStringMatcher(String value, Map<String, String> options) {
- super(value, options, true);
+ super(value, options);
}
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return StringUtils.equals(resourceValue, getExpandedValue(evalContext));
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue);
+ }
+
int getPriority() { return 1 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);}
}
final class CaseInsensitiveStringMatcher extends AbstractStringResourceMatcher {
- CaseInsensitiveStringMatcher(String value, Map<String, String> options) { super(value, options, false); }
+ CaseInsensitiveStringMatcher(String value, Map<String, String> options) { super(value, options); }
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return StringUtils.equalsIgnoreCase(resourceValue, getExpandedValue(evalContext));
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
+ }
+
int getPriority() {return 2 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
@@ -437,7 +448,7 @@ final class QuotedCaseSensitiveStringMatcher extends AbstractStringResourceMatch
private final String quoteChars;
QuotedCaseSensitiveStringMatcher(String value, Map<String, String> options, String quoteChars) {
- super(value, options, true);
+ super(value, options);
this.quoteChars = quoteChars;
}
@@ -451,28 +462,49 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
}
}
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (startsWithAnyChar(resourceValue, quoteChars)) {
+ return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue);
+ } else {
+ return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
+ }
+ }
+
int getPriority() {return 2 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
final class CaseSensitiveStartsWithMatcher extends AbstractStringResourceMatcher {
CaseSensitiveStartsWithMatcher(String value, Map<String, String> options) {
- super(value, options, true);
+ super(value, options);
}
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return StringUtils.startsWith(resourceValue, getExpandedValue(evalContext));
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue);
+ }
+
int getPriority() { return 3 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);}
}
final class CaseInsensitiveStartsWithMatcher extends AbstractStringResourceMatcher {
- CaseInsensitiveStartsWithMatcher(String value, Map<String, String> options) { super(value, options, false); }
+ CaseInsensitiveStartsWithMatcher(String value, Map<String, String> options) { super(value, options); }
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return StringUtils.startsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
+ }
+
int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
@@ -480,7 +512,7 @@ final class QuotedCaseSensitiveStartsWithMatcher extends AbstractStringResourceM
private final String quoteChars;
QuotedCaseSensitiveStartsWithMatcher(String value, Map<String, String> options, String quoteChars) {
- super(value, options, true);
+ super(value, options);
this.quoteChars = quoteChars;
}
@@ -494,30 +526,51 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
}
}
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (startsWithAnyChar(resourceValue, quoteChars)) {
+ return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue);
+ } else {
+ return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
+ }
+ }
+
int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
final class CaseSensitiveEndsWithMatcher extends AbstractStringResourceMatcher {
CaseSensitiveEndsWithMatcher(String value, Map<String, String> options) {
- super(value, options, true);
+ super(value, options);
}
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return StringUtils.endsWith(resourceValue, getExpandedValue(evalContext));
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return true; // isPrefixMatch() is always true for endsWith
+ }
+
int getPriority() { return 3 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
final class CaseInsensitiveEndsWithMatcher extends AbstractStringResourceMatcher {
CaseInsensitiveEndsWithMatcher(String value, Map<String, String> options) {
- super(value, options, false);
+ super(value, options);
}
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return StringUtils.endsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return true; // isPrefixMatch() is always true for endsWith
+ }
+
int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
@@ -525,7 +578,7 @@ final class QuotedCaseSensitiveEndsWithMatcher extends AbstractStringResourceMat
private final String quoteChars;
QuotedCaseSensitiveEndsWithMatcher(String value, Map<String, String> options, String quoteChars) {
- super(value, options, true);
+ super(value, options);
this.quoteChars = quoteChars;
}
@@ -539,31 +592,48 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
}
}
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return true; // isPrefixMatch() is always true for endsWith
+ }
+
int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
final class CaseSensitiveWildcardMatcher extends AbstractStringResourceMatcher {
CaseSensitiveWildcardMatcher(String value, Map<String, String> options) {
- super(value, options, true);
+ super(value, options);
}
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), IOCase.SENSITIVE);
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.SENSITIVE);
+ }
+
int getPriority() { return 5 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
final class CaseInsensitiveWildcardMatcher extends AbstractStringResourceMatcher {
CaseInsensitiveWildcardMatcher(String value, Map<String, String> options) {
- super(value, options, false);
+ super(value, options);
}
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), IOCase.INSENSITIVE);
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.INSENSITIVE);
+ }
+
int getPriority() {return 6 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
@@ -571,7 +641,7 @@ final class QuotedCaseSensitiveWildcardMatcher extends AbstractStringResourceMat
private final String quoteChars;
QuotedCaseSensitiveWildcardMatcher(String value, Map<String, String> options, String quoteChars) {
- super(value, options, true);
+ super(value, options);
this.quoteChars = quoteChars;
}
@@ -583,6 +653,13 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), caseSensitivity);
}
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ IOCase caseSensitivity = startsWithAnyChar(resourceValue, quoteChars) ? IOCase.SENSITIVE : IOCase.INSENSITIVE;
+
+ return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), caseSensitivity);
+ }
+
int getPriority() {return 6 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java
index 702cb27..977fd49 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java
@@ -21,12 +21,15 @@
import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Map;
+import static org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType.NONE;
+
public class RangerDefaultResourceMatcher extends RangerAbstractResourceMatcher {
private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultResourceMatcher.class);
@@ -37,43 +40,8 @@ public boolean isMatch(Object resource, ResourceElementMatchingScope matchingSco
LOG.debug("==> RangerDefaultResourceMatcher.isMatch(" + resource + ", " + evalContext + ")");
}
- boolean ret = false;
- boolean allValuesRequested = isAllValuesRequested(resource);
- boolean isPrefixMatch = matchingScope == ResourceElementMatchingScope.SELF_OR_PREFIX;
-
- if (isMatchAny || (allValuesRequested && !isPrefixMatch)) {
- ret = isMatchAny;
- } else {
- if (resource instanceof String) {
- String strValue = (String) resource;
-
- for (ResourceMatcher resourceMatcher : resourceMatchers.getResourceMatchers()) {
- ret = resourceMatcher.isMatch(strValue, matchingScope, evalContext);
-
- if (ret) {
- break;
- }
- }
- } else if (resource instanceof Collection) {
- @SuppressWarnings("unchecked")
- Collection<String> resourceValues = (Collection<String>) resource;
-
- for (ResourceMatcher resourceMatcher : resourceMatchers.getResourceMatchers()) {
- for (String resourceValue : resourceValues) {
- ret = resourceMatcher.isMatch(resourceValue, matchingScope, evalContext);
-
- if (ret) {
- break;
- }
- }
- if (ret) {
- break;
- }
- }
- }
- }
-
- ret = applyExcludes(allValuesRequested, ret);
+ ResourceElementMatchType matchType = getMatchType(resource, matchingScope, evalContext);
+ boolean ret = ResourceMatcher.isMatch(matchType, matchingScope);
if (ret == false) {
if(LOG.isDebugEnabled()) {
@@ -96,6 +64,66 @@ public boolean isMatch(Object resource, ResourceElementMatchingScope matchingSco
return ret;
}
+ @Override
+ public ResourceElementMatchType getMatchType(Object resource, ResourceElementMatchingScope matchingScope, Map<String, Object> evalContext) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerDefaultResourceMatcher.getMatchType(" + resource + ", " + evalContext + ")");
+ }
+
+ ResourceElementMatchType ret = NONE;
+ boolean allValuesRequested = isAllValuesRequested(resource);
+ boolean isPrefixMatch = matchingScope == ResourceElementMatchingScope.SELF_OR_PREFIX;
+
+ if (isMatchAny || (allValuesRequested && !isPrefixMatch)) {
+ ret = isMatchAny ? ResourceElementMatchType.SELF : NONE;
+ } else {
+ if (resource instanceof String) {
+ String strValue = (String) resource;
+
+ for (ResourceMatcher resourceMatcher : resourceMatchers.getResourceMatchers()) {
+ ResourceElementMatchType matchType = resourceMatcher.getMatchType(strValue, matchingScope, evalContext);
+
+ if (matchType != NONE) {
+ ret = matchType;
+ }
+
+ if (ret == ResourceElementMatchType.SELF) {
+ break;
+ }
+ }
+ } else if (resource instanceof Collection) {
+ @SuppressWarnings("unchecked")
+ Collection<String> resourceValues = (Collection<String>) resource;
+
+ for (ResourceMatcher resourceMatcher : resourceMatchers.getResourceMatchers()) {
+ for (String resourceValue : resourceValues) {
+ ResourceElementMatchType matchType = resourceMatcher.getMatchType(resourceValue, matchingScope, evalContext);
+
+ if (matchType != NONE) {
+ ret = matchType;
+ }
+
+ if (ret == ResourceElementMatchType.SELF) {
+ break;
+ }
+ }
+
+ if (ret == ResourceElementMatchType.SELF) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = applyExcludes(allValuesRequested, ret);
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerDefaultResourceMatcher.getMatchType(" + resource + ", " + evalContext + "): " + ret);
+ }
+
+ return ret;
+ }
+
public StringBuilder toString(StringBuilder sb) {
sb.append("RangerDefaultResourceMatcher={");
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java
index 3c1523c..d376e90 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java
@@ -130,7 +130,7 @@ ResourceMatcher getMatcher(String policyValue) {
if (isWildcardPresent) {
ret = new RecursiveWildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, RangerPathResourceMatcher::isRecursiveWildCardMatch, optIgnoreCase ? 8 : 7);
} else {
- ret = new RecursivePathResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith, optIgnoreCase ? 8 : 7);
+ ret = new RecursivePathResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, optIgnoreCase ? 8 : 7);
}
if (optReplaceTokens) {
@@ -263,15 +263,15 @@ private ResourceMatcher getPathMatcher(String policyValue) {
if (needWildcardMatch) { // test?, test*a*, test*a*b, *test*a
ret = new WildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6);
} else if (wildcardStartIdx == -1) { // test, testa, testab
- ret = new PathResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, optIgnoreCase ? 2 : 1, !optIgnoreCase);
+ ret = new PathResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, !optIgnoreCase, optIgnoreCase ? 2 : 1);
} else if (wildcardStartIdx == 0) { // *test, **test, *testa, *testab
String matchStr = policyValue.substring(wildcardEndIdx + 1);
- ret = new PathResourceMatcher(matchStr, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::endsWithIgnoreCase : StringUtils::endsWith, optIgnoreCase ? 4 : 3, !optIgnoreCase);
+ ret = new PathEndsWithResourceMatcher(matchStr, getOptions(), pathSeparatorChar, !optIgnoreCase, optIgnoreCase ? 4 : 3);
} else if (wildcardEndIdx != (len - 1)) { // test*a, test*ab
ret = new WildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6);
} else { // test*, test**, testa*, testab*
String matchStr = policyValue.substring(0, wildcardStartIdx);
- ret = new PathResourceMatcher(matchStr, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith, optIgnoreCase ? 4 : 3, !optIgnoreCase);
+ ret = new PathStartsWithResourceMatcher(matchStr, getOptions(), pathSeparatorChar, !optIgnoreCase, optIgnoreCase ? 4 : 3);
}
if (optReplaceTokens) {
@@ -298,7 +298,7 @@ static abstract class AbstractPathResourceMatcher extends ResourceMatcher {
final int priority;
final boolean isCaseSensitive;
- AbstractPathResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, int priority, boolean isCaseSensitive) {
+ AbstractPathResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean isCaseSensitive, int priority) {
super(value, options);
this.pathSeparatorChar = pathSeparatorChar;
@@ -308,19 +308,13 @@ static abstract class AbstractPathResourceMatcher extends ResourceMatcher {
int getPriority() {
return priority + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);
}
-
- @Override
- public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
- return isCaseSensitive ? StringUtils.startsWith(getExpandedValue(evalContext), resourceValue)
- : StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
- }
}
static class PathResourceMatcher extends AbstractPathResourceMatcher {
final BiFunction<String, String, Boolean> function;
- PathResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, BiFunction<String, String, Boolean> function, int priority, boolean isCaseSensitive) {
- super(value, options, pathSeparatorChar, priority, isCaseSensitive);
+ PathResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, BiFunction<String, String, Boolean> function, boolean isCaseSensitive, int priority) {
+ super(value, options, pathSeparatorChar, isCaseSensitive, priority);
this.function = function;
}
@@ -328,21 +322,42 @@ static class PathResourceMatcher extends AbstractPathResourceMatcher {
@Override
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
if (LOG.isDebugEnabled()) {
- LOG.debug("==> PathResourceMatcher.isMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")");
+ LOG.debug("==> PathResourceMatcher.isMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
}
String expandedValue = getExpandedValue(evalContext);
boolean ret = function.apply(resourceValue, expandedValue);
if (LOG.isDebugEnabled()) {
- LOG.debug("<== PathResourceMatcher.isMatch(resourceValue=" + resourceValue + ", expandedValue=" + expandedValue + ") : result:[" + ret + "]");
+ LOG.debug("<== PathResourceMatcher.isMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret );
}
return ret;
}
@Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathResourceMatcher.isPrefixMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
+ boolean ret = isCaseSensitive ? StringUtils.startsWith(getExpandedValue(evalContext), resourceValue)
+ : StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathResourceMatcher.isPrefixMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret );
+ }
+
+ return ret;
+
+ }
+
+ @Override
public boolean isChildMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathResourceMatcher.isChildMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
boolean ret = false;
String expandedValue = getExpandedValue(evalContext);
int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar);
@@ -357,9 +372,143 @@ public boolean isChildMatch(String resourceValue, Map<String, Object> evalContex
ret = function.apply(resourceValue, shorterExpandedValue);
}
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathResourceMatcher.isChildMatch(resourceValue={}, lastLevelSeparatorIndex={}): ret={}", resourceValue, lastLevelSeparatorIndex, ret );
+ }
+
+ return ret;
+ }
+ }
+
+ static class PathStartsWithResourceMatcher extends AbstractPathResourceMatcher {
+ PathStartsWithResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean isCaseSensitive, int priority) {
+ super(value, options, pathSeparatorChar, isCaseSensitive, priority);
+ }
+
+ @Override
+ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathStartsWithResourceMatcher.isMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
+ boolean ret = isCaseSensitive ? StringUtils.startsWith(resourceValue, getExpandedValue(evalContext))
+ : StringUtils.startsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathStartsWithResourceMatcher.isMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret );
+ }
+
return ret;
}
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathStartsWithResourceMatcher.isPrefixMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
+ boolean ret = isCaseSensitive ? StringUtils.startsWith(getExpandedValue(evalContext), resourceValue)
+ : StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathStartsWithResourceMatcher.isPrefixMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret );
+ }
+
+ return ret;
+ }
+
+ @Override
+ public boolean isChildMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathStartsWithResourceMatcher.isChildMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
+ boolean ret = false;
+ String expandedValue = getExpandedValue(evalContext);
+ int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar);
+
+ if (lastLevelSeparatorIndex != -1) {
+ String shorterExpandedValue = expandedValue.substring(0, lastLevelSeparatorIndex);
+
+ if (resourceValue.charAt(resourceValue.length()-1) == pathSeparatorChar) {
+ resourceValue = resourceValue.substring(0, resourceValue.length()-1);
+ }
+
+ ret = isCaseSensitive ? StringUtils.startsWith(resourceValue, shorterExpandedValue)
+ : StringUtils.startsWithIgnoreCase(resourceValue, shorterExpandedValue);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathStartsWithResourceMatcher.isChildMatch(resourceValue={}, lastLevelSeparatorIndex={}): ret={}", resourceValue, lastLevelSeparatorIndex, ret );
+ }
+
+ return ret;
+ }
+ }
+
+ static class PathEndsWithResourceMatcher extends AbstractPathResourceMatcher {
+ PathEndsWithResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean isCaseSensitive, int priority) {
+ super(value, options, pathSeparatorChar, isCaseSensitive, priority);
+ }
+
+ @Override
+ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathEndsWithResourceMatcher.isMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
+ boolean ret = isCaseSensitive ? StringUtils.endsWith(resourceValue, getExpandedValue(evalContext))
+ : StringUtils.endsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathEndsWithResourceMatcher.isMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret );
+ }
+
+ return ret;
+ }
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathEndsWithResourceMatcher.isPrefixMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
+ boolean ret = true; // isPrefixMatch() is always true for endsWith
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathEndsWithResourceMatcher.isPrefixMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret );
+ }
+
+ return ret;
+ }
+
+ @Override
+ public boolean isChildMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> PathEndsWithResourceMatcher.isChildMatch(resourceValue={}, evalContext={})", resourceValue, evalContext);
+ }
+
+ boolean ret = false;
+ String expandedValue = getExpandedValue(evalContext);
+ int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar);
+
+ if (lastLevelSeparatorIndex != -1) {
+ String shorterExpandedValue = expandedValue.substring(0, lastLevelSeparatorIndex);
+
+ if (resourceValue.charAt(resourceValue.length()-1) == pathSeparatorChar) {
+ resourceValue = resourceValue.substring(0, resourceValue.length()-1);
+ }
+
+ ret = isCaseSensitive ? StringUtils.endsWith(resourceValue, shorterExpandedValue)
+ : StringUtils.endsWithIgnoreCase(resourceValue, shorterExpandedValue);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== PathEndsWithResourceMatcher.isChildMatch(resourceValue={}, lastLevelSeparatorIndex={}): ret={}", resourceValue, lastLevelSeparatorIndex, ret );
+ }
+
+ return ret;
+ }
}
static class WildcardResourceMatcher extends AbstractPathResourceMatcher {
@@ -367,14 +516,14 @@ static class WildcardResourceMatcher extends AbstractPathResourceMatcher {
final IOCase ioCase;
WildcardResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean optIgnoreCase, TriFunction<String, String, IOCase, Boolean> function, int priority) {
- super(value, options, pathSeparatorChar, priority, !optIgnoreCase);
+ super(value, options, pathSeparatorChar, !optIgnoreCase, priority);
this.function = function;
this.ioCase = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE;
}
@Override
- boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
+ public boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> WildcardResourceMatcher.isMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")");
}
@@ -389,6 +538,20 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
}
@Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> WildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")");
+ }
+
+ boolean ret = ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), ioCase);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== WildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", expandedValue=" + getExpandedValue(evalContext) + ") : result:[" + ret + "]");
+ }
+ return ret;
+ }
+
+ @Override
public boolean isChildMatch(String resourceValue, Map<String, Object> evalContext) {
boolean ret = false;
String expandedValue = getExpandedValue(evalContext);
@@ -414,7 +577,7 @@ static class RecursiveWildcardResourceMatcher extends AbstractPathResourceMatche
String[] wildcardPathElements;
RecursiveWildcardResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean optIgnoreCase, QuintFunction<String, String, Character, IOCase, Boolean, String[]> function, int priority) {
- super(value, options, pathSeparatorChar, priority, !optIgnoreCase);
+ super(value, options, pathSeparatorChar, !optIgnoreCase, priority);
this.function = function;
this.ioCase = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE;
@@ -445,6 +608,20 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
}
@Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")");
+ }
+
+ boolean ret = ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), ioCase);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", expandedValue=" + getExpandedValue(evalContext) + ") : result:[" + ret + "]");
+ }
+ return ret;
+ }
+
+ @Override
public boolean isChildMatch(String resourceValue, Map<String, Object> evalContext) {
boolean ret = false;
String expandedValue = getExpandedValue(evalContext);
@@ -470,14 +647,16 @@ static class RecursivePathResourceMatcher extends AbstractPathResourceMatcher {
String valueWithoutSeparator;
String valueWithSeparator;
+ final IOCase ioCase;
final BiFunction<String, String, Boolean> primaryFunction;
final BiFunction<String, String, Boolean> fallbackFunction;
- RecursivePathResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, BiFunction<String, String, Boolean> primaryFunction, BiFunction<String, String, Boolean> fallbackFunction, int priority) {
- super(value, options, pathSeparatorChar, priority, true);
+ RecursivePathResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean optIgnoreCase, int priority) {
+ super(value, options, pathSeparatorChar, true, priority);
- this.primaryFunction = primaryFunction;
- this.fallbackFunction = fallbackFunction;
+ this.ioCase = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE;
+ this.primaryFunction = optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals;
+ this.fallbackFunction = optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith;
}
String getStringToCompare(String policyValue) {
@@ -521,6 +700,20 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
}
@Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")");
+ }
+
+ boolean ret = ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), ioCase);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", expandedValue=" + getExpandedValue(evalContext) + ") : result:[" + ret + "]");
+ }
+ return ret;
+ }
+
+ @Override
public boolean isChildMatch(String resourceValue, Map<String, Object> evalContext) {
boolean ret = false;
final String noSeparator;
@@ -534,7 +727,7 @@ public boolean isChildMatch(String resourceValue, Map<String, Object> evalContex
}
noSeparator = valueWithoutSeparator;
}
- final int lastLevelSeparatorIndex = noSeparator.lastIndexOf(pathSeparatorChar);
+ final int lastLevelSeparatorIndex = noSeparator != null ? noSeparator.lastIndexOf(pathSeparatorChar) : -1;
if (lastLevelSeparatorIndex != -1) {
final String shorterExpandedValue = noSeparator.substring(0, lastLevelSeparatorIndex);
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java
index c4ce30d..ec22e01 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java
@@ -22,6 +22,7 @@
import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType;
import java.util.Map;
@@ -34,6 +35,8 @@ public interface RangerResourceMatcher {
boolean isMatchAny();
+ ResourceElementMatchType getMatchType(Object resource, ResourceElementMatchingScope matchingScope, Map<String, Object> evalContext);
+
boolean isMatch(Object resource, ResourceElementMatchingScope matchingScope, Map<String, Object> evalContext);
boolean isCompleteMatch(String resource, Map<String, Object> evalContext);
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java
index ce73b30..0575636 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java
@@ -237,7 +237,7 @@ static String getPathWithOutScheme(String url) {
final class CaseSensitiveURLRecursiveWildcardMatcher extends AbstractStringResourceMatcher {
private final char levelSeparatorChar;
CaseSensitiveURLRecursiveWildcardMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
- super(value, options, true);
+ super(value, options);
this.levelSeparatorChar = levelSeparatorChar;
}
@@ -245,13 +245,19 @@ final class CaseSensitiveURLRecursiveWildcardMatcher extends AbstractStringResou
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return RangerURLResourceMatcher.isRecursiveWildCardMatch(resourceValue, getExpandedValue(evalContext), levelSeparatorChar, IOCase.SENSITIVE);
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.SENSITIVE);
+ }
+
int getPriority() { return 7 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);}
}
final class CaseInsensitiveURLRecursiveWildcardMatcher extends AbstractStringResourceMatcher {
private final char levelSeparatorChar;
CaseInsensitiveURLRecursiveWildcardMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
- super(value, options, false);
+ super(value, options);
this.levelSeparatorChar = levelSeparatorChar;
}
@@ -259,6 +265,12 @@ final class CaseInsensitiveURLRecursiveWildcardMatcher extends AbstractStringRes
boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return RangerURLResourceMatcher.isRecursiveWildCardMatch(resourceValue, getExpandedValue(evalContext), levelSeparatorChar, IOCase.INSENSITIVE);
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.INSENSITIVE);
+ }
+
int getPriority() { return 8 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);}
}
@@ -268,8 +280,8 @@ abstract class RecursiveMatcher extends AbstractStringResourceMatcher {
String valueWithoutSeparator;
String valueWithSeparator;
- RecursiveMatcher(String value, Map<String, String> options, char levelSeparatorChar, boolean isCaseSensitive) {
- super(value, options, isCaseSensitive);
+ RecursiveMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
+ super(value, options);
this.levelSeparatorChar = levelSeparatorChar;
}
@@ -284,7 +296,7 @@ String getStringToCompare(String policyValue) {
final class CaseSensitiveURLRecursiveMatcher extends RecursiveMatcher {
CaseSensitiveURLRecursiveMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
- super(value, options, levelSeparatorChar, true);
+ super(value, options, levelSeparatorChar);
}
@Override
@@ -311,12 +323,18 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return ret;
}
+
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue);
+ }
+
int getPriority() { return 7 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);}
}
final class CaseInsensitiveURLRecursiveMatcher extends RecursiveMatcher {
CaseInsensitiveURLRecursiveMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
- super(value, options, levelSeparatorChar, false);
+ super(value, options, levelSeparatorChar);
}
@Override
@@ -344,5 +362,10 @@ boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
return ret;
}
+ @Override
+ public boolean isPrefixMatch(String resourceValue, Map<String, Object> evalContext) {
+ return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue);
+ }
+
int getPriority() { return 8 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);}
}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java
index ad0942f..77245ea 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java
@@ -19,6 +19,7 @@
package org.apache.ranger.plugin.resourcematcher;
+import org.apache.commons.io.IOCase;
import org.apache.commons.lang.StringUtils;
import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType;
@@ -30,8 +31,12 @@
import org.slf4j.LoggerFactory;
import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
import java.util.Map;
+import java.util.Stack;
abstract class ResourceMatcher {
private static final Logger LOG = LoggerFactory.getLogger(ResourceMatcher.class);
@@ -60,19 +65,8 @@ abstract class ResourceMatcher {
final boolean isMatch(String resourceValue, ResourceElementMatchingScope matchingScope, Map<String, Object> evalContext) {
final ResourceElementMatchType matchType = getMatchType(resourceValue, matchingScope, evalContext);
- final boolean ret;
- if (matchType == ResourceElementMatchType.SELF) {
- ret = true;
- } else if (matchType == ResourceElementMatchType.PREFIX) {
- ret = matchingScope == ResourceElementMatchingScope.SELF_OR_PREFIX;
- } else if (matchType == ResourceElementMatchType.CHILD) {
- ret = matchingScope == ResourceElementMatchingScope.SELF_OR_CHILD;
- } else {
- ret = false;
- }
-
- return ret;
+ return isMatch(matchType, matchingScope);
}
final ResourceElementMatchType getMatchType(String resourceValue, ResourceElementMatchingScope matchingScope, Map<String, Object> evalContext) {
@@ -152,6 +146,153 @@ public static boolean startsWithAnyChar(String value, String startChars) {
return ret;
}
+ public static boolean isMatch(ResourceElementMatchType matchType, ResourceElementMatchingScope matchingScope) {
+ final boolean ret;
+
+ switch (matchType) {
+ case SELF:
+ ret = true;
+ break;
+
+ case CHILD:
+ ret = matchingScope == ResourceElementMatchingScope.SELF_OR_CHILD;
+ break;
+
+ case PREFIX:
+ ret = matchingScope == ResourceElementMatchingScope.SELF_OR_PREFIX;
+ break;
+
+ default:
+ LOG.error("invalid ResourceElementMatchType: {}}", matchType);
+
+ ret = false;
+ }
+
+ return ret;
+ }
+
+ // modified version of FilenameUtils.wildcardMatch(), to check if value is a prefix match for wildcardMatcher
+ public static boolean wildcardPrefixMatch(String value, String wildcardMatcher, IOCase caseSensitivity) {
+ if (value == null && wildcardMatcher == null) {
+ return true;
+ } else if (value == null || wildcardMatcher == null) {
+ return false;
+ }
+
+ if (caseSensitivity == null) {
+ caseSensitivity = IOCase.SENSITIVE;
+ }
+
+ List<String> wcsTokens = splitOnTokens(wildcardMatcher);
+ boolean anyChars = false;
+ int textIdx = 0;
+ int wcsIdx = 0;
+ Stack<int[]> backtrack = new Stack<>();
+
+ do {
+ if (backtrack.size() > 0) {
+ int[] array = backtrack.pop();
+
+ wcsIdx = array[0];
+ textIdx = array[1];
+ anyChars = true;
+ }
+
+ for(; wcsIdx < wcsTokens.size(); ++wcsIdx) {
+ String wcsToken = wcsTokens.get(wcsIdx);
+
+ if (wcsToken.equals("?")) {
+ ++textIdx;
+
+ if (textIdx > value.length()) {
+ break;
+ }
+
+ anyChars = false;
+ } else if (wcsToken.equals("*")) {
+ anyChars = true;
+
+ if (wcsIdx == wcsTokens.size() - 1) {
+ textIdx = value.length();
+ }
+ } else {
+ // changes from FilenameUtils.wildcardMatch(): added following 3 lines to check if value is a prefix match for wildcardMatcher
+ if (wcsToken.length() > (value.length() - textIdx)) {
+ wcsToken = wcsToken.substring(0, value.length() - textIdx);
+ }
+
+ if (anyChars) {
+ textIdx = caseSensitivity.checkIndexOf(value, textIdx, wcsToken);
+
+ if (textIdx == -1) {
+ break;
+ }
+
+ int repeat = caseSensitivity.checkIndexOf(value, textIdx + 1, wcsToken);
+
+ if (repeat >= 0) {
+ backtrack.push(new int[]{wcsIdx, repeat});
+ }
+ } else if (!caseSensitivity.checkRegionMatches(value, textIdx, wcsToken)) {
+ break;
+ }
+
+ textIdx += wcsToken.length();
+
+ anyChars = false;
+ }
+ }
+
+ // changes from FilenameUtils.wildcardMatch(): replaced the condition in 'if' below to check if value is a prefix match for wildcardMatcher
+ // original if: if (wcsIdx == wcsTokens.size() && textIdx == value.length())
+ if (wcsIdx == wcsTokens.size() || textIdx == value.length()) {
+ return true;
+ }
+ } while (backtrack.size() > 0);
+
+ return anyChars;
+ }
+
+ static List<String> splitOnTokens(String text) {
+ if (text.indexOf(63) == -1 && text.indexOf(42) == -1) {
+ return Collections.singletonList(text);
+ } else {
+ char[] array = text.toCharArray();
+ List<String> list = new ArrayList<>(2);
+ StringBuilder buffer = new StringBuilder();
+ char prevChar = 0;
+ char[] arr$ = array;
+ int len$ = array.length;
+
+ for(int i$ = 0; i$ < len$; ++i$) {
+ char ch = arr$[i$];
+
+ if (ch != '?' && ch != '*') {
+ buffer.append(ch);
+ } else {
+ if (buffer.length() != 0) {
+ list.add(buffer.toString());
+ buffer.setLength(0);
+ }
+
+ if (ch == '?') {
+ list.add("?");
+ } else if (prevChar != '*') {
+ list.add("*");
+ }
+ }
+
+ prevChar = ch;
+ }
+
+ if (buffer.length() != 0) {
+ list.add(buffer.toString());
+ }
+
+ return list;
+ }
+ }
+
public static class PriorityComparator implements Comparator<ResourceMatcher>, Serializable {
@Override
public int compare(ResourceMatcher me, ResourceMatcher other) {
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java
index 767795e..b686418 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java
@@ -19,7 +19,9 @@
package org.apache.ranger.plugin.resourcematcher;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType;
import org.junit.Test;
import java.util.Map;
@@ -47,5 +49,12 @@ public boolean isMatch(Object resource, ResourceElementMatchingScope matchingSco
fail("This method is not expected to be used by test!");
return false;
}
+
+ @Override
+ public ResourceElementMatchType getMatchType(Object resource, ResourceElementMatchingScope matchingScope, Map<String, Object> evalContext) {
+ fail("This method is not expected to be used by test!");
+ return RangerAccessRequest.ResourceElementMatchType.NONE;
+ }
+
}
}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java
index e00bc83..8a297bd 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java
@@ -23,6 +23,7 @@
import org.apache.ranger.plugin.model.RangerPolicy;
import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType;
import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
import org.junit.Test;
@@ -30,43 +31,132 @@
import java.util.Map;
import static org.junit.Assert.*;
+import static org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType.*;
public class RangerDefaultResourceMatcherTest {
Object[][] data = {
- // { resource, policy, excludes, result
- { "*", "*", false, true, "user" }, // resource is all values
- { "*", "*", true, false, "user" },
- { "*", "a*", false, false, "user" }, // but, policy is not match any
- { "*", "a*", true, false, "user" }, // ==> compare with above: exclude flag has no effect here
- { "a*", "a", false, false, "user" }, // resource has regex marker!
- { "a*", "a", true, true, "user" },
- { "a", "a", false, true, "user" }, // exact match
- { "a", "a", true, false, "user" },
- { "a1", "a*", false, true, "user" }, // trivial regex match
- { "a1", "a*", true, false, "user" },
+ // { resource, policy, excludes, matchType, result, user }
+ { "*", "*", false, SELF, true, "user" }, // resource is all values
+ { "*", "*", true, NONE, false, "user" },
+ { "*", "a*", false, NONE, false, "user" }, // but, policy is not match any
+ { "*", "a*", true, NONE, false, "user" }, // ==> compare with above: exclude flag has no effect here
+ { "a*", "a", false, NONE, false, "user" }, // resource has regex marker!
+ { "a*", "a", true, SELF, true, "user" },
+ { "a", "a", false, SELF, true, "user" }, // exact match
+ { "a", "a", true, NONE, false, "user" },
+ { "a1", "a*", false, SELF, true, "user" }, // trivial regex match
+ { "a1", "a*", true, NONE, false, "user" },
+
+ // matchScope=SELF, excludes=false
+ { "*", "*", false, SELF, true, "user" }, // resource is all values
+ { "a*", "*", false, SELF, true, "user" }, // resource has regex, policy matches
+ { "a*", "a", false, NONE, false, "user" }, // resource has regex, policy does not match
+ { "*", "a*", false, NONE, false, "user" }, // resource has regex, policy does not match
+ { "a*", "a*", false, SELF, true, "user" }, // resource has regex, policy matches
+ { "a?", "a*", false, SELF, true, "user" }, // resource has regex, policy matches
+ { "a*b", "a*", false, SELF, true, "user" }, // resource has regex, policy matches
+ { "a?b", "a*", false, SELF, true, "user" }, // resource has regex, policy matches
+ { "a*b", "a*b", false, SELF, true, "user" }, // resource has regex, policy matches
+ { "a?b", "a?b", false, SELF, true, "user" }, // resource has regex, policy matches
+ { "a1b", "a1b", false, SELF, true, "user" }, // exact match
+ { "a1b", "a*", false, SELF, true, "user" }, // regex match - suffix
+ { "a1b", "*b", false, SELF, true, "user" }, // regex match - prefix
+ { "a1b", "*1*", false, SELF, true, "user" }, // regex match
+ { "a1b", "a?b", false, SELF, true, "user" }, // regex match - single char
+ { "a", "abc", false, NONE, false, "user" }, // policy has more than resource
+ { "ab", "abc", false, NONE, false, "user" }, // policy has more than resource
+ { "ab", "*c", false, NONE, false, "user" }, // policy has more than resource
+ { "*b", "a*bc", false, NONE, false, "user" }, // policy has more than resource
+ { "a*b", "a*bc", false, NONE, false, "user" }, // policy has more than resource
+
+ // matchScope=SELF, excludes=true
+ { "*", "*", true, NONE, false, "user" },
+ { "a*", "*", true, NONE, false, "user" },
+ { "a*", "a", true, SELF, true, "user" },
+ { "*", "a*", true, NONE, false, "user" }, // ==> compare with above: exclude flag has no effect here
+ { "a*", "a*", true, NONE, false, "user" },
+ { "a?", "a*", true, NONE, false, "user" },
+ { "a*b", "a*", true, NONE, false, "user" },
+ { "a?b", "a*", true, NONE, false, "user" },
+ { "a*b", "a*b", true, NONE, false, "user" },
+ { "a?b", "a?b", true, NONE, false, "user" },
+ { "a1b", "a1b", true, NONE, false, "user" },
+ { "a1b", "a*", true, NONE, false, "user" },
+ { "a1b", "*b", true, NONE, false, "user" },
+ { "a1b", "*1*", true, NONE, false, "user" },
+ { "a1b", "a?b", true, NONE, false, "user" },
+ { "a", "abc", true, SELF, true, "user" },
+ { "ab", "abc", true, SELF, true, "user" },
+ { "ab", "*c", true, SELF, true, "user" },
+ { "*b", "a*bc", true, SELF, true, "user" },
+ { "a*b", "a*bc", true, SELF, true, "user" },
+ };
+
+ Object[][] dataForPrefixMatch = {
+ // { resource, policy, excludes, matchType, result, user }
+ { "a", "abc", false, PREFIX, true, "user" },
+ { "ab", "abc", false, PREFIX, true, "user" },
+ { "ab", "*c", false, PREFIX, true, "user" },
+ { "a", "a*c", false, PREFIX, true, "user" },
+ { "ab", "a*c", false, PREFIX, true, "user" },
+ { "abc", "a*c", false, SELF, true, "user" },
+ { "abcd", "a*c", false, PREFIX, true, "user" },
+ { "abcd", "a*c*d", false, SELF, true, "user" },
+ { "abcd", "a*c*de", false, PREFIX, true, "user" },
+ { "acbd", "ab*c", false, NONE, false, "user" },
+ { "b", "ab*c", false, NONE, false, "user" },
+ { "a", "ab*", false, PREFIX, true, "user" },
+ { "b", "ab*", false, NONE, false, "user" },
};
@Test
public void testIsMatch() throws Exception {
- for (Object[] row : data) {
- String resource = (String)row[0];
- String policyValue = (String)row[1];
- boolean excludes = (boolean)row[2];
- boolean result = (boolean)row[3];
- String user = (String) row[4];
+ ResourceElementMatchingScope matchScope = ResourceElementMatchingScope.SELF;
- Map<String, Object> evalContext = new HashMap<>();
+ for (Object[] row : data) {
+ String resource = (String)row[0];
+ String policyValue = (String)row[1];
+ boolean excludes = (boolean)row[2];
+ ResourceElementMatchType matchType = (ResourceElementMatchType) row[3];
+ boolean result = (boolean)row[4];
+ String user = (String) row[5];
+ Map<String, Object> evalContext = new HashMap<>();
+
RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user);
MatcherWrapper matcher = new MatcherWrapper(policyValue, excludes);
- assertEquals(getMessage(row), result, matcher.isMatch(resource, ResourceElementMatchingScope.SELF, evalContext));
+
+ assertEquals(getMessage(row), matchType, matcher.getMatchType(resource, matchScope, evalContext));
+ assertEquals(getMessage(row), result, matcher.isMatch(resource, matchScope, evalContext));
+ }
+ }
+
+ @Test
+ public void testIsPrefixMatch() {
+ ResourceElementMatchingScope matchScope = ResourceElementMatchingScope.SELF_OR_PREFIX;
+
+ for (Object[] row : dataForPrefixMatch) {
+ String resource = (String)row[0];
+ String policyValue = (String)row[1];
+ boolean excludes = (boolean)row[2];
+ ResourceElementMatchType matchType = (ResourceElementMatchType) row[3];
+ boolean result = (boolean)row[4];
+ String user = (String) row[5];
+ Map<String, Object> evalContext = new HashMap<>();
+
+ RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user);
+
+ MatcherWrapper matcher = new MatcherWrapper(policyValue, excludes);
+
+ assertEquals(getMessage(row), matchType, matcher.getMatchType(resource, matchScope, evalContext));
+ assertEquals(getMessage(row), result, matcher.isMatch(resource, matchScope, evalContext));
}
}
String getMessage(Object[] row) {
- return String.format("Resource=%s, Policy=%s, excludes=%s, result=%s",
- (String)row[0], (String)row[1], (boolean)row[2], (boolean)row[3]);
+ return String.format("Resource=%s, Policy=%s, excludes=%s, matchScope=%s, matchType=%s, result=%s",
+ row[0], row[1], row[2], row[3], row[4], row[5]);
}
static class MatcherWrapper extends RangerDefaultResourceMatcher {
@@ -74,7 +164,7 @@ static class MatcherWrapper extends RangerDefaultResourceMatcher {
RangerResourceDef resourceDef = new RangerResourceDef();
Map<String, String> matcherOptions = new HashMap<>();
- matcherOptions.put(OPTION_WILD_CARD, Boolean.toString(policyValue.contains(WILDCARD_ASTERISK)));
+ matcherOptions.put(OPTION_WILD_CARD, Boolean.toString(policyValue.contains(WILDCARD_ASTERISK) || policyValue.contains(WILDCARD_QUESTION_MARK)));
matcherOptions.put(OPTION_IGNORE_CASE, Boolean.toString(false));
resourceDef.setMatcherOptions(matcherOptions);
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java
index ed02be6..3727b30 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java
@@ -94,6 +94,59 @@ public class RangerPathResourceMatcherTest {
{ "/app/hbase/test.db", "/app/hbase/test.db/tmp/test.t*", true, false, false, "user" },
};
+ Object[][] dataForSelfOrPrefixScope = {
+ // { resource, policy, optWildcard, recursive, result
+ { "/", "/app/hive/test.db", true, false, true, "user" },
+ { "/app", "/app/hive/test.db", true, false, true, "user" },
+ { "/app/", "/app/hive/test.db", true, false, true, "user" },
+ { "/app/hive", "/app/hive/test.db", true, false, true, "user" },
+ { "/app/hive/", "/app/hive/test.db", true, false, true, "user" },
+ { "/app/hive/test.db", "/app/hive/test.db", true, false, true, "user" },
+ { "/", "/app/*/test.db", true, false, true, "user" },
+ { "/app", "/app/*/test.db", true, false, true, "user" },
+ { "/app/", "/app/*/test.db", true, false, true, "user" },
+ { "/app/hive", "/app/*/test.db", true, false, true, "user" },
+ { "/app/hive/", "/app/*/test.db", true, false, true, "user" },
+ { "/app/hive/test.db", "/app/*/test.db", true, false, true, "user" },
+ { "/", "*/hive/test.db", true, false, true, "user" },
+ { "/app", "*/hive/test.db", true, false, true, "user" },
+ { "/app/", "*/hive/test.db", true, false, true, "user" },
+ { "/app/hive", "*/hive/test.db", true, false, true, "user" },
+ { "/app/hive/", "*/hive/test.db", true, false, true, "user" },
+ { "/app/hive/test.db", "*/hive/test.db", true, false, true, "user" },
+ { "/", "/*", true, false, true, "user" },
+ { "/app", "/*", true, false, true, "user" },
+ { "/app/", "/*", true, false, true, "user" },
+ { "/app/hive", "/*", true, false, true, "user" },
+ { "/app/hive/", "/*", true, false, true, "user" },
+ { "/app/hive/test.db", "/*", true, false, true, "user" },
+
+ { "/", "/app/hive/test.db", true, true, true, "user" },
+ { "/app", "/app/hive/test.db", true, true, true, "user" },
+ { "/app/", "/app/hive/test.db", true, true, true, "user" },
+ { "/app/hive", "/app/hive/test.db", true, true, true, "user" },
+ { "/app/hive/", "/app/hive/test.db", true, true, true, "user" },
+ { "/app/hive/test.db", "/app/hive/test.db", true, true, true, "user" },
+ { "/", "/app/*/test.db", true, true, true, "user" },
+ { "/app", "/app/*/test.db", true, true, true, "user" },
+ { "/app/", "/app/*/test.db", true, true, true, "user" },
+ { "/app/hive", "/app/*/test.db", true, true, true, "user" },
+ { "/app/hive/", "/app/*/test.db", true, true, true, "user" },
+ { "/app/hive/test.db", "/app/*/test.db", true, true, true, "user" },
+ { "/", "*/hive/test.db", true, true, true, "user" },
+ { "/app", "*/hive/test.db", true, true, true, "user" },
+ { "/app/", "*/hive/test.db", true, true, true, "user" },
+ { "/app/hive", "*/hive/test.db", true, true, true, "user" },
+ { "/app/hive/", "*/hive/test.db", true, true, true, "user" },
+ { "/app/hive/test.db", "*/hive/test.db", true, true, true, "user" },
+ { "/", "/", true, true, true, "user" },
+ { "/app", "/", true, true, true, "user" },
+ { "/app/", "/", true, true, true, "user" },
+ { "/app/hive", "/", true, true, true, "user" },
+ { "/app/hive/", "/", true, true, true, "user" },
+ { "/app/hive/test.db", "/", true, true, true, "user" },
+ };
+
@Test
public void testIsMatch() throws Exception {
for (Object[] row : data) {
@@ -130,6 +183,26 @@ public void testIsMatchForSelfOrChildScope() throws Exception {
}
}
+ @Test
+ public void testIsMatchForSelfOrPrefixScope() {
+ ResourceElementMatchingScope matchScope = ResourceElementMatchingScope.SELF_OR_PREFIX;
+
+ for (Object[] row : dataForSelfOrPrefixScope) {
+ String resource = (String)row[0];
+ String policyValue = (String)row[1];
+ boolean optWildcard = (boolean)row[2];
+ boolean isRecursive = (boolean)row[3];
+ boolean result = (boolean)row[4];
+ String user = (String) row[5];
+ Map<String, Object> evalContext = new HashMap<>();
+
+ RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user);
+
+ MatcherWrapper matcher = new MatcherWrapper(policyValue, optWildcard, isRecursive);
+ assertEquals(getMessage(row), result, matcher.isMatch(resource, matchScope, evalContext));
+ }
+ }
+
String getMessage(Object[] row) {
return String.format("Resource=%s, Policy=%s, optWildcard=%s, recursive=%s, result=%s",
(String)row[0], (String)row[1], (boolean)row[2], (boolean)row[3], (boolean)row[4]);
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java
index 904cb61..cc73076 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java
@@ -53,6 +53,60 @@ public class RangerURLResourceMatcherTest {
{ "://apps/warehouse/data/emp.db", "hdfs://app/*", true, true, false, "user" }
};
+
+ Object[][] dataForSelfOrPrefixScope = {
+ // { resource, policy, optWildcard, recursive, result
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" },
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020/*", true, false, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020/*", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/*", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/*", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/*", true, false, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/*", true, false, true, "user" },
+
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" },
+ { "hdfs://hostname:8020/", "hdfs://hostname:8020/", true, true, true, "user" },
+ { "hdfs://hostname:8020/app", "hdfs://hostname:8020/", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/", true, true, true, "user" },
+ { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/", true, true, true, "user" },
+ };
+
@Test
public void testIsMatch() throws Exception {
for (Object[] row : data) {
@@ -71,6 +125,25 @@ public void testIsMatch() throws Exception {
}
}
+ @Test
+ public void testIsPrefixMatch() {
+ for (Object[] row : dataForSelfOrPrefixScope) {
+ String resource = (String)row[0];
+ String policyValue = (String)row[1];
+ boolean optWildcard = (boolean)row[2];
+ boolean isRecursive = (boolean)row[3];
+ boolean result = (boolean)row[4];
+ String user = (String) row[5];
+
+ Map<String, Object> evalContext = new HashMap<>();
+
+ RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user);
+
+ MatcherWrapper matcher = new MatcherWrapper(policyValue, optWildcard, isRecursive);
+ assertEquals(getMessage(row), result, matcher.isMatch(resource, ResourceElementMatchingScope.SELF_OR_PREFIX, evalContext));
+ }
+ }
+
String getMessage(Object[] row) {
return String.format("Resource=%s, Policy=%s, optWildcard=%s, recursive=%s, result=%s",
(String)row[0], (String)row[1], (boolean)row[2], (boolean)row[3], (boolean)row[4]);