This is an automated email from the ASF dual-hosted git repository.

madhan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new c9d138ff7 RANGER-4328: updated resource matcher handling of 
SELF_OR_PREFIX scope
c9d138ff7 is described below

commit c9d138ff7d9b5aeaa58a56d318ea5aaae357ba16
Author: Madhan Neethiraj <[email protected]>
AuthorDate: Mon Jul 24 09:09:54 2023 -0700

    RANGER-4328: updated resource matcher handling of SELF_OR_PREFIX scope
---
 .../RangerAbstractResourceMatcher.java             | 125 ++++++++---
 .../RangerDefaultResourceMatcher.java              |  76 ++++---
 .../resourcematcher/RangerPathResourceMatcher.java | 239 +++++++++++++++++++--
 .../resourcematcher/RangerResourceMatcher.java     |   3 +
 .../resourcematcher/RangerURLResourceMatcher.java  |  35 ++-
 .../plugin/resourcematcher/ResourceMatcher.java    | 165 ++++++++++++--
 .../RangerAbstractResourceMatcherTest.java         |   9 +
 .../RangerDefaultResourceMatcherTest.java          | 132 ++++++++++--
 .../RangerPathResourceMatcherTest.java             |  73 +++++++
 .../RangerURLResourceMatcherTest.java              |  73 +++++++
 10 files changed, 820 insertions(+), 110 deletions(-)

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 cb8d46adf..5eee8d11a 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.io.IOCase;
 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 abstract class RangerAbstractResourceMatcher 
implements RangerResourceMat
                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 @@ public abstract class RangerAbstractResourceMatcher 
implements RangerResourceMat
 }
 
 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 @@ abstract class AbstractStringResourceMatcher extends 
ResourceMatcher {
 
 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 @@ final class QuotedCaseSensitiveStringMatcher extends 
AbstractStringResourceMatch
                }
        }
 
+       @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 @@ final class QuotedCaseSensitiveStartsWithMatcher extends 
AbstractStringResourceM
                }
        }
 
+       @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 @@ final class QuotedCaseSensitiveEndsWithMatcher extends 
AbstractStringResourceMat
                }
        }
 
+       @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 @@ final class QuotedCaseSensitiveWildcardMatcher extends 
AbstractStringResourceMat
                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 702cb272f..977fd4941 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 @@ package org.apache.ranger.plugin.resourcematcher;
 
 
 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,20 +40,54 @@ public class RangerDefaultResourceMatcher extends 
RangerAbstractResourceMatcher
                        LOG.debug("==> RangerDefaultResourceMatcher.isMatch(" + 
resource + ", " + evalContext + ")");
                }
 
-               boolean ret = false;
-               boolean allValuesRequested = isAllValuesRequested(resource);
-               boolean isPrefixMatch = matchingScope == 
ResourceElementMatchingScope.SELF_OR_PREFIX;
+               ResourceElementMatchType matchType = getMatchType(resource, 
matchingScope, evalContext);
+               boolean                  ret       = 
ResourceMatcher.isMatch(matchType, matchingScope);
+
+               if (ret == false) {
+                       if(LOG.isDebugEnabled()) {
+                               StringBuilder sb = new StringBuilder();
+                               sb.append("[");
+                               for (String policyValue: policyValues) {
+                                       sb.append(policyValue);
+                                       sb.append(" ");
+                               }
+                               sb.append("]");
+
+                               LOG.debug("RangerDefaultResourceMatcher.isMatch 
returns FALSE, (resource=" + resource + ", policyValues=" + sb.toString() + 
")");
+                       }
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug("<== RangerDefaultResourceMatcher.isMatch(" + 
resource + ", " + evalContext + "): " + ret);
+               }
+
+               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;
+                       ret = isMatchAny ? ResourceElementMatchType.SELF : NONE;
                } else {
                        if (resource instanceof String) {
                                String strValue = (String) resource;
 
                                for (ResourceMatcher resourceMatcher : 
resourceMatchers.getResourceMatchers()) {
-                                       ret = resourceMatcher.isMatch(strValue, 
matchingScope, evalContext);
+                                       ResourceElementMatchType matchType = 
resourceMatcher.getMatchType(strValue, matchingScope, evalContext);
 
-                                       if (ret) {
+                                       if (matchType != NONE) {
+                                               ret = matchType;
+                                       }
+
+                                       if (ret == 
ResourceElementMatchType.SELF) {
                                                break;
                                        }
                                }
@@ -60,13 +97,18 @@ public class RangerDefaultResourceMatcher extends 
RangerAbstractResourceMatcher
 
                                for (ResourceMatcher resourceMatcher : 
resourceMatchers.getResourceMatchers()) {
                                        for (String resourceValue : 
resourceValues) {
-                                               ret = 
resourceMatcher.isMatch(resourceValue, matchingScope, evalContext);
+                                               ResourceElementMatchType 
matchType = resourceMatcher.getMatchType(resourceValue, matchingScope, 
evalContext);
+
+                                               if (matchType != NONE) {
+                                                       ret = matchType;
+                                               }
 
-                                               if (ret) {
+                                               if (ret == 
ResourceElementMatchType.SELF) {
                                                        break;
                                                }
                                        }
-                                       if (ret) {
+
+                                       if (ret == 
ResourceElementMatchType.SELF) {
                                                break;
                                        }
                                }
@@ -75,22 +117,8 @@ public class RangerDefaultResourceMatcher extends 
RangerAbstractResourceMatcher
 
                ret = applyExcludes(allValuesRequested, ret);
 
-               if (ret == false) {
-                       if(LOG.isDebugEnabled()) {
-                               StringBuilder sb = new StringBuilder();
-                               sb.append("[");
-                               for (String policyValue: policyValues) {
-                                       sb.append(policyValue);
-                                       sb.append(" ");
-                               }
-                               sb.append("]");
-
-                               LOG.debug("RangerDefaultResourceMatcher.isMatch 
returns FALSE, (resource=" + resource + ", policyValues=" + sb.toString() + 
")");
-                       }
-               }
-
                if(LOG.isDebugEnabled()) {
-                       LOG.debug("<== RangerDefaultResourceMatcher.isMatch(" + 
resource + ", " + evalContext + "): " + ret);
+                       LOG.debug("<== 
RangerDefaultResourceMatcher.getMatchType(" + resource + ", " + evalContext + 
"): " + ret);
                }
 
                return ret;
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 3c1523c25..d376e9025 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 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                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 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                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 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                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 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                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 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                @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 class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                                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 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                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 + ")");
                        }
@@ -388,6 +537,20 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                        return ret;
                }
 
+               @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;
@@ -414,7 +577,7 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                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;
@@ -444,6 +607,20 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                        return ret;
                }
 
+               @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;
@@ -470,14 +647,16 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                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) {
@@ -520,6 +699,20 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                        return ret;
                }
 
+               @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;
@@ -534,7 +727,7 @@ public class RangerPathResourceMatcher extends 
RangerDefaultResourceMatcher {
                                }
                                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 c4ce30d36..ec22e01bf 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 @@ package org.apache.ranger.plugin.resourcematcher;
 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 ce73b3006..057563696 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 @@ public class RangerURLResourceMatcher extends 
RangerDefaultResourceMatcher {
 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 @@ abstract class RecursiveMatcher extends 
AbstractStringResourceMatcher {
 
 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 @@ final class CaseSensitiveURLRecursiveMatcher extends 
RecursiveMatcher {
 
         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 @@ final class CaseInsensitiveURLRecursiveMatcher extends 
RecursiveMatcher {
         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 ad0942fbe..77245eae1 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.Logger;
 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 @@ abstract class ResourceMatcher {
         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 767795e81..b6864183d 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 class RangerAbstractResourceMatcherTest {
             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 e00bc834d..8a297bde0 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 com.google.common.collect.Lists;
 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.HashMap;
 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 {
+        ResourceElementMatchingScope matchScope = 
ResourceElementMatchingScope.SELF;
+
         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];
+            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<>();
 
-            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 @@ public class RangerDefaultResourceMatcherTest {
             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 ed02be674..3727b30d4 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 class RangerPathResourceMatcherTest {
         }
     }
 
+    @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 904cb61b2..cc7307615 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 class RangerURLResourceMatcherTest {
         }
     }
 
+    @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]);

Reply via email to