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 2022/01/04 22:59:13 UTC

[ranger] branch master updated: RANGER-3567: support request expressions in policy resources

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 56bac60  RANGER-3567: support request expressions in policy resources
56bac60 is described below

commit 56bac60551f77f582bdb9693705a8c8682b17b56
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Sun Dec 26 23:53:13 2021 -0800

    RANGER-3567: support request expressions in policy resources
---
 .../policyengine/RangerAccessRequestImpl.java      |  19 +++-
 .../policyengine/RangerRequestScriptEvaluator.java |   8 +-
 .../plugin/policyengine/RangerResourceTrie.java    |   6 ++
 .../RangerAbstractResourceMatcher.java             |  79 +++++++-------
 .../resourcematcher/RangerPathResourceMatcher.java |  36 +++----
 .../resourcematcher/RangerURLResourceMatcher.java  |  28 ++---
 .../plugin/resourcematcher/ResourceMatcher.java    |  38 +++++--
 .../plugin/util/RangerAccessRequestUtil.java       |  29 ++++++
 .../plugin/util/RangerRequestExprResolver.java     |   1 +
 .../ranger/plugin/util/StringTokenReplacer.java    |   7 +-
 .../plugin/policyengine/TestPolicyEngine.java      |  20 ++++
 ...policyengine_resource_with_req_expressions.json | 113 +++++++++++++++++++++
 12 files changed, 298 insertions(+), 86 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java
index 8bfc161..a22b8b6 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java
@@ -30,6 +30,7 @@ import java.util.Set;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
+import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 
 public class RangerAccessRequestImpl implements RangerAccessRequest {
 	private static final Logger LOG = Logger.getLogger(RangerAccessRequestImpl.class);
@@ -256,7 +257,17 @@ public class RangerAccessRequestImpl implements RangerAccessRequest {
 	public void setResourceMatchingScope(ResourceMatchingScope scope) { this.resourceMatchingScope = scope; }
 
 	public void setContext(Map<String, Object> context) {
-		this.context = (context == null) ? new HashMap<String, Object>() : context;
+		if (context == null) {
+			this.context = new HashMap<>();
+		} else {
+			this.context = context;
+		}
+
+		RangerAccessRequest current = RangerAccessRequestUtil.getRequestFromContext(this.context);
+
+		if (current == null) {
+			RangerAccessRequestUtil.setRequestInContext(this);
+		}
 	}
 
 	public void extractAndSetClientIPAddress(boolean useForwardedIPAddress, String[]trustedProxyAddresses) {
@@ -344,7 +355,11 @@ public class RangerAccessRequestImpl implements RangerAccessRequest {
 		sb.append("context={");
 		if(context != null) {
 			for(Map.Entry<String, Object> e : context.entrySet()) {
-				sb.append(e.getKey()).append("={").append(e.getValue()).append("} ");
+				Object val = e.getValue();
+
+				if (!(val instanceof RangerAccessRequest)) { // to avoid recursive calls
+					sb.append(e.getKey()).append("={").append(val).append("} ");
+				}
 			}
 		}
 		sb.append("} ");
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
index d99a7d5..6f12b1b 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
@@ -324,10 +324,6 @@ public final class RangerRequestScriptEvaluator {
 		return ret;
 	}
 
-	public Map<String, Object> getRequestContext() {
-		return accessRequest.getContext();
-	}
-
 	public String getRequestContextAttribute(String attributeName) {
 		String ret = null;
 
@@ -604,6 +600,10 @@ public final class RangerRequestScriptEvaluator {
 		return ret;
 	}
 
+	private Map<String, Object> getRequestContext() {
+		return accessRequest.getContext();
+	}
+
 	private Set<RangerTagForEval> getAllTags() {
 		Set<RangerTagForEval> ret = RangerAccessRequestUtil.getRequestTagsFromContext(accessRequest.getContext());
 		if(ret == null) {
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java
index 80ed569..6e27524 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java
@@ -31,6 +31,7 @@ import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceEvalua
 import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher;
 import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher;
 import org.apache.ranger.plugin.util.RangerPerfTracer;
+import org.apache.ranger.plugin.util.RangerRequestExprResolver;
 import org.apache.ranger.plugin.util.ServiceDefUtil;
 
 import java.util.ArrayList;
@@ -127,6 +128,7 @@ public class RangerResourceTrie<T extends RangerPolicyResourceEvaluator> {
 
         Map<String, String> matcherOptions           = resourceDef.getMatcherOptions();
         boolean             optReplaceTokens         = RangerAbstractResourceMatcher.getOptionReplaceTokens(matcherOptions);
+        boolean             optReplaceReqExpressions = RangerAbstractResourceMatcher.getOptionReplaceReqExpressions(matcherOptions);
         String              tokenReplaceSpecialChars = "";
 
         if(optReplaceTokens) {
@@ -139,6 +141,10 @@ public class RangerResourceTrie<T extends RangerPolicyResourceEvaluator> {
             tokenReplaceSpecialChars += delimiterEscape;
         }
 
+        if (optReplaceReqExpressions) {
+            tokenReplaceSpecialChars += RangerRequestExprResolver.EXPRESSION_START.charAt(0);
+        }
+
         this.resourceDef             = resourceDef;
         this.optIgnoreCase           = RangerAbstractResourceMatcher.getOptionIgnoreCase(matcherOptions);
         this.optWildcard             = RangerAbstractResourceMatcher.getOptionWildCard(matcherOptions);
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 7841838..beb9bc7 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,15 +40,16 @@ 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_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";
-	public final static String OPTION_TOKEN_DELIMITER_ESCAPE = "tokenDelimiterEscape";
-	public final static String OPTION_TOKEN_DELIMITER_PREFIX = "tokenDelimiterPrefix";
+	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";
+	public final static String OPTION_TOKEN_DELIMITER_ESCAPE  = "tokenDelimiterEscape";
+	public final static String OPTION_TOKEN_DELIMITER_PREFIX  = "tokenDelimiterPrefix";
+	public final static String OPTION_REPLACE_REQ_EXPRESSIONS = "replaceReqExpressions";
 
 	protected RangerResourceDef    resourceDef;
 	protected RangerPolicyResource policyResource;
@@ -181,6 +182,12 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 		return ServiceDefUtil.getOption(options, OPTION_TOKEN_DELIMITER_PREFIX, "");
 	}
 
+	public static boolean getOptionReplaceReqExpressions(Map<String, String> options) {
+		return ServiceDefUtil.getBooleanOption(options, OPTION_REPLACE_REQ_EXPRESSIONS, true);
+	}
+
+	protected Map<String, String> getOptions() { return resourceDef != null ? resourceDef.getMatcherOptions() : null; }
+
 	protected ResourceMatcherWrapper buildResourceMatchers() {
 		List<ResourceMatcher> resourceMatchers = new ArrayList<>();
 		boolean needsDynamicEval = false;
@@ -362,17 +369,17 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 		}
 
 		if (needWildcardMatch) { // test?, test*a*, test*a*b, *test*a
-			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue)) : new CaseSensitiveWildcardMatcher(policyValue);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, getOptions(), optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue, getOptions())) : new CaseSensitiveWildcardMatcher(policyValue, getOptions());
 		} else if (wildcardStartIdx == -1) { // test, testa, testab
-			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStringMatcher(policyValue, optQuoteChars) : new CaseInsensitiveStringMatcher(policyValue)) : new CaseSensitiveStringMatcher(policyValue);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStringMatcher(policyValue, getOptions(), optQuoteChars) : new CaseInsensitiveStringMatcher(policyValue, getOptions())) : new CaseSensitiveStringMatcher(policyValue, getOptions());
 		} else if (wildcardStartIdx == 0) { // *test, **test, *testa, *testab
 			String matchStr = policyValue.substring(wildcardEndIdx + 1);
-			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveEndsWithMatcher(matchStr, optQuoteChars) : new CaseInsensitiveEndsWithMatcher(matchStr)) : new CaseSensitiveEndsWithMatcher(matchStr);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveEndsWithMatcher(matchStr, getOptions(), optQuoteChars) : new CaseInsensitiveEndsWithMatcher(matchStr, getOptions())) : new CaseSensitiveEndsWithMatcher(matchStr, getOptions());
 		} else if (wildcardEndIdx != (len - 1)) { // test*a, test*ab
-			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue)) : new CaseSensitiveWildcardMatcher(policyValue);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, getOptions(), optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue, getOptions())) : new CaseSensitiveWildcardMatcher(policyValue, getOptions());
 		} else { // test*, test**, testa*, testab*
 			String matchStr = policyValue.substring(0, wildcardStartIdx);
-			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStartsWithMatcher(matchStr, optQuoteChars) : new CaseInsensitiveStartsWithMatcher(matchStr)) : new CaseSensitiveStartsWithMatcher(matchStr);
+			ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStartsWithMatcher(matchStr, getOptions(), optQuoteChars) : new CaseInsensitiveStartsWithMatcher(matchStr, getOptions())) : new CaseSensitiveStartsWithMatcher(matchStr, getOptions());
 		}
 
 		if(optReplaceTokens) {
@@ -384,8 +391,8 @@ public abstract class RangerAbstractResourceMatcher implements RangerResourceMat
 }
 
 final class CaseSensitiveStringMatcher extends ResourceMatcher {
-	CaseSensitiveStringMatcher(String value) {
-		super(value);
+	CaseSensitiveStringMatcher(String value, Map<String, String> options) {
+		super(value, options);
 	}
 
 	@Override
@@ -396,7 +403,7 @@ final class CaseSensitiveStringMatcher extends ResourceMatcher {
 }
 
 final class CaseInsensitiveStringMatcher extends ResourceMatcher {
-	CaseInsensitiveStringMatcher(String value) { super(value); }
+	CaseInsensitiveStringMatcher(String value, Map<String, String> options) { super(value, options); }
 
 	@Override
 	boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
@@ -408,8 +415,8 @@ final class CaseInsensitiveStringMatcher extends ResourceMatcher {
 final class QuotedCaseSensitiveStringMatcher extends ResourceMatcher {
 	private final String quoteChars;
 
-	QuotedCaseSensitiveStringMatcher(String value, String quoteChars) {
-		super(value);
+	QuotedCaseSensitiveStringMatcher(String value, Map<String, String> options, String quoteChars) {
+		super(value, options);
 
 		this.quoteChars = quoteChars;
 	}
@@ -427,8 +434,8 @@ final class QuotedCaseSensitiveStringMatcher extends ResourceMatcher {
 }
 
 final class CaseSensitiveStartsWithMatcher extends ResourceMatcher {
-	CaseSensitiveStartsWithMatcher(String value) {
-		super(value);
+	CaseSensitiveStartsWithMatcher(String value, Map<String, String> options) {
+		super(value, options);
 	}
 
 	@Override
@@ -439,7 +446,7 @@ final class CaseSensitiveStartsWithMatcher extends ResourceMatcher {
 }
 
 final class CaseInsensitiveStartsWithMatcher extends ResourceMatcher {
-	CaseInsensitiveStartsWithMatcher(String value) { super(value); }
+	CaseInsensitiveStartsWithMatcher(String value, Map<String, String> options) { super(value, options); }
 
 	@Override
 	boolean isMatch(String resourceValue, Map<String, Object> evalContext) {
@@ -451,8 +458,8 @@ final class CaseInsensitiveStartsWithMatcher extends ResourceMatcher {
 final class QuotedCaseSensitiveStartsWithMatcher extends ResourceMatcher {
 	private final String quoteChars;
 
-	QuotedCaseSensitiveStartsWithMatcher(String value, String quoteChars) {
-		super(value);
+	QuotedCaseSensitiveStartsWithMatcher(String value, Map<String, String> options, String quoteChars) {
+		super(value, options);
 
 		this.quoteChars = quoteChars;
 	}
@@ -470,8 +477,8 @@ final class QuotedCaseSensitiveStartsWithMatcher extends ResourceMatcher {
 }
 
 final class CaseSensitiveEndsWithMatcher extends ResourceMatcher {
-	CaseSensitiveEndsWithMatcher(String value) {
-		super(value);
+	CaseSensitiveEndsWithMatcher(String value, Map<String, String> options) {
+		super(value, options);
 	}
 
 	@Override
@@ -482,8 +489,8 @@ final class CaseSensitiveEndsWithMatcher extends ResourceMatcher {
 }
 
 final class CaseInsensitiveEndsWithMatcher extends ResourceMatcher {
-	CaseInsensitiveEndsWithMatcher(String value) {
-		super(value);
+	CaseInsensitiveEndsWithMatcher(String value, Map<String, String> options) {
+		super(value, options);
 	}
 
 	@Override
@@ -496,8 +503,8 @@ final class CaseInsensitiveEndsWithMatcher extends ResourceMatcher {
 final class QuotedCaseSensitiveEndsWithMatcher extends ResourceMatcher {
 	private final String quoteChars;
 
-	QuotedCaseSensitiveEndsWithMatcher(String value, String quoteChars) {
-		super(value);
+	QuotedCaseSensitiveEndsWithMatcher(String value, Map<String, String> options, String quoteChars) {
+		super(value, options);
 
 		this.quoteChars = quoteChars;
 	}
@@ -515,8 +522,8 @@ final class QuotedCaseSensitiveEndsWithMatcher extends ResourceMatcher {
 }
 
 final class CaseSensitiveWildcardMatcher extends ResourceMatcher {
-	CaseSensitiveWildcardMatcher(String value) {
-		super(value);
+	CaseSensitiveWildcardMatcher(String value, Map<String, String> options) {
+		super(value, options);
 	}
 
 	@Override
@@ -528,8 +535,8 @@ final class CaseSensitiveWildcardMatcher extends ResourceMatcher {
 
 
 final class CaseInsensitiveWildcardMatcher extends ResourceMatcher {
-	CaseInsensitiveWildcardMatcher(String value) {
-		super(value);
+	CaseInsensitiveWildcardMatcher(String value, Map<String, String> options) {
+		super(value, options);
 	}
 
 	@Override
@@ -542,8 +549,8 @@ final class CaseInsensitiveWildcardMatcher extends ResourceMatcher {
 final class QuotedCaseSensitiveWildcardMatcher extends ResourceMatcher {
 	private final String quoteChars;
 
-	QuotedCaseSensitiveWildcardMatcher(String value, String quoteChars) {
-		super(value);
+	QuotedCaseSensitiveWildcardMatcher(String value, Map<String, String> options, String quoteChars) {
+		super(value, options);
 
 		this.quoteChars = quoteChars;
 	}
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 43297d6..46e10f2 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
@@ -111,7 +111,7 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 
 		// To ensure that when policyValue is single '*', ResourceMatcher created here returns true for isMatchAny()
 		if (optWildCard && WILDCARD_ASTERISK.equals(policyValue)) {
-			return new CaseInsensitiveStringMatcher("");
+			return new CaseInsensitiveStringMatcher("", getOptions());
 		}
 
 		boolean isWildcardPresent = false;
@@ -130,9 +130,9 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 		final ResourceMatcher ret;
 
 		if (isWildcardPresent) {
-			ret = new RecursiveWildcardResourceMatcher(policyValue, pathSeparatorChar, optIgnoreCase, RangerPathResourceMatcher::isRecursiveWildCardMatch, optIgnoreCase ? 8 : 7);
+			ret = new RecursiveWildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, RangerPathResourceMatcher::isRecursiveWildCardMatch, optIgnoreCase ? 8 : 7);
 		} else {
-			ret = new RecursivePathResourceMatcher(policyValue, pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith, optIgnoreCase ? 8 : 7);
+			ret = new RecursivePathResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith, optIgnoreCase ? 8 : 7);
 		}
 
 		if (optReplaceTokens) {
@@ -233,17 +233,17 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 		}
 
 		if (needWildcardMatch) { // test?, test*a*, test*a*b, *test*a
-			ret = new WildcardResourceMatcher(policyValue, pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6);
+			ret = new WildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6);
 		} else if (wildcardStartIdx == -1) { // test, testa, testab
-			ret = new StringResourceMatcher(policyValue, pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, optIgnoreCase ? 2 : 1);
+			ret = new StringResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, optIgnoreCase ? 2 : 1);
 		} else if (wildcardStartIdx == 0) { // *test, **test, *testa, *testab
 			String matchStr = policyValue.substring(wildcardEndIdx + 1);
-			ret = new StringResourceMatcher(matchStr, pathSeparatorChar, optIgnoreCase ? StringUtils::endsWithIgnoreCase : StringUtils::endsWith, optIgnoreCase ? 4 : 3);
+			ret = new StringResourceMatcher(matchStr, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::endsWithIgnoreCase : StringUtils::endsWith, optIgnoreCase ? 4 : 3);
 		} else if (wildcardEndIdx != (len - 1)) { // test*a, test*ab
-			ret = new WildcardResourceMatcher(policyValue, pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6);
+			ret = new WildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6);
 		} else { // test*, test**, testa*, testab*
 			String matchStr = policyValue.substring(0, wildcardStartIdx);
-			ret = new StringResourceMatcher(matchStr, pathSeparatorChar, optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith, optIgnoreCase ? 4 : 3);
+			ret = new StringResourceMatcher(matchStr, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith, optIgnoreCase ? 4 : 3);
 		}
 
 		if (optReplaceTokens) {
@@ -265,8 +265,8 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 		final char    pathSeparatorChar;
 		final int     priority;
 
-		PathResourceMatcher(String value, char pathSeparatorChar, int priority) {
-			super(value);
+		PathResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, int priority) {
+			super(value, options);
 			this.pathSeparatorChar    = pathSeparatorChar;
 			this.priority             = priority;
 		}
@@ -277,8 +277,8 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 
 	static class StringResourceMatcher extends PathResourceMatcher {
 		final BiFunction<String, String, Boolean> function;
-		StringResourceMatcher(String value, char pathSeparatorChar, BiFunction<String, String, Boolean> function, int priority) {
-			super(value, pathSeparatorChar, priority);
+		StringResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, BiFunction<String, String, Boolean> function, int priority) {
+			super(value, options, pathSeparatorChar, priority);
 			this.function = function;
 		}
 		@Override
@@ -313,8 +313,8 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 		final TriFunction<String, String, IOCase, Boolean> function;
 		final IOCase ioCase;
 
-		WildcardResourceMatcher(String value, char pathSeparatorChar, boolean optIgnoreCase, TriFunction<String, String, IOCase, Boolean> function, int priority) {
-			super(value, pathSeparatorChar, priority);
+		WildcardResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean optIgnoreCase, TriFunction<String, String, IOCase, Boolean> function, int priority) {
+			super(value, options, pathSeparatorChar, priority);
 			this.function = function;
 			this.ioCase   = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE;
 		}
@@ -349,8 +349,8 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 		final QuadFunction<String, String, Character, IOCase, Boolean> function;
 		final IOCase ioCase;
 
-		RecursiveWildcardResourceMatcher(String value, char pathSeparatorChar, boolean optIgnoreCase, QuadFunction<String, String, Character, IOCase, Boolean> function, int priority) {
-			super(value, pathSeparatorChar, priority);
+		RecursiveWildcardResourceMatcher(String value, Map<String, String> options, char pathSeparatorChar, boolean optIgnoreCase, QuadFunction<String, String, Character, IOCase, Boolean> function, int priority) {
+			super(value, options, pathSeparatorChar, priority);
 			this.function = function;
 			this.ioCase   = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE;
 		}
@@ -388,8 +388,8 @@ public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher {
 		final BiFunction<String, String, Boolean> primaryFunction;
 		final BiFunction<String, String, Boolean> fallbackFunction;
 
-		RecursivePathResourceMatcher(String value, char pathSeparatorChar, BiFunction<String, String, Boolean> primaryFunction, BiFunction<String, String, Boolean> fallbackFunction, int priority) {
-			super(value, pathSeparatorChar, priority);
+		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);
 			this.primaryFunction    = primaryFunction;
 			this.fallbackFunction   = fallbackFunction;
 		}
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 e21d907..be6e031 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
@@ -110,7 +110,7 @@ public class RangerURLResourceMatcher extends RangerDefaultResourceMatcher {
 
         // To ensure that when policyValue is single '*', ResourceMatcher created here returns true for isMatchAny()
         if (optWildCard && WILDCARD_ASTERISK.equals(policyValue)) {
-            return new CaseInsensitiveStringMatcher("");
+            return new CaseInsensitiveStringMatcher("", getOptions());
         }
 
         boolean isWildcardPresent = false;
@@ -129,10 +129,10 @@ public class RangerURLResourceMatcher extends RangerDefaultResourceMatcher {
         final ResourceMatcher ret;
 
         if (isWildcardPresent) {
-            ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveWildcardMatcher(policyValue, pathSeparatorChar)
-                    : new CaseSensitiveURLRecursiveWildcardMatcher(policyValue, pathSeparatorChar);
+            ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveWildcardMatcher(policyValue, getOptions(), pathSeparatorChar)
+                    : new CaseSensitiveURLRecursiveWildcardMatcher(policyValue, getOptions(), pathSeparatorChar);
         } else {
-            ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveMatcher(policyValue, pathSeparatorChar) : new CaseSensitiveURLRecursiveMatcher(policyValue, pathSeparatorChar);
+            ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveMatcher(policyValue, getOptions(), pathSeparatorChar) : new CaseSensitiveURLRecursiveMatcher(policyValue, getOptions(), pathSeparatorChar);
         }
 
         if (optReplaceTokens) {
@@ -231,8 +231,8 @@ public class RangerURLResourceMatcher extends RangerDefaultResourceMatcher {
 
 final class CaseSensitiveURLRecursiveWildcardMatcher extends ResourceMatcher {
     private final char levelSeparatorChar;
-    CaseSensitiveURLRecursiveWildcardMatcher(String value, char levelSeparatorChar) {
-        super(value);
+    CaseSensitiveURLRecursiveWildcardMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
+        super(value, options);
         this.levelSeparatorChar = levelSeparatorChar;
     }
 
@@ -245,8 +245,8 @@ final class CaseSensitiveURLRecursiveWildcardMatcher extends ResourceMatcher {
 
 final class CaseInsensitiveURLRecursiveWildcardMatcher extends ResourceMatcher {
     private final char levelSeparatorChar;
-    CaseInsensitiveURLRecursiveWildcardMatcher(String value, char levelSeparatorChar) {
-        super(value);
+    CaseInsensitiveURLRecursiveWildcardMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
+        super(value, options);
         this.levelSeparatorChar = levelSeparatorChar;
     }
 
@@ -263,8 +263,8 @@ abstract class RecursiveMatcher extends ResourceMatcher {
     String valueWithoutSeparator;
     String valueWithSeparator;
 
-    RecursiveMatcher(String value, char levelSeparatorChar) {
-        super(value);
+    RecursiveMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
+        super(value, options);
         this.levelSeparatorChar = levelSeparatorChar;
     }
 
@@ -278,8 +278,8 @@ abstract class RecursiveMatcher extends ResourceMatcher {
 }
 
 final class CaseSensitiveURLRecursiveMatcher extends RecursiveMatcher {
-    CaseSensitiveURLRecursiveMatcher(String value, char levelSeparatorChar) {
-        super(value, levelSeparatorChar);
+    CaseSensitiveURLRecursiveMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
+        super(value, options, levelSeparatorChar);
     }
 
     @Override
@@ -310,8 +310,8 @@ final class CaseSensitiveURLRecursiveMatcher extends RecursiveMatcher {
 }
 
 final class CaseInsensitiveURLRecursiveMatcher extends RecursiveMatcher {
-    CaseInsensitiveURLRecursiveMatcher(String value, char levelSeparatorChar) {
-        super(value, levelSeparatorChar);
+    CaseInsensitiveURLRecursiveMatcher(String value, Map<String, String> options, char levelSeparatorChar) {
+        super(value, options, levelSeparatorChar);
     }
 
     @Override
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 6d8e293..abda58f 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
@@ -22,6 +22,9 @@ 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.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
+import org.apache.ranger.plugin.util.RangerRequestExprResolver;
 import org.apache.ranger.plugin.util.StringTokenReplacer;
 
 import java.io.Serializable;
@@ -32,12 +35,21 @@ import java.util.Map;
 abstract class ResourceMatcher {
     private static final Log LOG = LogFactory.getLog(ResourceMatcher.class);
 
-    protected final String value;
-    protected StringTokenReplacer tokenReplacer;
+    protected final String                    value;
+    protected final RangerRequestExprResolver exprResolver;
+    protected       StringTokenReplacer       tokenReplacer;
 
     static final int DYNAMIC_EVALUATION_PENALTY = 8;
 
-    ResourceMatcher(String value) { this.value = value; }
+    ResourceMatcher(String value, Map<String, String> options) {
+        this.value = value;
+
+        if (RangerAbstractResourceMatcher.getOptionReplaceReqExpressions(options) && RangerRequestExprResolver.hasExpressions(value)) {
+            exprResolver = new RangerRequestExprResolver(value, null); // TODO: serviceType
+        } else {
+            exprResolver = null;
+        }
+    }
 
     abstract boolean isMatch(String resourceValue, Map<String, Object> evalContext);
     abstract int getPriority();
@@ -45,7 +57,7 @@ abstract class ResourceMatcher {
     boolean isMatchAny() { return value != null && value.length() == 0; }
 
     boolean getNeedsDynamicEval() {
-        return tokenReplacer != null;
+        return exprResolver != null || tokenReplacer != null;
     }
 
     public boolean isMatchAny(Collection<String> resourceValues, Map<String, Object> evalContext) {
@@ -71,7 +83,7 @@ abstract class ResourceMatcher {
                     ", endDelimiter=" + endDelimiterChar + ", escapeChar=" + escapeChar + ", prefix=" + tokenPrefix);
         }
 
-        if(value != null && (value.indexOf(escapeChar) != -1 || (value.indexOf(startDelimiterChar) != -1 && value.indexOf(endDelimiterChar) != -1))) {
+        if(exprResolver != null || StringTokenReplacer.hasToken(value, startDelimiterChar, endDelimiterChar, escapeChar)) {
             tokenReplacer = new StringTokenReplacer(startDelimiterChar, endDelimiterChar, escapeChar, tokenPrefix);
         }
 
@@ -82,12 +94,18 @@ abstract class ResourceMatcher {
     }
 
     String getExpandedValue(Map<String, Object> evalContext) {
-        final String ret;
+        String ret = value;
 
-        if(tokenReplacer != null) {
-            ret = tokenReplacer.replaceTokens(value, evalContext);
-        } else {
-            ret = value;
+        if (exprResolver != null) {
+            RangerAccessRequest accessRequest = RangerAccessRequestUtil.getRequestFromContext(evalContext);
+
+            if (accessRequest != null) {
+                ret = exprResolver.resolveExpressions(accessRequest);
+            }
+        }
+
+        if (tokenReplacer != null) {
+            ret = tokenReplacer.replaceTokens(ret, evalContext);
         }
 
         return ret;
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java
index 4740055..12bd473 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java
@@ -29,6 +29,7 @@ import org.apache.commons.collections.MapUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
 import org.apache.ranger.plugin.policyengine.RangerAccessResource;
 
 public class RangerAccessRequestUtil {
@@ -45,6 +46,7 @@ public class RangerAccessRequestUtil {
 	public static final String KEY_ROLES = "ROLES";
 	public static final String KEY_CONTEXT_ACCESSTYPES = "ACCESSTYPES";
 	public static final String KEY_CONTEXT_IS_ANY_ACCESS = "ISANYACCESS";
+	public static final String KEY_CONTEXT_REQUEST       = "_REQUEST";
 
 	public static void setRequestTagsInContext(Map<String, Object> context, Set<RangerTagForEval> tags) {
 		if(CollectionUtils.isEmpty(tags)) {
@@ -184,4 +186,31 @@ public class RangerAccessRequestUtil {
 	public static Boolean getIsAnyAccessInContext(Map<String, Object> context) {
 		return (Boolean)context.get(KEY_CONTEXT_IS_ANY_ACCESS);
 	}
+
+	public static void setRequestInContext(RangerAccessRequest request) {
+		Map<String, Object> context = request.getContext();
+
+		if (context != null) {
+			context.put(KEY_CONTEXT_REQUEST, request);
+		}
+	}
+
+	public static RangerAccessRequest getRequestFromContext(Map<String, Object> context) {
+		RangerAccessRequest ret = null;
+
+		if (context != null) {
+			Object val = context.get(KEY_CONTEXT_REQUEST);
+
+			if (val != null) {
+				if (val instanceof RangerAccessRequest) {
+					ret = (RangerAccessRequest) val;
+				} else {
+					LOG.error("getRequestFromContext(): expected RangerAccessRequest, but found " + val.getClass().getCanonicalName());
+				}
+			}
+		}
+
+		return ret;
+	}
+
 }
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java
index d70430b..ceffdda 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java
@@ -36,6 +36,7 @@ public class RangerRequestExprResolver {
     private static final String  REGEX_GROUP_EXPR   = "expr";
     private static final String  SCRIPT_ENGINE_NAME = "JavaScript";
     private static final Pattern PATTERN            = Pattern.compile("\\$\\{\\{(?<" + REGEX_GROUP_EXPR + ">.*?)\\}\\}");
+    public  static final String  EXPRESSION_START   = "${{";
 
     private final String  str;
     private final String  serviceType;
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java
index 2d09d44..bf482ff 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java
@@ -34,9 +34,12 @@ public class StringTokenReplacer {
         this.tokenPrefix = tokenPrefix;
     }
 
+    public static boolean hasToken(String value, char startDelimiterChar, char endDelimiterChar, char escapeChar) {
+        return value != null && (value.indexOf(escapeChar) != -1 || (value.indexOf(startDelimiterChar) != -1 && value.indexOf(endDelimiterChar) != -1));
+    }
+
     public String replaceTokens(String value, Map<String, Object> tokens) {
-        if(tokens == null || tokens.size() < 1 || value == null || value.length() < 1 ||
-                (value.indexOf(startChar) == -1 && value.indexOf(endChar) == -1 && value.indexOf(escapeChar) == -1)) {
+        if(tokens == null || tokens.size() < 1 || !hasToken(value, startChar, endChar, escapeChar)) {
             return value;
         }
 
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 81c3744..9ba418a 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
@@ -28,6 +28,7 @@ import com.google.gson.JsonParseException;
 import com.google.gson.reflect.TypeToken;
 
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.ranger.audit.provider.AuditHandler;
 import org.apache.ranger.audit.provider.AuditProviderFactory;
@@ -51,6 +52,7 @@ import org.apache.ranger.plugin.service.RangerBasePlugin;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 import org.apache.ranger.plugin.util.RangerRequestedResources;
 import org.apache.ranger.plugin.util.RangerRoles;
+import org.apache.ranger.plugin.util.RangerUserStore;
 import org.apache.ranger.plugin.util.ServicePolicies;
 import org.apache.ranger.plugin.util.ServiceTags;
 import org.junit.AfterClass;
@@ -449,6 +451,13 @@ public class TestPolicyEngine {
 		runTestsFromResourceFiles(awsTestResourceFiles);
 	}
 
+	@Test
+	public void testPolicyEngine_resourceWithReqExpressions() {
+		String[] resourceFiles = {"/policyengine/test_policyengine_resource_with_req_expressions.json"};
+
+		runTestsFromResourceFiles(resourceFiles);
+	}
+
 	private void runTestsFromResourceFiles(String[] resourceNames) {
 		for(String resourceName : resourceNames) {
 			InputStream inStream = this.getClass().getResourceAsStream(resourceName);
@@ -660,6 +669,15 @@ public class TestPolicyEngine {
                 RangerAccessResult expected = test.result;
                 RangerAccessResult result;
 
+                if (MapUtils.isNotEmpty(test.userAttributes) || MapUtils.isNotEmpty(test.groupAttributes)) {
+                    RangerUserStore userStore = new RangerUserStore();
+
+                    userStore.setUserAttrMapping(test.userAttributes);
+                    userStore.setGroupAttrMapping(test.groupAttributes);
+
+                    RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore);
+                }
+
 				result   = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ACCESS, auditHandler);
 
 				policyEngine.evaluateAuditPolicies(result);
@@ -771,6 +789,8 @@ public class TestPolicyEngine {
 			public RangerAccessResult  dataMaskResult;
 			public RangerAccessResult rowFilterResult;
 			public RangerResourceAccessInfo resourceAccessInfo;
+			public Map<String, Map<String, String>> userAttributes;
+			public Map<String, Map<String, String>> groupAttributes;
 		}
 
 		class TagPolicyInfo {
diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_resource_with_req_expressions.json b/agents-common/src/test/resources/policyengine/test_policyengine_resource_with_req_expressions.json
new file mode 100644
index 0000000..95e0c7c
--- /dev/null
+++ b/agents-common/src/test/resources/policyengine/test_policyengine_resource_with_req_expressions.json
@@ -0,0 +1,113 @@
+{
+  "serviceName":"hdfsdev",
+
+  "serviceDef":{
+    "name":"hdfs",
+    "id":1,
+    "resources":[
+      {"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"recursiveSupported": true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Resource Path","description":"HDFS file or directory path"},
+      {"name":"path2","type":"path","level":1,"mandatory":true,"lookupSupported":true,"recursiveSupported": true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true, "replaceReqExpressions":false},"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":10,"name":"allow-all-to-user /home/${{USER._name}}","isEnabled":true,"isAuditEnabled":true,
+      "resources":{"path":{"values":["/home/${{USER._name}}"],"isRecursive":false}},
+      "policyItems":[
+        {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false}
+      ]
+    },
+    {"id":20,"name":"allow-all-to-user1 /home/${{USER._name}}","isEnabled":true,"isAuditEnabled":true,
+      "resources":{"path2":{"values":["/home/${{USER._name}}"],"isRecursive":false}},
+      "policyItems":[
+        {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false}
+      ]
+    },
+    {"id":30,"name":"allow-all-to-user /home/${{USER._name}}/{USER}.data","isEnabled":true,"isAuditEnabled":true,
+      "resources":{"path":{"values":["/home/${{USER._name}}/{USER}.data"],"isRecursive":true}},
+      "policyItems":[
+        {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false}
+      ]
+    },
+    {"id":40,"name":"allow-all-to-user /test/${{USER.testAttr1}}","isEnabled":true,"isAuditEnabled":true,
+      "resources":{"path":{"values":["/test/${{USER.testAttr1}}"],"isRecursive":true}},
+      "policyItems":[
+        {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false}
+      ]
+    }
+  ],
+
+  "tests":[
+    {"name":"ALLOW 'write path2=/home/${{USER._name}}' for u=scott; no evaluation of request-expression",
+      "request":{
+        "resource":{"elements":{"path2":"/home/${{USER._name}}"}}, "resourceMatchingScope": "SELF_OR_CHILD",
+        "accessType":"write","user":"scott","userGroups":[],"requestData":"write path2=/home/${{USER._name}}"
+      },
+      "result":{"isAudited":true,"isAllowed":true,"policyId": 20}
+    },
+    {"name":"DENY 'write path2=/home/scott' for u=scott; no evaluation of request-expression",
+      "request":{
+        "resource":{"elements":{"path2":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD",
+        "accessType":"write","user":"scott","userGroups":[],"requestData":"write path2=/home/scott"
+      },
+      "result":{"isAudited":false,"isAllowed":false,"policyId": -1}
+    },
+    {"name":"ALLOW 'write /home/scott' for u=scott for scope SELF_OR_CHILD",
+      "request":{
+        "resource":{"elements":{"path":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD",
+        "accessType":"write","user":"scott","userGroups":[],"requestData":"write /home/scott"
+      },
+      "result":{"isAudited":true,"isAllowed":true,"policyId": 10}
+    },
+    {"name":"DENY 'ANY /home/scott' for u=joe for scope SELF_OR_CHILD",
+      "request":{
+        "resource":{"elements":{"path":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD",
+        "accessType":"","user":"joe","userGroups":[],"requestData":"ANY /home/scott"
+      },
+      "result":{"isAudited":false,"isAllowed":false,"policyId": -1}
+    },
+    {"name":"DENY 'ANY /home/scott' for u=scot for scope SELF_OR_CHILD",
+      "request":{
+        "resource":{"elements":{"path":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD",
+        "accessType":"","user":"scot","userGroups":[],"requestData":"ANY /home/scott"
+      },
+      "result":{"isAudited":false,"isAllowed":false,"policyId": -1}
+    },
+    {"name":"DENY 'ANY /home/sscott' for u=scott for scope SELF_OR_CHILD",
+      "request":{
+        "resource":{"elements":{"path":"/home/sscott"}}, "resourceMatchingScope": "SELF_OR_CHILD",
+        "accessType":"","user":"scott","userGroups":[],"requestData":"ANY /home/sscott"
+      },
+      "result":{"isAudited":false,"isAllowed":false,"policyId": -1}
+    },
+    {"name":"ALLOW 'write /home/scott/scott.data/test.txt' for u=scott",
+      "request":{
+        "resource":{"elements":{"path":"/home/scott/scott.data/test.txt"}},
+        "accessType":"write","user":"scott","userGroups":[],"requestData":"write /home/scott/scott.data/test.txt"
+      },
+      "result":{"isAudited":true,"isAllowed":true,"policyId": 30}
+    },
+    {"name":"ALLOW 'write /home/scott/scott.data/test.txt' for u=scott",
+      "request":{
+        "resource":{"elements":{"path":"/home/scott/scott.data/test.txt"}},
+        "accessType":"write","user":"scott","userGroups":[],"requestData":"write /home/scott/scott.data/test.txt"
+      },
+      "result":{"isAudited":true,"isAllowed":true,"policyId": 30}
+    },
+    {"name":"ALLOW 'write /test/x_scott_y/test.txt' for u=scott",
+      "request":{
+        "resource":{"elements":{"path":"/test/x_scott_y/test.txt"}},
+        "accessType":"write","user":"scott","userGroups":[],"requestData":"write /test/x_scott_y/test.txt"
+      },
+      "userAttributes": { "scott": { "testAttr1": "x_{USER}_y" } },
+      "result":{"isAudited":true,"isAllowed":true,"policyId": 40}
+    }
+  ]
+}
\ No newline at end of file