You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2021/09/21 15:13:47 UTC

[ranger] branch ranger-2.2 updated: RANGER-3420: resource matcher updated to support quoted resource names

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

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


The following commit(s) were added to refs/heads/ranger-2.2 by this push:
     new 259f508  RANGER-3420: resource matcher updated to support quoted resource names
259f508 is described below

commit 259f50847f0e8720184362ad25604fe9fe301fd6
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Mon Sep 20 01:16:13 2021 -0700

    RANGER-3420: resource matcher updated to support quoted resource names
    
    (cherry picked from commit 3af1378c0570bafc3618fc75683ec4ee8f4daadd)
---
 .../RangerAbstractResourceMatcher.java             |  118 ++-
 .../plugin/resourcematcher/ResourceMatcher.java    |   11 +
 .../TestDefaultPolicyResourceMatcher.java          |    7 +
 .../test_defaultpolicyresourcematcher_quoted.json  | 1035 ++++++++++++++++++++
 4 files changed, 1161 insertions(+), 10 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 4754539..7841838 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
@@ -40,8 +40,10 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 
 	public final static String WILDCARD_ASTERISK = "*";
 
-	public final static String OPTION_IGNORE_CASE = "ignoreCase";
-	public final static String OPTION_WILD_CARD   = "wildCard";
+	public final static String OPTION_IGNORE_CASE            = "ignoreCase";
+	public final static String OPTION_QUOTED_CASE_SENSITIVE  = "quotedCaseSensitive";
+	public final static String OPTION_QUOTE_CHARS            = "quoteChars";
+	public final static String OPTION_WILD_CARD              = "wildCard";
 	public final static String OPTION_REPLACE_TOKENS         = "replaceTokens";
 	public final static String OPTION_TOKEN_DELIMITER_START  = "tokenDelimiterStart";
 	public final static String OPTION_TOKEN_DELIMITER_END    = "tokenDelimiterEnd";
@@ -52,6 +54,8 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 	protected RangerPolicyResource policyResource;
 
 	protected boolean      optIgnoreCase;
+	protected boolean      optQuotedCaseSensitive;
+	protected String       optQuoteChars = "\"";
 	protected boolean      optWildCard;
 
 	protected List<String> policyValues;
@@ -83,8 +87,10 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 
 		Map<String, String> options = resourceDef != null ? resourceDef.getMatcherOptions() : null;
 
-		optIgnoreCase = getOptionIgnoreCase(options);
-		optWildCard   = getOptionWildCard(options);
+		optIgnoreCase          = getOptionIgnoreCase(options);
+		optQuotedCaseSensitive = getOptionQuotedCaseSensitive(options);
+		optQuoteChars          = getOptionQuoteChars(options);
+		optWildCard            = getOptionWildCard(options);
 
 		policyValues = new ArrayList<>();
 		policyIsExcludes = policyResource != null && policyResource.getIsExcludes();
@@ -143,6 +149,14 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 		return ServiceDefUtil.getBooleanOption(options, OPTION_IGNORE_CASE, true);
 	}
 
+	public static boolean getOptionQuotedCaseSensitive(Map<String, String> options) {
+		return ServiceDefUtil.getBooleanOption(options, OPTION_QUOTED_CASE_SENSITIVE, false);
+	}
+
+	public static String getOptionQuoteChars(Map<String, String> options) {
+		return ServiceDefUtil.getOption(options, OPTION_QUOTE_CHARS, "\"");
+	}
+
 	public static boolean getOptionWildCard(Map<String, String> options) {
 		return ServiceDefUtil.getBooleanOption(options, OPTION_WILD_CARD, true);
 	}
@@ -208,7 +222,7 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 			if(isMatchAny) {
 				ret = StringUtils.isEmpty(resource) || StringUtils.containsOnly(resource, WILDCARD_ASTERISK);
 			} else {
-				ret = optIgnoreCase ? StringUtils.equalsIgnoreCase(resource, policyValue) : StringUtils.equals(resource, policyValue);
+				ret = optIgnoreCase && !(optQuotedCaseSensitive && ResourceMatcher.startsWithAnyChar(resource, optQuoteChars)) ? StringUtils.equalsIgnoreCase(resource, policyValue) : StringUtils.equals(resource, policyValue);
 			}
 
 			if(policyIsExcludes) {
@@ -246,6 +260,8 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 		}
 		sb.append("} ");
 		sb.append("optIgnoreCase={").append(optIgnoreCase).append("} ");
+		sb.append("optQuotedCaseSensitive={").append(optQuotedCaseSensitive).append("} ");
+		sb.append("optQuoteChars={").append(optQuoteChars).append("} ");
 		sb.append("optWildCard={").append(optWildCard).append("} ");
 
 		sb.append("policyValues={");
@@ -346,17 +362,17 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 		}
 
 		if (needWildcardMatch) { // test?, test*a*, test*a*b, *test*a
-			ret = optIgnoreCase ? new CaseInsensitiveWildcardMatcher(policyValue) : new CaseSensitiveWildcardMatcher(policyValue);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue)) : new CaseSensitiveWildcardMatcher(policyValue);
 		} else if (wildcardStartIdx == -1) { // test, testa, testab
-			ret = optIgnoreCase ? new CaseInsensitiveStringMatcher(policyValue) : new CaseSensitiveStringMatcher(policyValue);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStringMatcher(policyValue, optQuoteChars) : new CaseInsensitiveStringMatcher(policyValue)) : new CaseSensitiveStringMatcher(policyValue);
 		} else if (wildcardStartIdx == 0) { // *test, **test, *testa, *testab
 			String matchStr = policyValue.substring(wildcardEndIdx + 1);
-			ret = optIgnoreCase ? new CaseInsensitiveEndsWithMatcher(matchStr) : new CaseSensitiveEndsWithMatcher(matchStr);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveEndsWithMatcher(matchStr, optQuoteChars) : new CaseInsensitiveEndsWithMatcher(matchStr)) : new CaseSensitiveEndsWithMatcher(matchStr);
 		} else if (wildcardEndIdx != (len - 1)) { // test*a, test*ab
-			ret = optIgnoreCase ? new CaseInsensitiveWildcardMatcher(policyValue) : new CaseSensitiveWildcardMatcher(policyValue);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue)) : new CaseSensitiveWildcardMatcher(policyValue);
 		} else { // test*, test**, testa*, testab*
 			String matchStr = policyValue.substring(0, wildcardStartIdx);
-			ret = optIgnoreCase ? new CaseInsensitiveStartsWithMatcher(matchStr) : new CaseSensitiveStartsWithMatcher(matchStr);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStartsWithMatcher(matchStr, optQuoteChars) : new CaseInsensitiveStartsWithMatcher(matchStr)) : new CaseSensitiveStartsWithMatcher(matchStr);
 		}
 
 		if(optReplaceTokens) {
@@ -389,6 +405,27 @@ final class CaseInsensitiveStringMatcher extends ResourceMatcher {
 	int getPriority() {return 2 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
 }
 
+final class QuotedCaseSensitiveStringMatcher extends ResourceMatcher {
+	private final String quoteChars;
+
+	QuotedCaseSensitiveStringMatcher(String value, String quoteChars) {
+		super(value);
+
+		this.quoteChars = quoteChars;
+	}
+
+	@Override
+	boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
+		if (startsWithAnyChar(resourceValue, quoteChars)) {
+			return StringUtils.equals(resourceValue, getExpandedValue(evalContext));
+		} else {
+			return StringUtils.equalsIgnoreCase(resourceValue, getExpandedValue(evalContext));
+		}
+	}
+
+	int getPriority() {return 2 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
+}
+
 final class CaseSensitiveStartsWithMatcher extends ResourceMatcher {
 	CaseSensitiveStartsWithMatcher(String value) {
 		super(value);
@@ -411,6 +448,27 @@ final class CaseInsensitiveStartsWithMatcher extends ResourceMatcher {
 	int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
 }
 
+final class QuotedCaseSensitiveStartsWithMatcher extends ResourceMatcher {
+	private final String quoteChars;
+
+	QuotedCaseSensitiveStartsWithMatcher(String value, String quoteChars) {
+		super(value);
+
+		this.quoteChars = quoteChars;
+	}
+
+	@Override
+	boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
+		if (startsWithAnyChar(resourceValue, quoteChars)) {
+			return StringUtils.startsWith(resourceValue, getExpandedValue(evalContext));
+		} else {
+			return StringUtils.startsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
+		}
+	}
+
+	int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
+}
+
 final class CaseSensitiveEndsWithMatcher extends ResourceMatcher {
 	CaseSensitiveEndsWithMatcher(String value) {
 		super(value);
@@ -435,6 +493,27 @@ final class CaseInsensitiveEndsWithMatcher extends ResourceMatcher {
 	int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
 }
 
+final class QuotedCaseSensitiveEndsWithMatcher extends ResourceMatcher {
+	private final String quoteChars;
+
+	QuotedCaseSensitiveEndsWithMatcher(String value, String quoteChars) {
+		super(value);
+
+		this.quoteChars = quoteChars;
+	}
+
+	@Override
+	boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
+		if (startsWithAnyChar(resourceValue, quoteChars)) {
+			return StringUtils.endsWith(resourceValue, getExpandedValue(evalContext));
+		} else {
+			return StringUtils.endsWithIgnoreCase(resourceValue, getExpandedValue(evalContext));
+		}
+	}
+
+	int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
+}
+
 final class CaseSensitiveWildcardMatcher extends ResourceMatcher {
 	CaseSensitiveWildcardMatcher(String value) {
 		super(value);
@@ -460,6 +539,25 @@ final class CaseInsensitiveWildcardMatcher extends ResourceMatcher {
 	int getPriority() {return 6 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
 }
 
+final class QuotedCaseSensitiveWildcardMatcher extends ResourceMatcher {
+	private final String quoteChars;
+
+	QuotedCaseSensitiveWildcardMatcher(String value, String quoteChars) {
+		super(value);
+
+		this.quoteChars = quoteChars;
+	}
+
+	@Override
+	boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
+		IOCase caseSensitivity = startsWithAnyChar(resourceValue, quoteChars) ? IOCase.SENSITIVE : IOCase.INSENSITIVE;
+
+		return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), caseSensitivity);
+	}
+
+	int getPriority() {return 6 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); }
+}
+
 final class ResourceMatcherWrapper {
 	private final boolean needsDynamicEval;
 	private final List<ResourceMatcher> resourceMatchers;
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 35856a9..6d8e293 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.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.plugin.util.StringTokenReplacer;
@@ -92,6 +93,16 @@ abstract class ResourceMatcher {
         return ret;
     }
 
+    public static boolean startsWithAnyChar(String value, String startChars) {
+        boolean ret = false;
+
+        if (value != null && value.length() > 0 && startChars != null) {
+            ret = StringUtils.contains(startChars, value.charAt(0));
+        }
+
+        return ret;
+    }
+
     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/TestDefaultPolicyResourceMatcher.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestDefaultPolicyResourceMatcher.java
index 8ea7105..2b432a1 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestDefaultPolicyResourceMatcher.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestDefaultPolicyResourceMatcher.java
@@ -97,6 +97,13 @@ public class TestDefaultPolicyResourceMatcher {
 	}
 
 	@Test
+	public void testDefaultPolicyResourceMatcherQuoted() throws Exception {
+		String[] tests = {"/resourcematcher/test_defaultpolicyresourcematcher_quoted.json"};
+
+		runTestsFromResourceFiles(tests, null);
+	}
+
+	@Test
 	public void testDefaultPolicyResourceMatcher_ResourceSpecific() throws Exception {
 		String[] tests = { "/resourcematcher/test_defaultpolicyresourcematcher.json" };
 
diff --git a/agents-common/src/test/resources/resourcematcher/test_defaultpolicyresourcematcher_quoted.json b/agents-common/src/test/resources/resourcematcher/test_defaultpolicyresourcematcher_quoted.json
new file mode 100644
index 0000000..cebfb72
--- /dev/null
+++ b/agents-common/src/test/resources/resourcematcher/test_defaultpolicyresourcematcher_quoted.json
@@ -0,0 +1,1035 @@
+{
+  "serviceDef": {
+    "name": "quoted",
+    "id": 3,
+    "resources": [
+      {
+        "name": "database",
+        "level": 1,
+        "mandatory": true,
+        "lookupSupported": true,
+        "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+        "matcherOptions": {
+          "wildCard": true,
+          "ignoreCase": true,
+          "quotedCaseSensitive": true,
+          "quoteChars": "\""
+        },
+        "label": "Quoted Database",
+        "description": "Quoted Database",
+        "isExcludes": true
+      },
+      {
+        "name": "table",
+        "level": 2,
+        "parent": "database",
+        "mandatory": true,
+        "lookupSupported": true,
+        "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+        "matcherOptions": {
+          "wildCard": true,
+          "ignoreCase": true,
+          "quotedCaseSensitive": true,
+          "quoteChars": "\""
+        },
+        "label": "Quoted Table",
+        "description": "Quoted Table",
+        "isExcludes": true
+      },
+      {
+        "name": "column",
+        "level": 3,
+        "parent": "table",
+        "mandatory": true,
+        "lookupSupported": true,
+        "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+        "matcherOptions": {
+          "wildCard": true,
+          "ignoreCase": true,
+          "quotedCaseSensitive": true,
+          "quoteChars": "\""
+        },
+        "label": "Quoted Column",
+        "description": "Quoted Column",
+        "isExcludes": true
+      }
+    ],
+    "accessTypes": [
+      {
+        "name": "select",
+        "label": "Select"
+      },
+      {
+        "name": "update",
+        "label": "Update"
+      },
+      {
+        "name": "create",
+        "label": "Create"
+      },
+      {
+        "name": "drop",
+        "label": "Drop"
+      },
+      {
+        "name": "alter",
+        "label": "Alter"
+      },
+      {
+        "name": "index",
+        "label": "Index"
+      },
+      {
+        "name": "lock",
+        "label": "Lock"
+      },
+      {
+        "name": "all",
+        "label": "All"
+      }
+    ]
+  },
+  "testCases": [
+    {
+      "name": "Quoted values in policy: database=\"Test*:table=\"teSt*:column=*",
+      "policyResources": {
+        "database": {"values": [ "\"Test*"] },
+        "table": {"values": ["\"teSt*"]},
+        "column": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact '\"TestDB.\"teStTbl.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for exact '\"TestDB.\"teStTbl.\"ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"\"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "No MATCH for unquoted 'TestDB.\"teStTbl.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "TestDB", "table":"\"teStTbl", "column":"SSN"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "No MATCH for unquoted 'TestDB.teStTbl.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "\"TestDB", "table":"teStTbl", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards '\"TestDB.\"teStTbl'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "\"TestDB","table": "\"teStTbl"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards '\"TestDB'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "\"TestDB"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "Quoted values in resource: database=Test*:table=teSt*:column=*",
+      "policyResources": {
+        "database": {"values": [ "Test*"] },
+        "table": {"values": ["teSt*"]},
+        "column": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "No MATCH for quoted '\"TestDB.\"teStTbl.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "No MATCH for exact '\"TestDB.\"teStTbl.\"ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"\"ssn"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "MATCH for unquoted 'testdb.testtbl.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "testdb", "table":"testtbl", "column":"SSN"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for unquoted 'TestDB.teStTbl.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "TestDB", "table":"teStTbl", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'TestDB.teStTbl'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "TestDB","table": "teStTbl"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'TestDB'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "TestDB"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=*, isExclude=true:table=*, isExcludes=true",
+      "policyResources": {
+        "database": {"values": ["*"], "isExcludes": true},
+        "table": {"values": ["*"], "isExcludes": true}
+      },
+      "tests": [
+        {
+          "name": "NO MATCH for invalid resource level",
+          "type": "anyMatch",
+          "resource" : {
+            "elements" : { "database":"finance", "invalid-resource-name":"any"}
+          },
+          "evalContext": {},
+          "result" : false
+        }
+      ,
+        {
+          "name": "No MATCH for parent 'finance.tax.ssn'",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "No MATCH for exact 'finance:tax'",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "No MATCH for parent with wildcards 'finance'",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "No MATCH for any ''",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ]
+    },
+    {
+      "name": "database=*:table=tax, isExcludes=true",
+      "policyResources": {
+        "database": {"values": ["*"]},
+        "table": {"values": ["tax"], "isExcludes": true}
+      },
+      "tests": [
+        {
+          "name": "NO MATCH for invalid resource level",
+          "type": "anyMatch",
+          "resource" : {
+            "elements" : { "database":"finance", "invalid-resource-name":"any"}
+          },
+          "evalContext": {},
+          "result" : false
+        }
+      ,
+        {
+          "name": "No MATCH for parent 'finance.tax.ssn'",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "No MATCH for exact 'finance:tax'",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child ''",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=*:table=*",
+      "policyResources": {
+        "database": {"values": ["*"]},
+        "table": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "NO MATCH for invalid resource level",
+          "type": "anyMatch",
+          "resource" : {
+            "elements" : { "database":"finance", "invalid-resource-name":"any"}
+          },
+          "evalContext": {},
+          "result" : false
+        }
+        ,
+        {
+          "name": "MATCH for parent 'finance.tax.ssn'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for exact 'finance:tax'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+        ,
+        {
+          "name": "MATCH for parent with wildcards ''",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=finance:table=tax",
+      "policyResources": {
+        "database": {"values": ["finance"]},
+        "table": {"values": ["tax"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for parent 'finance.tax.ssn'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for exact 'finance:tax'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child ''",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=*:table=tax",
+      "policyResources": {
+        "database": {"values": ["*"]},
+        "table": {"values": ["tax"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for parent 'finance.tax.ssn'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for exact 'finance:tax'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child ''",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=finance:table=*",
+      "policyResources": {
+        "database": {"values": ["finance"]},
+        "table": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for parent 'finance.tax.ssn'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for exact 'finance:tax'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child ''",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=finance",
+      "policyResources": {
+        "database": {"values": ["finance"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for parent 'finance.tax.ssn'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent 'finance:tax'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for exact 'finance'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child ''",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=*",
+      "policyResources": {
+        "database": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "NO MATCH for invalid resource level",
+          "type": "anyMatch",
+          "resource" : {
+            "elements" : { "database":"finance", "invalid-resource-name":"any"}
+          },
+          "evalContext": {},
+          "result" : false
+        }
+      ,
+        {
+          "name": "MATCH for parent 'finance.tax.ssn'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent 'finance:tax'",
+          "type": "ancestorMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for exact 'finance'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards ''",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    },
+    {
+      "name": "database=*:table=*:column=*",
+      "policyResources": {
+        "database": {"values": [ "*"] },
+        "table": {"values": ["*"]},
+        "column": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance:tax'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards ''",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "NO MATCH for any 'finance::ssn'",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {
+              "database": "finance","column":"ssn"
+            }
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ]
+    }
+  ,
+    {
+      "name": "database=finance:table=tax:column=ssn",
+      "policyResources": {
+        "database": {"values": [ "finance"] },
+        "table": {"values": ["tax"]},
+        "column": {"values": ["ssn"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance:tax'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child ''",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    }
+    ,
+
+    {
+      "name": "database=*:table=*:column=ssn",
+      "policyResources": {
+        "database": {"values": [ "*"] },
+        "table": {"values": ["*"]},
+        "column": {"values": ["ssn"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance:tax'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    }
+  ,
+
+    {
+      "name": "database=finance:table=*:column=*",
+      "policyResources": {
+        "database": {"values": [ "finance"] },
+        "table": {"values": ["*"]},
+        "column": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance:tax'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    }
+  ,
+
+    {
+      "name": "database=*:table=tax:column=*",
+      "policyResources": {
+        "database": {"values": [ "*"] },
+        "table": {"values": ["tax"]},
+        "column": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance:tax'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    }
+  ,
+    {
+      "name": "database=*:table=tax:column=ssn",
+      "policyResources": {
+        "database": {"values": [ "*"] },
+        "table": {"values": ["tax"]},
+        "column": {"values": ["ssn"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance:tax'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    }
+  ,
+    {
+      "name": "database=finance:table=*:column=ssn",
+      "policyResources": {
+        "database": {"values": [ "finance"] },
+        "table": {"values": ["*"]},
+        "column": {"values": ["ssn"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance:tax'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    }
+  ,
+    {
+      "name": "database=finance:table=tax:column=*",
+      "policyResources": {
+        "database": {"values": [ "finance"] },
+        "table": {"values": ["tax"]},
+        "column": {"values": ["*"]}
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact 'finance.tax.ssn'",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {"database": "finance", "table":"tax", "column":"ssn"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for parent with wildcards 'finance:tax'",
+          "type": "selfAndAllDescendants",
+          "resource": {
+            "elements": {"database": "finance","table": "tax"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ,
+        {
+          "name": "MATCH for child 'finance'",
+          "type": "descendantMatch",
+          "resource": {
+            "elements": {"database": "finance"}
+          },
+          "evalContext": {},
+          "result": true
+        }
+      ]
+    }
+  ,
+    {
+      "name": "empty policyresources",
+      "policyResources": {
+      },
+      "tests": [
+        {
+          "name": "MATCH for exact ''",
+          "type": "exactMatch",
+          "resource": {
+            "elements": {}
+          },
+          "evalContext": {},
+          "result": true
+        }
+        ,
+        {
+          "name": "MATCH for parent 'default'",
+          "type": "anyMatch",
+          "resource": {
+            "elements": {"database": "default"}
+          },
+          "evalContext": {},
+          "result": false
+        }
+      ]
+    }
+  ]
+}