RANGER-1145: Policy engine optimization: convert wildcard matches into prefix 
and suffix match

Signed-off-by: Madhan Neethiraj <[email protected]>
(cherry picked from commit 818e1d354312443fd8e29d49861b91d13e239be2)


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/d79f127c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/d79f127c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/d79f127c

Branch: refs/heads/ranger-0.6
Commit: d79f127c2f8331610f5c28cafed43f2eccff6b11
Parents: 543bfe0
Author: Abhay Kulkarni <[email protected]>
Authored: Thu Aug 4 20:53:58 2016 -0700
Committer: Madhan Neethiraj <[email protected]>
Committed: Thu Sep 15 12:20:41 2016 -0700

----------------------------------------------------------------------
 .../RangerAbstractResourceMatcher.java          | 199 ++++++++++++++++---
 .../RangerDefaultResourceMatcher.java           |  21 +-
 .../RangerPathResourceMatcher.java              | 144 ++++++++------
 .../resourcematcher/RangerResourceMatcher.java  |   1 +
 .../plugin/resourcematcher/ResourceMatcher.java |  39 ++++
 .../plugin/policyengine/TestPolicyEngine.java   |   7 +
 .../RangerDefaultResourceMatcherTest.java       |  13 +-
 .../RangerPathResourceMatcherTest.java          |  81 ++++++++
 .../test_policyengine_hdfs_resourcespec.json    |  38 ++++
 9 files changed, 443 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java
----------------------------------------------------------------------
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 cd725c9..329b8e8 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
@@ -20,10 +20,13 @@
 package org.apache.ranger.plugin.resourcematcher;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOCase;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -51,6 +54,7 @@ public abstract class RangerAbstractResourceMatcher 
implements RangerResourceMat
        protected List<String> policyValues     = null;
        protected boolean      policyIsExcludes = false;
        protected boolean      isMatchAny       = false;
+       protected List<ResourceMatcher> resourceMatchers = null;
 
        @Override
        public void setResourceDef(RangerResourceDef resourceDef) {
@@ -75,33 +79,42 @@ public abstract class RangerAbstractResourceMatcher 
implements RangerResourceMat
                policyIsExcludes = policyResource == null ? false : 
policyResource.getIsExcludes();
 
                if(policyResource != null && policyResource.getValues() != 
null) {
-                       boolean isWildCardPresent = false;
-                       for(String policyValue : policyResource.getValues()) {
-                               if(StringUtils.isEmpty(policyValue)) {
+                       for (String policyValue : policyResource.getValues()) {
+                               if (StringUtils.isEmpty(policyValue)) {
                                        continue;
                                }
-
-                               if(optWildCard) {
-                                       if 
(StringUtils.containsOnly(policyValue, WILDCARD_ASTERISK)) {
-                                               isMatchAny = true;
-                                       } else if (!isWildCardPresent && 
StringUtils.containsAny(policyValue, WILDCARDS)) {
-                                               isWildCardPresent = true;
-                                       }
-                               }
                                policyValues.add(policyValue);
                        }
-                       optWildCard = optWildCard && isWildCardPresent;
-               }
-
-               if(policyValues.isEmpty()) {
-                       isMatchAny = true;
                }
+               resourceMatchers = buildResourceMatchers();
+               isMatchAny = CollectionUtils.isEmpty(resourceMatchers);
 
                if(LOG.isDebugEnabled()) {
                        LOG.debug("<== RangerAbstractResourceMatcher.init()");
                }
        }
 
+       protected List<ResourceMatcher> buildResourceMatchers() {
+               List<ResourceMatcher> ret = new ArrayList<ResourceMatcher> ();
+
+               for (String policyValue : policyValues) {
+                       ResourceMatcher matcher = getMatcher(policyValue);
+
+                       if (matcher != null) {
+                               if (matcher.isMatchAny()) {
+                                       ret.clear();
+                                       break;
+                               } else {
+                                       ret.add(matcher);
+                               }
+                       }
+               }
+
+               Collections.sort(ret);
+
+               return ret;
+       }
+
        @Override
        public boolean isCompleteMatch(String resource) {
                if(LOG.isDebugEnabled()) {
@@ -228,11 +241,6 @@ public abstract class RangerAbstractResourceMatcher 
implements RangerResourceMat
                return sb;
        }
 
-       /**
-        * Is resource asking to authorize all possible values at this level?
-        * @param resource
-        * @return
-        */
        boolean isAllValuesRequested(String resource) {
                boolean result = StringUtils.isEmpty(resource) || 
WILDCARD_ASTERISK.equals(resource);
                if (LOG.isDebugEnabled()) {
@@ -246,13 +254,156 @@ public abstract class RangerAbstractResourceMatcher 
implements RangerResourceMat
         * - Resource denotes all possible values (i.e. resource in (null, "", 
"*")
         * - where as policy does not allow all possible values (i.e. 
policy.values().contains("*")
         *
-        * @param allValuesRequested
-        * @param resultWithoutExcludes
-     * @return
      */
        public boolean applyExcludes(boolean allValuesRequested, boolean 
resultWithoutExcludes) {
                if (!policyIsExcludes) return resultWithoutExcludes;            
       // not an excludes policy!
                if (allValuesRequested && !isMatchAny)  return 
resultWithoutExcludes;  // one case where excludes has no effect
                return !resultWithoutExcludes;                                  
       // all other cases flip it
        }
+
+       ResourceMatcher getMatcher(String policyValue) {
+               final int len = policyValue != null ? policyValue.length() : 0;
+
+               if (len == 0) {
+                       return null;
+               }
+
+               final ResourceMatcher ret;
+
+               int wildcardStartIdx = -1;
+               int wildcardEndIdx = -1;
+               boolean needWildcardMatch = false;
+
+               // If optWildcard is true
+               //   If ('?' found or non-contiguous '*'s found in policyValue)
+               //         needWildcardMatch = true
+               //       End
+               //
+               //       wildcardStartIdx is set to index of first '*' in 
policyValue or -1 if '*' is not found in policyValue, and
+               //       wildcardEndIdx is set to index of last '*' in 
policyValue or -1 if '*' is not found in policyValue
+               // Else
+               //       needWildcardMatch is set to false
+               // End
+               if (optWildCard) {
+                       for (int i = 0; i < len; i++) {
+                               final char c = policyValue.charAt(i);
+
+                               if (c == '?') {
+                                       needWildcardMatch = true;
+                                       break;
+                               } else if (c == '*') {
+                                       if (wildcardEndIdx == -1 || 
wildcardEndIdx == (i - 1)) {
+                                               wildcardEndIdx = i;
+                                               if (wildcardStartIdx == -1) {
+                                                       wildcardStartIdx = i;
+                                               }
+                                       } else {
+                                               needWildcardMatch = true;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               if (needWildcardMatch) {
+                       ret = optIgnoreCase ? new 
CaseInsensitiveWildcardMatcher(policyValue) : new 
CaseSensitiveWildcardMatcher(policyValue);
+               } else if (wildcardStartIdx == -1) {
+                       ret = optIgnoreCase ? new 
CaseInsensitiveStringMatcher(policyValue) : new 
CaseSensitiveStringMatcher(policyValue);
+               } else if (wildcardStartIdx == 0) {
+                       String matchStr = policyValue.substring(wildcardEndIdx 
+ 1);
+                       ret = optIgnoreCase ? new 
CaseInsensitiveEndsWithMatcher(matchStr) : new 
CaseSensitiveEndsWithMatcher(matchStr);
+               } else {
+                       String matchStr = policyValue.substring(0, 
wildcardStartIdx);
+                       ret = optIgnoreCase ? new 
CaseInsensitiveStartsWithMatcher(matchStr) : new 
CaseSensitiveStartsWithMatcher(matchStr);
+               }
+
+               return ret;
+       }
+}
+
+final class CaseSensitiveStringMatcher extends ResourceMatcher {
+       CaseSensitiveStringMatcher(String value) {
+               super(value);
+       }
+
+       boolean isMatch(String str) {
+               return StringUtils.equals(str, value);
+       }
+       int getPriority() { return 1;}
+}
+
+final class CaseInsensitiveStringMatcher extends ResourceMatcher {
+       CaseInsensitiveStringMatcher(String value) { super(value); }
+
+       boolean isMatch(String str) {
+               return StringUtils.equalsIgnoreCase(str, value);
+       }
+       int getPriority() {return 2; }
+}
+
+final class CaseSensitiveStartsWithMatcher extends ResourceMatcher {
+       CaseSensitiveStartsWithMatcher(String value) {
+               super(value);
+       }
+
+       boolean isMatch(String str) {
+               return StringUtils.startsWith(str, value);
+       }
+       int getPriority() { return 3;}
+}
+
+final class CaseInsensitiveStartsWithMatcher extends ResourceMatcher {
+       CaseInsensitiveStartsWithMatcher(String value) { super(value); }
+
+       boolean isMatch(String str) {
+               return StringUtils.startsWithIgnoreCase(str, value);
+       }
+       int getPriority() { return 4; }
 }
+
+final class CaseSensitiveEndsWithMatcher extends ResourceMatcher {
+       CaseSensitiveEndsWithMatcher(String value) {
+               super(value);
+       }
+
+       boolean isMatch(String str) {
+               return StringUtils.endsWith(str, value);
+       }
+       int getPriority() { return 3; }
+}
+
+final class CaseInsensitiveEndsWithMatcher extends ResourceMatcher {
+       CaseInsensitiveEndsWithMatcher(String value) {
+               super(value);
+       }
+
+       boolean isMatch(String str) {
+               return StringUtils.endsWithIgnoreCase(str, value);
+       }
+       int getPriority() { return 4; }
+}
+
+final class CaseSensitiveWildcardMatcher extends ResourceMatcher {
+       CaseSensitiveWildcardMatcher(String value) {
+               super(value);
+       }
+
+       boolean isMatch(String str) {
+               return FilenameUtils.wildcardMatch(str, value, 
IOCase.SENSITIVE);
+       }
+       int getPriority() { return 5; }
+}
+
+
+final class CaseInsensitiveWildcardMatcher extends ResourceMatcher {
+       CaseInsensitiveWildcardMatcher(String value) {
+               super(value);
+       }
+
+       boolean isMatch(String str) {
+               return FilenameUtils.wildcardMatch(str, value, 
IOCase.INSENSITIVE);
+       }
+       int getPriority() {return 6; }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java
----------------------------------------------------------------------
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 669cf0a..0a11be0 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
@@ -20,9 +20,7 @@
 package org.apache.ranger.plugin.resourcematcher;
 
 
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOCase;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -42,17 +40,12 @@ public class RangerDefaultResourceMatcher extends 
RangerAbstractResourceMatcher
                if(allValuesRequested || isMatchAny) {
                        ret = isMatchAny;
                } else {
-                       for(String policyValue : policyValues) {
-                               if(optWildCard) {
-                                       ret = optIgnoreCase ? 
FilenameUtils.wildcardMatch(resource, policyValue, IOCase.INSENSITIVE)
-                                                                               
: FilenameUtils.wildcardMatch(resource, policyValue, IOCase.SENSITIVE);
-                               } else {
-                                       ret = optIgnoreCase ? 
StringUtils.equalsIgnoreCase(resource, policyValue)
-                                                                               
: StringUtils.equals(resource, policyValue);
-                               }
-
-                               if(ret) {
-                                       break;
+                       if (CollectionUtils.isNotEmpty(resourceMatchers)) {
+                               for (ResourceMatcher resourceMatcher : 
resourceMatchers) {
+                                       ret = resourceMatcher.isMatch(resource);
+                                       if (ret) {
+                                               break;
+                                       }
                                }
                        }
                }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java
----------------------------------------------------------------------
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 5c555eb..d508f3f 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
@@ -19,10 +19,6 @@
 
 package org.apache.ranger.plugin.resourcematcher;
 
-
-import java.util.ArrayList;
-import java.util.List;
-
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOCase;
 import org.apache.commons.lang.ArrayUtils;
@@ -30,16 +26,19 @@ import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 
-public class RangerPathResourceMatcher extends RangerAbstractResourceMatcher {
+public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
        private static final Log LOG = 
LogFactory.getLog(RangerPathResourceMatcher.class);
 
-       public static final String OPTION_PATH_SEPERATOR       = 
"pathSeparatorChar";
-       public static final char   DEFAULT_PATH_SEPERATOR_CHAR = 
org.apache.hadoop.fs.Path.SEPARATOR_CHAR;
+       private static final String OPTION_PATH_SEPARATOR       = 
"pathSeparatorChar";
+       private static final char   DEFAULT_PATH_SEPARATOR_CHAR = 
org.apache.hadoop.fs.Path.SEPARATOR_CHAR;
 
-       private boolean      policyIsRecursive    = false;
-       private char         pathSeparatorChar    = DEFAULT_PATH_SEPERATOR_CHAR;
-       private List<String> policyValuesForMatch = null;
+       private boolean policyIsRecursive    = false;
+       private char    pathSeparatorChar = '/';
 
        @Override
        public void init() {
@@ -47,76 +46,82 @@ public class RangerPathResourceMatcher extends 
RangerAbstractResourceMatcher {
                        LOG.debug("==> RangerPathResourceMatcher.init()");
                }
 
+               policyIsRecursive = policyResource == null ? false : 
policyResource.getIsRecursive();
+               pathSeparatorChar = getCharOption(OPTION_PATH_SEPARATOR, 
DEFAULT_PATH_SEPARATOR_CHAR);
+
                super.init();
 
-               policyIsRecursive = policyResource == null ? false : 
policyResource.getIsRecursive();
-               pathSeparatorChar = getCharOption(OPTION_PATH_SEPERATOR, 
DEFAULT_PATH_SEPERATOR_CHAR);
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug("<== RangerPathResourceMatcher.init()");
+               }
+       }
 
-               if(policyIsRecursive && optWildCard && !isMatchAny) {
-                       policyValuesForMatch = new ArrayList<String>();
+       @Override
+       protected List<ResourceMatcher> buildResourceMatchers() {
+               List<ResourceMatcher> ret = new ArrayList<ResourceMatcher>();
+
+               for (String policyValue : policyValues) {
+                       if (optWildCard && policyIsRecursive) {
+                               if (policyValue.charAt(policyValue.length() - 
1) == pathSeparatorChar) {
+                                       policyValue += WILDCARD_ASTERISK;
+                               }
+                       }
 
-                       for(String policyValue : policyValues) {
-                               if(policyValue.charAt(policyValue.length() - 1) 
== pathSeparatorChar) {
-                                       policyValuesForMatch.add(policyValue + 
WILDCARD_ASTERISK);
+                       ResourceMatcher matcher = getMatcher(policyValue);
+
+                       if (matcher != null) {
+                               if (matcher.isMatchAny()) {
+                                       ret.clear();
+                                       break;
                                } else {
-                                       policyValuesForMatch.add(policyValue);
+                                       ret.add(matcher);
                                }
                        }
-               } else {
-                       policyValuesForMatch = policyValues;
                }
 
-               if(LOG.isDebugEnabled()) {
-                       LOG.debug("<== RangerPathResourceMatcher.init()");
-               }
+               Collections.sort(ret);
+
+               return ret;
        }
 
        @Override
-       public boolean isMatch(String resource) {
-               if(LOG.isDebugEnabled()) {
-                       LOG.debug("==> RangerPathResourceMatcher.isMatch(" + 
resource + ")");
+       ResourceMatcher getMatcher(String policyValue) {
+               if(! policyIsRecursive) {
+                       return super.getMatcher(policyValue);
                }
 
-               boolean ret = false;
-               boolean allValuesRequested = isAllValuesRequested(resource);
+               final int len = policyValue != null ? policyValue.length() : 0;
 
-               if(allValuesRequested || isMatchAny) {
-                       ret = isMatchAny;
-               } else {
-                       IOCase caseSensitivity = optIgnoreCase ? 
IOCase.INSENSITIVE : IOCase.SENSITIVE;
-
-                       for(String policyValue : policyValuesForMatch) {
-                               if(policyIsRecursive && optWildCard) {
-                                       ret = 
isRecursiveWildCardMatch(resource, policyValue, pathSeparatorChar, 
caseSensitivity);
-                               } else if(policyIsRecursive) {
-                                       ret = optIgnoreCase ? 
StringUtils.startsWithIgnoreCase(resource, policyValue)
-                                                                               
: StringUtils.startsWith(resource, policyValue);
-                               } else if(optWildCard) {
-                                       ret = 
FilenameUtils.wildcardMatch(resource, policyValue, caseSensitivity);
-                               } else {
-                                       ret = optIgnoreCase ? 
StringUtils.equalsIgnoreCase(resource, policyValue)
-                                                                               
: StringUtils.equals(resource, policyValue);
-                               }
+               if (len == 0) {
+                       return null;
+               }
+
+               boolean isWildcardPresent = false;
+
+               if (optWildCard) {
+                       for (int i = 0; i < len; i++) {
+                               final char c = policyValue.charAt(i);
 
-                               if(ret) {
+                               if (c == '?' || c == '*') {
+                                       isWildcardPresent = true;
                                        break;
                                }
                        }
                }
 
-               ret = applyExcludes(allValuesRequested, ret);
+               final ResourceMatcher ret;
 
-               if(LOG.isDebugEnabled()) {
-                       LOG.debug("<== RangerPathResourceMatcher.isMatch(" + 
resource + "): " + ret);
+               if (isWildcardPresent) {
+                       ret = optIgnoreCase ? new 
CaseInsensitiveRecursiveWildcardMatcher(policyValue, pathSeparatorChar)
+                                                               : new 
CaseSensitiveRecursiveWildcardMatcher(policyValue, pathSeparatorChar);
+               } else {
+                       ret = optIgnoreCase ? new 
CaseInsensitiveStartsWithMatcher(policyValue) : new 
CaseSensitiveStartsWithMatcher(policyValue);
                }
 
                return ret;
        }
 
-       private boolean isRecursiveWildCardMatch(String pathToCheck, String 
wildcardPath, char pathSeparatorChar, IOCase caseSensitivity) {
-               if(LOG.isDebugEnabled()) {
-                       LOG.debug("==> 
RangerPathResourceMatcher.isRecursiveWildCardMatch(" + pathToCheck + ", " + 
wildcardPath + ", " + pathSeparatorChar + ")");
-               }
+       static boolean isRecursiveWildCardMatch(String pathToCheck, String 
wildcardPath, char pathSeparatorChar, IOCase caseSensitivity) {
 
                boolean ret = false;
 
@@ -148,13 +153,10 @@ public class RangerPathResourceMatcher extends 
RangerAbstractResourceMatcher {
                        }
                }
 
-               if(LOG.isDebugEnabled()) {
-                       LOG.debug("<== 
RangerPathResourceMatcher.isRecursiveWildCardMatch(" + pathToCheck + ", " + 
wildcardPath + ", " + pathSeparatorChar + "): " + ret);
-               }
-
                return ret;
        }
 
+
        public StringBuilder toString(StringBuilder sb) {
                sb.append("RangerPathResourceMatcher={");
 
@@ -167,3 +169,31 @@ public class RangerPathResourceMatcher extends 
RangerAbstractResourceMatcher {
                return sb;
        }
 }
+
+final class CaseSensitiveRecursiveWildcardMatcher extends ResourceMatcher {
+       private final char levelSeparatorChar;
+       CaseSensitiveRecursiveWildcardMatcher(String value, char 
levelSeparatorChar) {
+               super(value);
+               this.levelSeparatorChar = levelSeparatorChar;
+       }
+
+       boolean isMatch(String str) {
+               return RangerPathResourceMatcher.isRecursiveWildCardMatch(str, 
value, levelSeparatorChar, IOCase.SENSITIVE);
+       }
+       int getPriority() { return 7;}
+}
+
+final class CaseInsensitiveRecursiveWildcardMatcher extends ResourceMatcher {
+       private final char levelSeparatorChar;
+       CaseInsensitiveRecursiveWildcardMatcher(String value, char 
levelSeparatorChar) {
+               super(value);
+               this.levelSeparatorChar = levelSeparatorChar;
+       }
+
+       boolean isMatch(String str) {
+               return RangerPathResourceMatcher.isRecursiveWildCardMatch(str, 
value, levelSeparatorChar, IOCase.INSENSITIVE);
+       }
+       int getPriority() { return 8;}
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java
----------------------------------------------------------------------
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 e4d3ce5..c1b3404 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
@@ -32,4 +32,5 @@ public interface RangerResourceMatcher {
        boolean isMatch(String resource);
 
        boolean isCompleteMatch(String resource);
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..39eb339
--- /dev/null
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.plugin.resourcematcher;
+
+abstract class ResourceMatcher implements Comparable<ResourceMatcher> {
+    protected final String value;
+
+    ResourceMatcher(String value) { this.value = value; }
+
+    abstract boolean isMatch(String str);
+    abstract int getPriority();
+
+    boolean isMatchAny() { return value != null && value.length() == 0; }
+
+    @Override
+    public int compareTo(ResourceMatcher other) { return 
Integer.compare(getPriority(), other.getPriority()); }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName() + "(" + this.value + ")";
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
index ab5626e..f825f40 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
@@ -160,6 +160,13 @@ public class TestPolicyEngine {
        }
 
        @Test
+       public void testPolicyEngine_hdfs_resourcespec() {
+               String[] hdfsTestResourceFiles = { 
"/policyengine/test_policyengine_hdfs_resourcespec.json" };
+
+               runTestsFromResourceFiles(hdfsTestResourceFiles);
+       }
+
+       @Test
        public void testPolicyEngine_hdfs() {
                String[] hdfsTestResourceFiles = { 
"/policyengine/test_policyengine_hdfs.json" };
 

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java
----------------------------------------------------------------------
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 5576a09..d2fb62c 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
@@ -20,6 +20,7 @@
 package org.apache.ranger.plugin.resourcematcher;
 
 import com.google.common.collect.Lists;
+import org.apache.ranger.plugin.model.RangerPolicy;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -60,14 +61,16 @@ public class RangerDefaultResourceMatcherTest {
 
     static class MatcherWrapper extends RangerDefaultResourceMatcher {
         MatcherWrapper(String policyValue, boolean exclude) {
-            this.policyValues = Lists.newArrayList(policyValue);
-            if (WILDCARD_ASTERISK.equals(policyValue)) {
-                this.isMatchAny = true;
-            }
+            RangerPolicy.RangerPolicyResource policyResource = new 
RangerPolicy.RangerPolicyResource();
+            policyResource.setIsExcludes(exclude);
+            policyResource.setValues(Lists.newArrayList(policyValue));
+            setPolicyResource(policyResource);
+
             if (policyValue.contains(WILDCARD_ASTERISK)) {
                 this.optWildCard = true;
             }
-            this.policyIsExcludes = exclude;
+            this.optIgnoreCase = false;
+            init();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..c9d207f
--- /dev/null
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.plugin.resourcematcher;
+
+import com.google.common.collect.Lists;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class RangerPathResourceMatcherTest {
+
+    Object[][] data = {
+            // { resource, policy, optWildcard, recursive, result
+            { "/app/hive/test.db",    "/",                 true, false, false 
},
+            { "/app/hive/test.db",    "/",                 true, true,  true },
+            { "/app/hive/test.db",    "/*",                true, false, true },
+            { "/app/hbase/test.tbl",  "/*",                true, false, true },
+            { "/app/hive/test.db",    "/app",              true, false, false 
},
+            { "/app/hive/test.db",    "/app/",             true, false, false 
},
+            { "/app/hive/test.db",    "/app/",             true, true,  true },
+            { "/app/hive/test.db",    "/app/*",            true, false, true },
+            { "/app/hbase/test.tbl",  "/app/*",            true, false, true },
+            { "/app/hive/test.db",    "/app/hive/*",       true, false, true },
+            { "/app/hbase/test.tbl",  "/app/hive/*",       true, false, false 
},
+            { "/app/hive/test.db",    "/app/hive/test*",   true, false, true },
+            { "/app/hbase/test.tbl",  "/app/hive/test*",   true, false, false 
},
+            { "/app/hive/test.db",    "/app/hive/test.db", true, false, true },
+            { "/app/hbase/test.tbl",  "/app/hive/test.db", true, false, false 
},
+    };
+
+    @Test
+    public void testIsMatch() throws Exception {
+        for (Object[] row : data) {
+            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];
+
+            MatcherWrapper matcher = new MatcherWrapper(policyValue, 
optWildcard, isRecursive);
+            assertEquals(getMessage(row), result, matcher.isMatch(resource));
+        }
+    }
+
+    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]);
+    }
+
+    static class MatcherWrapper extends RangerPathResourceMatcher {
+        MatcherWrapper(String policyValue, boolean optWildcard, boolean 
isRecursive) {
+            super.optWildCard = optWildcard;
+
+            RangerPolicy.RangerPolicyResource policyResource = new 
RangerPolicy.RangerPolicyResource();
+            policyResource.setIsRecursive(isRecursive);
+            policyResource.setValues(Lists.newArrayList(policyValue));
+            setPolicyResource(policyResource);
+
+            init();
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d79f127c/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_resourcespec.json
----------------------------------------------------------------------
diff --git 
a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_resourcespec.json
 
b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_resourcespec.json
new file mode 100644
index 0000000..da0a629
--- /dev/null
+++ 
b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_resourcespec.json
@@ -0,0 +1,38 @@
+{
+  "serviceName":"hdfsdev",
+
+  "serviceDef":{
+    "name":"hdfs",
+    "id":1,
+    "resources":[
+    
{"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true,
 "ignoreCase":true},"label":"Resource Path","description":"HDFS file or 
directory path"}
+    ],
+    "accessTypes":[
+      {"name":"read","label":"Read"},
+      {"name":"write","label":"Write"},
+      {"name":"execute","label":"Execute"}
+    ],
+    "contextEnrichers": [ ],
+    "policyConditions": [ ]
+  },
+
+  "policies":[
+    {"id":1,"name":"allow-read-to-finance under 
/finance/rest*ricted/","isEnabled":true,"isAuditEnabled":true,
+     
"resources":{"path":{"values":["/finance/rest*ricted/"],"isRecursive":true}},
+     "policyItems":[
+       
{"accesses":[{"type":"read","isAllowed":true}],"users":[],"groups":["finance"],"delegateAdmin":false,
 "conditions":[ ] }
+     ]
+    }
+  ],
+
+  "tests":[
+    {"name":"ALLOW 'read /finance/restricted/tmp/sales.db' for g=finance",
+     "request":{
+      "resource":{"elements":{"path":"/finance/restricted/tmp/sales.db"}},
+      
"accessType":"read","user":"user1","userGroups":["finance"],"requestData":"read 
/finance/restricted/tmp/sales.db"
+     },
+     "result":{"isAudited":true,"isAllowed":true,"policyId":1}
+    }
+  ]
+}
+

Reply via email to