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/12/21 01:48:26 UTC

[ranger] branch ranger-2.3 updated (ac27d80 -> 52a8558)

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

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


    from ac27d80  RANGER-3547:Upgrade to use log4j 2.16.0+ version to ensure that we are using supported version of log4j
     new 911bdc8  RANGER-3508: enhanced script condition expression for easier access to user/group/tag/resource attributes
     new 52a8558  RANGER-3550: enhancement to support use of user/tag attributes in row-filter/condition expressions

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 ...AnyOfExpectedTagsPresentConditionEvaluator.java |   6 +-
 ...oneOfExpectedTagsPresentConditionEvaluator.java |   9 +-
 .../RangerScriptConditionEvaluator.java            | 129 ++-------
 .../RangerTagsAllPresentConditionEvaluator.java    |   6 +-
 .../RangerRequestScriptEvaluator.java}             | 309 +++++++++++++++++----
 .../RangerDefaultRowFilterPolicyItemEvaluator.java |  30 +-
 .../ranger/plugin/service/RangerBasePlugin.java    |   4 +-
 .../ranger/plugin/util/RangerCommonConstants.java  |  28 +-
 .../plugin/util/RangerRequestExprResolver.java     |  88 ++++++
 .../ranger/plugin/util/ScriptEngineUtil.java       |  82 ++++++
 .../RangerCustomConditionMatcherTest.java          |  77 ++++-
 .../RangerRequestScriptEvaluatorTest.java          | 172 ++++++++++++
 .../plugin/util/RangerRequestExprResolverTest.java | 170 ++++++++++++
 13 files changed, 915 insertions(+), 195 deletions(-)
 rename agents-common/src/main/java/org/apache/ranger/plugin/{conditionevaluator/RangerScriptExecutionContext.java => policyengine/RangerRequestScriptEvaluator.java} (58%)
 create mode 100644 agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java
 create mode 100644 agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
 create mode 100644 agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
 create mode 100644 agents-common/src/test/java/org/apache/ranger/plugin/util/RangerRequestExprResolverTest.java

[ranger] 01/02: RANGER-3508: enhanced script condition expression for easier access to user/group/tag/resource attributes

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 911bdc88c86e316a04e8ae7719fc5a967425648e
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Mon Nov 8 17:12:58 2021 -0800

    RANGER-3508: enhanced script condition expression for easier access to user/group/tag/resource attributes
    
    (cherry picked from commit befc6d7e28b48c9e783af3e863af891c5fdd2b69)
---
 ...AnyOfExpectedTagsPresentConditionEvaluator.java |   6 +-
 ...oneOfExpectedTagsPresentConditionEvaluator.java |   9 +-
 .../RangerScriptConditionEvaluator.java            | 119 +-------
 .../RangerTagsAllPresentConditionEvaluator.java    |   6 +-
 .../RangerRequestScriptEvaluator.java}             | 307 +++++++++++++++++----
 .../ranger/plugin/service/RangerBasePlugin.java    |   4 +-
 .../ranger/plugin/util/RangerCommonConstants.java  |  28 +-
 .../ranger/plugin/util/ScriptEngineUtil.java       |  82 ++++++
 .../RangerCustomConditionMatcherTest.java          |  46 ++-
 .../RangerRequestScriptEvaluatorTest.java          | 169 ++++++++++++
 10 files changed, 590 insertions(+), 186 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java
index 3221f79..15826d1 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java
@@ -22,6 +22,7 @@ package org.apache.ranger.plugin.conditionevaluator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -63,9 +64,8 @@ public class RangerAnyOfExpectedTagsPresentConditionEvaluator extends RangerAbst
 
 		boolean matched = false;
 
-		RangerAccessRequest			 readOnlyRequest = request.getReadOnlyCopy();
-		RangerScriptExecutionContext context         = new RangerScriptExecutionContext(readOnlyRequest);
-		Set<String>                  resourceTags    = context.getAllTagTypes();
+		RangerRequestScriptEvaluator evaluator    = new RangerRequestScriptEvaluator(request);
+		Set<String>                  resourceTags = evaluator.getAllTagTypes();
 
 		if (resourceTags != null) {
 			// check if resource Tags does contain any of the policy Condition Tags
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java
index d04f4b3..9e6230d 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java
@@ -22,6 +22,7 @@ package org.apache.ranger.plugin.conditionevaluator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -61,11 +62,9 @@ public class RangerNoneOfExpectedTagsPresentConditionEvaluator extends RangerAbs
 			LOG.debug("==> RangerNoneOfExpectedTagsPresentConditionEvaluator.isMatched(" + request + ")");
 		}
 
-		boolean matched = true;
-
-		RangerAccessRequest readOnlyRequest = request.getReadOnlyCopy();
-		RangerScriptExecutionContext context = new RangerScriptExecutionContext(readOnlyRequest);
-		Set<String> resourceTags = context.getAllTagTypes();
+		boolean                      matched      = true;
+		RangerRequestScriptEvaluator evaluator    = new RangerRequestScriptEvaluator(request);
+		Set<String>                  resourceTags = evaluator.getAllTagTypes();
 
 		if (resourceTags != null) {
 			// check if resource Tags does not contain any tags in the policy condition
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java
index e57f599..b94225b 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java
@@ -24,17 +24,11 @@ import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.ranger.plugin.classloader.RangerPluginClassLoader;
-import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
-import org.apache.ranger.plugin.util.RangerPerfTracer;
+import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator;
+import org.apache.ranger.plugin.util.ScriptEngineUtil;
 
-import javax.script.Bindings;
 import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -43,24 +37,18 @@ import static org.apache.ranger.plugin.util.RangerCommonConstants.*;
 public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvaluator {
 	private static final Log LOG = LogFactory.getLog(RangerScriptConditionEvaluator.class);
 
-	private static final Log PERF_POLICY_CONDITION_SCRIPT_EVAL = RangerPerfTracer.getPerfLogger("policy.condition.script.eval");
-
-	private static final String SCRIPT_PREEXEC = SCRIPT_VAR_CONTEXT + "=JSON.parse(" + SCRIPT_VAR_CONTEXT_JSON + ");";
-
 	private ScriptEngine scriptEngine;
 	private boolean      enableJsonCtx = false;
 
 	@Override
 	public void init() {
-
 		if (LOG.isDebugEnabled()) {
 			LOG.debug("==> RangerScriptConditionEvaluator.init(" + condition + ")");
 		}
 
 		super.init();
 
-		String engineName = "JavaScript";
-
+		String              engineName  = "JavaScript";
 		Map<String, String> evalOptions = conditionDef. getEvaluatorOptions();
 
 		if (MapUtils.isNotEmpty(evalOptions)) {
@@ -77,51 +65,11 @@ public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvalu
 			LOG.debug("RangerScriptConditionEvaluator.init() - engineName=" + engineName);
 		}
 
-		String conditionType = condition != null ? condition.getType() : null;
-
-		try {
-			ScriptEngineManager manager = new ScriptEngineManager();
-
-			if (LOG.isDebugEnabled()) {
-				List<ScriptEngineFactory> factories = manager.getEngineFactories();
-
-				if (CollectionUtils.isEmpty(factories)) {
-					LOG.debug("List of scriptEngineFactories is empty!!");
-				} else {
-					for (ScriptEngineFactory factory : factories) {
-						LOG.debug("engineName=" + factory.getEngineName() + ", language=" + factory.getLanguageName());
-					}
-				}
-			}
-
-			scriptEngine = manager.getEngineByName(engineName);
-		} catch (Exception exp) {
-			LOG.error("RangerScriptConditionEvaluator.init() failed with exception=" + exp);
-		}
+		scriptEngine = ScriptEngineUtil.createScriptEngine(engineName, serviceDef.getName());
 
 		if (scriptEngine == null) {
-			LOG.warn("failed to initialize condition '" + conditionType + "': script engine '" + engineName + "' was not created in a default manner");
-			LOG.info("Will try to get script-engine from plugin-class-loader");
-
-
-			RangerPluginClassLoader pluginClassLoader;
-
-			try {
-
-				pluginClassLoader = RangerPluginClassLoader.getInstance(serviceDef.getName(), null);
-
-				if (pluginClassLoader != null) {
-					scriptEngine = pluginClassLoader.getScriptEngine(engineName);
-				} else {
-					LOG.error("Cannot get script-engine from null pluginClassLoader");
-				}
-
-			} catch (Throwable exp) {
-				LOG.error("RangerScriptConditionEvaluator.init() failed with exception=", exp);
-			}
-		}
+			String conditionType = condition != null ? condition.getType() : null;
 
-		if (scriptEngine == null) {
 			LOG.error("failed to initialize condition '" + conditionType + "': script engine '" + engineName + "' was not created");
 		} else {
 			LOG.info("ScriptEngine for engineName=[" + engineName + "] is successfully created");
@@ -137,70 +85,30 @@ public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvalu
 		if (LOG.isDebugEnabled()) {
 			LOG.debug("==> RangerScriptConditionEvaluator.isMatched()");
 		}
+
 		boolean result = true;
 
 		if (scriptEngine != null) {
-
 			String script = getScript();
 
 			if (StringUtils.isNotBlank(script)) {
-
-				RangerAccessRequest readOnlyRequest = request.getReadOnlyCopy();
-
-				RangerScriptExecutionContext context    = new RangerScriptExecutionContext(readOnlyRequest);
-				RangerTagForEval             currentTag = context.getCurrentTag();
-				Map<String, String>          tagAttribs = currentTag != null ? currentTag.getAttributes() : Collections.emptyMap();
-
-				Bindings bindings = scriptEngine.createBindings();
-
-				bindings.put("ctx", context);
-				bindings.put("tag", currentTag);
-				bindings.put("tagAttr", tagAttribs);
-
-				if (enableJsonCtx) {
-					bindings.put(SCRIPT_VAR_CONTEXT_JSON, context.toJson());
-
-					script = SCRIPT_PREEXEC + script;
-				}
-
 				if (LOG.isDebugEnabled()) {
 					LOG.debug("RangerScriptConditionEvaluator.isMatched(): script={" + script + "}");
 				}
 
-				RangerPerfTracer perf = null;
-
-				try {
-					long requestHash = request.hashCode();
+				RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request);
 
-					if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_CONDITION_SCRIPT_EVAL)) {
-						perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_CONDITION_SCRIPT_EVAL, "RangerScriptConditionEvaluator.isMatched(requestHash=" + requestHash + ")");
-					}
+				evaluator.evaluateConditionScript(scriptEngine, script, enableJsonCtx);
 
-					Object ret = scriptEngine.eval(script, bindings);
-
-					if (ret == null) {
-						ret = context.getResult();
-					}
-					if (ret instanceof Boolean) {
-						result = (Boolean) ret;
-					}
-
-				} catch (NullPointerException nullp) {
-					LOG.error("RangerScriptConditionEvaluator.isMatched(): eval called with NULL argument(s)", nullp);
-
-				} catch (ScriptException exception) {
-					LOG.error("RangerScriptConditionEvaluator.isMatched(): failed to evaluate script," +
-							" exception=" + exception);
-				} finally {
-					RangerPerfTracer.log(perf);
-				}
+				result = evaluator.getResult();
 			} else {
 				String conditionType = condition != null ? condition.getType() : null;
+
 				LOG.error("failed to evaluate condition '" + conditionType + "': script is empty");
 			}
-
 		} else {
 			String conditionType = condition != null ? condition.getType() : null;
+
 			LOG.error("failed to evaluate condition '" + conditionType + "': script engine not found");
 		}
 
@@ -213,13 +121,12 @@ public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvalu
 	}
 
 	protected String getScript() {
-		String ret = null;
-
+		String       ret    = null;
 		List<String> values = condition.getValues();
 
 		if (CollectionUtils.isNotEmpty(values)) {
-
 			String value = values.get(0);
+
 			if (StringUtils.isNotBlank(value)) {
 				ret = value.trim();
 			}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java
index 8616c66..c8edf32 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java
@@ -23,6 +23,7 @@ import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -63,9 +64,8 @@ public class RangerTagsAllPresentConditionEvaluator extends RangerAbstractCondit
 		boolean matched = true;
 
 		if (CollectionUtils.isNotEmpty(policyConditionTags))  {
-			RangerAccessRequest			 readOnlyRequest = request.getReadOnlyCopy();
-			RangerScriptExecutionContext context         = new RangerScriptExecutionContext(readOnlyRequest);
-			Set<String>                  resourceTags    = context.getAllTagTypes();
+			RangerRequestScriptEvaluator evaluator    = new RangerRequestScriptEvaluator(request);
+			Set<String>                  resourceTags = evaluator.getAllTagTypes();
 
 			// check if resource Tags  atleast have to have all the tags in policy Condition
 			matched = resourceTags != null && resourceTags.containsAll(policyConditionTags);
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptExecutionContext.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
similarity index 58%
rename from agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptExecutionContext.java
rename to agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
index 3563fd8..64f01c9 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptExecutionContext.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.ranger.plugin.conditionevaluator;
+package org.apache.ranger.plugin.policyengine;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
@@ -27,16 +27,19 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.ranger.authorization.utils.JsonUtils;
 import org.apache.ranger.authorization.utils.StringUtil;
 import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
-import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
-import org.apache.ranger.plugin.policyengine.RangerAccessResource;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 import org.apache.ranger.plugin.util.RangerPerfTracer;
 import org.apache.ranger.plugin.util.RangerUserStore;
 
+import javax.script.Bindings;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
@@ -45,26 +48,42 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import static org.apache.ranger.plugin.util.RangerCommonConstants.*;
 
 
-public final class RangerScriptExecutionContext {
-	private static final Log LOG = LogFactory.getLog(RangerScriptExecutionContext.class);
+public final class RangerRequestScriptEvaluator {
+	private static final Log LOG = LogFactory.getLog(RangerRequestScriptEvaluator.class);
 
 	private static final Log    PERF_POLICY_CONDITION_SCRIPT_TOJSON         = RangerPerfTracer.getPerfLogger("policy.condition.script.tojson");
+	private static final Log    PERF_POLICY_CONDITION_SCRIPT_EVAL           = RangerPerfTracer.getPerfLogger("policy.condition.script.eval");
 	private static final String TAG_ATTR_DATE_FORMAT_PROP                   = "ranger.plugin.tag.attr.additional.date.formats";
 	private static final String TAG_ATTR_DATE_FORMAT_SEPARATOR              = "||";
 	private static final String TAG_ATTR_DATE_FORMAT_SEPARATOR_REGEX        = "\\|\\|";
 	private static final String DEFAULT_RANGER_TAG_ATTRIBUTE_DATE_FORMAT    = "yyyy/MM/dd";
 	private static final String DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT_NAME = "ATLAS_DATE_FORMAT";
 	private static final String DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT     = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
-
-	private final RangerAccessRequest accessRequest;
-	private Boolean result = false;
+	private static final String SCRIPT_PREEXEC                              = SCRIPT_VAR__CTX + "=JSON.parse(" + SCRIPT_VAR__CTX_JSON + "); J=JSON.stringify;" +
+                                                                                 SCRIPT_VAR_REQ + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_REQUEST + ";" +
+                                                                                 SCRIPT_VAR_RES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_RESOURCE + ";" +
+                                                                                 SCRIPT_VAR_USER + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_ATTRIBUTES + ";" +
+                                                                                 SCRIPT_VAR_UGROUPS + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUPS + ";" +
+                                                                                 SCRIPT_VAR_UGROUP + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUP_ATTRIBUTES + ";" +
+                                                                                 SCRIPT_VAR_UGA + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_UGA + ";" +
+                                                                                 SCRIPT_VAR_UROLES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_ROLES + ";" +
+                                                                                 SCRIPT_VAR_TAG + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAG + ";" +
+                                                                                 SCRIPT_VAR_TAGS + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAGS + ";" +
+                                                                                 SCRIPT_VAR_TAGNAMES + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAG_NAMES + ";";
+
+	private static final Pattern JSON_VAR_NAMES_PATTERN = Pattern.compile(getJsonVarNamesPattern());
 
 	private static String[] dateFormatStrings = null;
 
+	private final RangerAccessRequest accessRequest;
+	private       Boolean             result = false;
+
 	static {
 		init(null);
 	}
@@ -93,25 +112,99 @@ public final class RangerScriptExecutionContext {
 				}
 			};
 
-	RangerScriptExecutionContext(final RangerAccessRequest accessRequest) {
-		this.accessRequest = accessRequest;
+
+	public static boolean needsJsonCtxEnabled(String script) {
+		Matcher matcher = JSON_VAR_NAMES_PATTERN.matcher(script);
+
+		boolean ret = matcher.matches();
+
+		return ret;
+	}
+
+
+	public RangerRequestScriptEvaluator(final RangerAccessRequest accessRequest) {
+		this.accessRequest = accessRequest.getReadOnlyCopy();
+	}
+
+	public Object evaluateScript(ScriptEngine scriptEngine, String script) {
+		return evaluateScript(scriptEngine, script, needsJsonCtxEnabled(script));
+	}
+
+	public Object evaluateConditionScript(ScriptEngine scriptEngine, String script, boolean enableJsonCtx) {
+		Object ret = evaluateScript(scriptEngine, script, enableJsonCtx);
+
+		if (ret == null) {
+			ret = getResult();
+		}
+
+		if (ret instanceof Boolean) {
+			result = (Boolean) ret;
+		}
+
+		return ret;
 	}
 
-	public String toJson() {
+	private Object evaluateScript(ScriptEngine scriptEngine, String script, boolean enableJsonCtx) {
+		Object              ret        = null;
+		Bindings            bindings   = scriptEngine.createBindings();
+		RangerTagForEval    currentTag = this.getCurrentTag();
+		Map<String, String> tagAttribs = currentTag != null ? currentTag.getAttributes() : Collections.emptyMap();
+
+		bindings.put(SCRIPT_VAR_ctx, this);
+		bindings.put(SCRIPT_VAR_tag, currentTag);
+		bindings.put(SCRIPT_VAR_tagAttr, tagAttribs);
+
+		if (enableJsonCtx) {
+			bindings.put(SCRIPT_VAR__CTX_JSON, this.toJson());
+
+			script = SCRIPT_PREEXEC + script;
+		}
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("RangerRequestScriptEvaluator.evaluateScript(): script={" + script + "}");
+		}
+
+		RangerPerfTracer perf = null;
+
+		try {
+			long requestHash = accessRequest.hashCode();
+
+			if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_CONDITION_SCRIPT_EVAL)) {
+				perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_CONDITION_SCRIPT_EVAL, "RangerRequestScriptEvaluator.evaluateScript(requestHash=" + requestHash + ")");
+			}
+
+			ret = scriptEngine.eval(script, bindings);
+		} catch (NullPointerException nullp) {
+			LOG.error("RangerRequestScriptEvaluator.evaluateScript(): eval called with NULL argument(s)", nullp);
+
+		} catch (ScriptException exception) {
+			LOG.error("RangerRequestScriptEvaluator.evaluateScript(): failed to evaluate script," +
+					" exception=" + exception);
+		} finally {
+			RangerPerfTracer.log(perf);
+		}
+
+		return ret;
+	}
+
+	private String toJson() {
 		RangerPerfTracer perf = null;
 
 		long requestHash = accessRequest.hashCode();
 
 		if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_CONDITION_SCRIPT_TOJSON)) {
-			perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_CONDITION_SCRIPT_TOJSON, "RangerScriptExecutionContext.toJson(requestHash=" + requestHash + ")");
+			perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_CONDITION_SCRIPT_TOJSON, "RangerRequestScriptEvaluator.toJson(requestHash=" + requestHash + ")");
 		}
 
-		Map<String, Object> ret       = new HashMap<>();
-		Map<String, Object> request   = new HashMap<>();
-		RangerUserStore     userStore = RangerAccessRequestUtil.getRequestUserStoreFromContext(accessRequest.getContext());
+		Map<String, Object> ret        = new HashMap<>();
+		Map<String, Object> request    = new HashMap<>();
+		RangerUserStore     userStore  = RangerAccessRequestUtil.getRequestUserStoreFromContext(accessRequest.getContext());
+		Collection<String>  userGroups = getSorted(getUserGroups());
+		Collection<String>  userRoles  = getSorted(getUserRoles());
+		Date                accessTime = accessRequest.getAccessTime();
 
-		if (accessRequest.getAccessTime() != null) {
-			request.put(SCRIPT_FIELD_ACCESS_TIME, accessRequest.getAccessTime().getTime());
+		if (accessTime != null) {
+			request.put(SCRIPT_FIELD_ACCESS_TIME, accessTime.getTime());
 		}
 
 		request.put(SCRIPT_FIELD_ACCESS_TYPE, accessRequest.getAccessType());
@@ -125,63 +218,69 @@ public final class RangerScriptExecutionContext {
 		request.put(SCRIPT_FIELD_REQUEST_DATA, accessRequest.getRequestData());
 
 		if (accessRequest.getResource() != null) {
-			request.put(SCRIPT_FIELD_RESOURCE, accessRequest.getResource().getAsMap());
-			request.put(SCRIPT_FIELD_RESOURCE_OWNER_USER, accessRequest.getResource().getOwnerUser());
+			Map<String, Object> resource = new HashMap<>(accessRequest.getResource().getAsMap());
+
+			resource.put(SCRIPT_FIELD__OWNER_USER, accessRequest.getResource().getOwnerUser());
+
+			request.put(SCRIPT_FIELD_RESOURCE, resource);
 		}
 
 		request.put(SCRIPT_FIELD_RESOURCE_MATCHING_SCOPE, accessRequest.getResourceMatchingScope());
 
-		request.put(SCRIPT_FIELD_USER, accessRequest.getUser());
-		request.put(SCRIPT_FIELD_USER_GROUPS, accessRequest.getUserGroups());
-		request.put(SCRIPT_FIELD_USER_ROLES, accessRequest.getUserRoles());
+		request.put(SCRIPT_FIELD_USER, getUser());
+		request.put(SCRIPT_FIELD_USER_GROUPS, userGroups);
+		request.put(SCRIPT_FIELD_USER_ROLES, userRoles);
 
-		if (userStore != null) {
-			Map<String, Map<String, String>> userAttrMapping  = userStore.getUserAttrMapping();
-			Map<String, Map<String, String>> groupAttrMapping = userStore.getGroupAttrMapping();
+		Map<String, Map<String, String>> userAttrMapping  = userStore != null ? userStore.getUserAttrMapping() : Collections.emptyMap();
+		Map<String, Map<String, String>> groupAttrMapping = userStore != null ? userStore.getGroupAttrMapping() : Collections.emptyMap();
+		Map<String, String>              userAttrs        = userAttrMapping.get(accessRequest.getUser());
+		Map<String, Map<String, String>> groupAttrs       = new HashMap<>();
 
-			if (userAttrMapping != null) {
-				request.put(SCRIPT_FIELD_USER_ATTRIBUTES, userAttrMapping.get(accessRequest.getUser()));
-			}
+		userAttrs = userAttrs != null ? new HashMap<>(userAttrs) : new HashMap<>();
 
-			if (groupAttrMapping != null && accessRequest.getUserGroups() != null) {
-				Map<String, Map<String, String>> groupAttrs = new HashMap<>();
+		userAttrs.put(SCRIPT_FIELD__NAME, getUser());
 
-				for (String groupName : accessRequest.getUserGroups()) {
-					groupAttrs.put(groupName, groupAttrMapping.get(groupName));
-				}
+		request.put(SCRIPT_FIELD_USER_ATTRIBUTES, userAttrs);
+
+		if (userGroups != null) {
+			for (String groupName : userGroups) {
+				Map<String, String> attrs = groupAttrMapping.get(groupName);
+
+				attrs = attrs != null ? new HashMap<>(attrs) : new HashMap<>();
+
+				attrs.put(SCRIPT_FIELD__NAME, groupName);
 
-				request.put(SCRIPT_FIELD_USER_GROUP_ATTRIBUTES, groupAttrs);
+				groupAttrs.put(groupName, attrs);
 			}
 		}
 
+		request.put(SCRIPT_FIELD_USER_GROUP_ATTRIBUTES, groupAttrs);
+		request.put(SCRIPT_FIELD_UGA, new UserGroupsAttributes(userGroups, groupAttrs).getAttributes());
+
 		ret.put(SCRIPT_FIELD_REQUEST, request);
 
 		Set<RangerTagForEval> requestTags = RangerAccessRequestUtil.getRequestTagsFromContext(getRequestContext());
 
 		if (CollectionUtils.isNotEmpty(requestTags)) {
-			Set<Map<String, Object>> tags = new HashSet<>();
+			Set<Map<String, Object>> tags     = new HashSet<>();
+			Set<String>              tagNames = new HashSet<>();
 
 			for (RangerTagForEval tag : requestTags) {
 				tags.add(toMap(tag));
+				tagNames.add(tag.getType());
 			}
 
 			ret.put(SCRIPT_FIELD_TAGS, tags);
+			ret.put(SCRIPT_FIELD_TAG_NAMES, tagNames);
 
 			RangerTagForEval currentTag = RangerAccessRequestUtil.getCurrentTagFromContext(getRequestContext());
 
 			if (currentTag != null) {
 				ret.put(SCRIPT_FIELD_TAG, toMap(currentTag));
 			}
-		}
-
-		RangerAccessResource resource = RangerAccessRequestUtil.getCurrentResourceFromContext(getRequestContext());
-
-		if (resource != null) {
-			ret.put(SCRIPT_FIELD_RESOURCE, resource.getAsMap());
-
-			if (resource.getOwnerUser() != null) {
-				ret.put(SCRIPT_FIELD_RESOURCE_OWNER_USER, resource.getOwnerUser());
-			}
+		} else {
+			ret.put(SCRIPT_FIELD_TAGS, Collections.emptySet());
+			ret.put(SCRIPT_FIELD_TAG_NAMES, Collections.emptySet());
 		}
 
 		String strRet = JsonUtils.objectToJson(ret);
@@ -211,7 +310,7 @@ public final class RangerScriptExecutionContext {
 			}
 		});
 
-		RangerScriptExecutionContext.dateFormatStrings = formatStrings;
+		RangerRequestScriptEvaluator.dateFormatStrings = formatStrings;
 	}
 
 	public String getResource() {
@@ -251,6 +350,8 @@ public final class RangerScriptExecutionContext {
 
 	public Set<String> getUserGroups() { return accessRequest.getUserGroups(); }
 
+	public Set<String> getUserRoles() { return accessRequest.getUserRoles(); }
+
 	public Date getAccessTime() { return accessRequest.getAccessTime() != null ? accessRequest.getAccessTime() : new Date(); }
 
 	public String getClientIPAddress() { return accessRequest.getClientIPAddress(); }
@@ -268,7 +369,7 @@ public final class RangerScriptExecutionContext {
 
 		if(ret == null ) {
 			if (LOG.isDebugEnabled()) {
-				logDebug("RangerScriptExecutionContext.getCurrentTag() - No current TAG object. Script execution must be for resource-based policy.");
+				logDebug("RangerRequestScriptEvaluator.getCurrentTag() - No current TAG object. Script execution must be for resource-based policy.");
 			}
 		}
 		return ret;
@@ -433,7 +534,7 @@ public final class RangerScriptExecutionContext {
 				ret = getAsDate(value, simpleDateFormat);
 				if (ret != null) {
 					if (LOG.isDebugEnabled()) {
-						logDebug("RangerScriptExecutionContext.getAsDate() -The best match found for Format-String:[" + simpleDateFormat.toPattern() + "], date:[" + ret +"]");
+						logDebug("RangerRequestScriptEvaluator.getAsDate() -The best match found for Format-String:[" + simpleDateFormat.toPattern() + "], date:[" + ret +"]");
 					}
 					break;
 				}
@@ -441,7 +542,7 @@ public final class RangerScriptExecutionContext {
 		}
 
 		if (ret == null) {
-			logError("RangerScriptExecutionContext.getAsDate() - Could not convert [" + value + "] to Date using any of the Format-Strings: " + Arrays.toString(dateFormatStrings));
+			logError("RangerRequestScriptEvaluator.getAsDate() - Could not convert [" + value + "] to Date using any of the Format-Strings: " + Arrays.toString(dateFormatStrings));
 		} else {
 			ret = StringUtil.getUTCDateForLocalDate(ret);
 		}
@@ -509,7 +610,7 @@ public final class RangerScriptExecutionContext {
 			if (LOG.isDebugEnabled()) {
 				String resource = accessRequest.getResource().getAsString();
 
-				logDebug("RangerScriptExecutionContext.getAllTags() - No TAGS. No TAGS for the RangerAccessResource=" + resource);
+				logDebug("RangerRequestScriptEvaluator.getAllTags() - No TAGS. No TAGS for the RangerAccessResource=" + resource);
 			}
 		}
 
@@ -519,13 +620,53 @@ public final class RangerScriptExecutionContext {
 	private static Map<String, Object> toMap(RangerTagForEval tag) {
 		Map<String, Object> ret = new HashMap<>();
 
-		ret.put("type", tag.getType());
-		ret.put("attributes", tag.getAttributes());
-		ret.put("matchType", tag.getMatchType());
+		if (tag.getAttributes() != null) {
+			ret.putAll(tag.getAttributes());
+		}
+
+		ret.put(SCRIPT_FIELD__TYPE, tag.getType());
+		ret.put(SCRIPT_FIELD__MATCH_TYPE, tag.getMatchType());
 
 		return ret;
 	}
 
+	private Collection<String> getSorted(Collection<String> coll) {
+		if (coll != null && coll.size() > 1) {
+			List<String> lst = new ArrayList<>(coll);
+
+			Collections.sort(lst);
+
+			coll = lst;
+		}
+
+		return coll;
+	}
+
+	private static String getJsonVarNamesPattern() {
+		List<String> varNames = new ArrayList<>();
+
+		/* include only variables setup by JSON.parse()
+		 *
+		varNames.add(SCRIPT_VAR_ctx);
+		varNames.add(SCRIPT_VAR_tag);
+		varNames.add(SCRIPT_VAR_tagAttr);
+		 *
+		 */
+		varNames.add(SCRIPT_VAR__CTX);
+		varNames.add(SCRIPT_VAR_REQ);
+		varNames.add(SCRIPT_VAR_RES);
+		varNames.add(SCRIPT_VAR_TAG);
+		varNames.add(SCRIPT_VAR_TAGNAMES);
+		varNames.add(SCRIPT_VAR_TAGS);
+		varNames.add(SCRIPT_VAR_UGA);
+		varNames.add(SCRIPT_VAR_UGROUP);
+		varNames.add(SCRIPT_VAR_UGROUPS);
+		varNames.add(SCRIPT_VAR_UROLES);
+		varNames.add(SCRIPT_VAR_USER);
+
+		return ".*(" + StringUtils.join(varNames, '|') + ").*";
+	}
+
 	public void logDebug(Object msg) {
 		LOG.debug(msg);
 	}
@@ -545,4 +686,64 @@ public final class RangerScriptExecutionContext {
 	public void logFatal(Object msg) {
 		LOG.fatal(msg);
 	}
+
+	public static class UserGroupsAttributes {
+		private final Collection<String>               groupNames;
+		private final Map<String, Map<String, String>> groupAttributes;
+
+		public UserGroupsAttributes(Collection<String> groupNames, Map<String, Map<String, String>> groupAttributes) {
+			this.groupNames      = groupNames;
+			this.groupAttributes = groupAttributes;
+		}
+
+		/*
+		  {
+		    sVal: {
+		      attr1: val1,
+		      attr2: val2
+		    },
+		    mVal: {
+		      attr1: [ val1, val1_2 ],
+		      attr2: [ val2, val2_2 ]
+		    }
+		  }
+		 */
+		public Map<String, Map<String, Object>> getAttributes() {
+			Map<String, Map<String, Object>> ret       = new HashMap<>();
+			Map<String, String>              valueMap  = new HashMap<>();
+			Map<String, List<String>>        valuesMap = new HashMap<>();
+
+			ret.put("sVal", (Map) valueMap);
+			ret.put("mVal", (Map) valuesMap);
+
+			if (groupNames != null && groupAttributes != null) {
+				for (String groupName : groupNames) {
+					Map<String, String> attributes = groupAttributes.get(groupName);
+
+					if (attributes != null) {
+						for (Map.Entry<String, String> entry : attributes.entrySet()) {
+							String attrName  = entry.getKey();
+							String attrValue = entry.getValue();
+
+							if (!valueMap.containsKey(attrName)) {
+								valueMap.put(attrName, attrValue);
+							}
+
+							List<String> values = valuesMap.get(attrName);
+
+							if (values == null) {
+								values = new ArrayList<>();
+
+								valuesMap.put(attrName, values);
+							}
+
+							values.add(attrValue);
+						}
+					}
+				}
+			}
+
+			return ret;
+		}
+	}
 }
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
index 57a4b4b..6430b98 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
@@ -33,7 +33,7 @@ import org.apache.ranger.audit.provider.StandAloneAuditProviderFactory;
 import org.apache.ranger.authorization.hadoop.config.RangerAuditConfig;
 import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
 import org.apache.ranger.authorization.utils.StringUtil;
-import org.apache.ranger.plugin.conditionevaluator.RangerScriptExecutionContext;
+import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator;
 import org.apache.ranger.plugin.contextenricher.RangerContextEnricher;
 import org.apache.ranger.plugin.contextenricher.RangerTagEnricher;
 import org.apache.ranger.plugin.model.RangerPolicy;
@@ -94,7 +94,7 @@ public class RangerBasePlugin {
 		setIsFallbackSupported(pluginConfig.getBoolean(pluginConfig.getPropertyPrefix() + ".is.fallback.supported", false));
 		setServiceAdmins(serviceAdmins);
 
-		RangerScriptExecutionContext.init(pluginConfig);
+		RangerRequestScriptEvaluator.init(pluginConfig);
 
 		this.chainedPlugins = initChainedPlugins();
 	}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
index 75132d8..63bed50 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
@@ -51,8 +51,23 @@ public class RangerCommonConstants {
 	public static final boolean POLICY_REST_CLIENT_SESSION_COOKIE_ENABLED            = true;
 
 	public static final String SCRIPT_OPTION_ENABLE_JSON_CTX        = "enableJsonCtx";
-	public static final String SCRIPT_VAR_CONTEXT                   = "_ctx";
-	public static final String SCRIPT_VAR_CONTEXT_JSON              = "_ctx_json";
+
+	public static final String SCRIPT_VAR_ctx                       = "ctx";
+	public static final String SCRIPT_VAR_tag                       = "tag";
+	public static final String SCRIPT_VAR_tagAttr                   = "tagAttr";
+	public static final String SCRIPT_VAR__CTX                      = "_ctx";
+	public static final String SCRIPT_VAR__CTX_JSON                 = "_ctx_json";
+	public static final String SCRIPT_VAR_REQ                       = "REQ";
+	public static final String SCRIPT_VAR_RES                       = "RES";
+	public static final String SCRIPT_VAR_TAG                       = "TAG";
+	public static final String SCRIPT_VAR_TAGNAMES                  = "TAGNAMES";
+	public static final String SCRIPT_VAR_TAGS                      = "TAGS";
+	public static final String SCRIPT_VAR_UGA                       = "UGA";
+	public static final String SCRIPT_VAR_UGROUP                    = "UGROUP";
+	public static final String SCRIPT_VAR_UGROUPS                   = "UGROUPS";
+	public static final String SCRIPT_VAR_UROLES                    = "UROLES";
+	public static final String SCRIPT_VAR_USER                      = "USER";
+
 	public static final String SCRIPT_FIELD_ACCESS_TIME             = "accessTime";
 	public static final String SCRIPT_FIELD_ACCESS_TYPE             = "accessType";
 	public static final String SCRIPT_FIELD_ACTION                  = "action";
@@ -61,17 +76,22 @@ public class RangerCommonConstants {
 	public static final String SCRIPT_FIELD_CLUSTER_NAME            = "clusterName";
 	public static final String SCRIPT_FIELD_CLUSTER_TYPE            = "clusterType";
 	public static final String SCRIPT_FIELD_FORWARDED_ADDRESSES     = "forwardedAddresses";
+	public static final String SCRIPT_FIELD__MATCH_TYPE             = "_matchType";
+	public static final String SCRIPT_FIELD__NAME                   = "_name";
+	public static final String SCRIPT_FIELD__OWNER_USER             = "_ownerUser";
 	public static final String SCRIPT_FIELD_REMOTE_IP_ADDRESS       = "remoteIPAddress";
+	public static final String SCRIPT_FIELD_REQUEST                 = "request";
 	public static final String SCRIPT_FIELD_REQUEST_DATA            = "requestData";
 	public static final String SCRIPT_FIELD_RESOURCE                = "resource";
-	public static final String SCRIPT_FIELD_RESOURCE_OWNER_USER     = "resourceOwnerUser";
 	public static final String SCRIPT_FIELD_RESOURCE_MATCHING_SCOPE = "resourceMatchingScope";
 	public static final String SCRIPT_FIELD_TAG                     = "tag";
 	public static final String SCRIPT_FIELD_TAGS                    = "tags";
+	public static final String SCRIPT_FIELD_TAG_NAMES               = "tagNames";
+	public static final String SCRIPT_FIELD__TYPE                   = "_type";
 	public static final String SCRIPT_FIELD_USER                    = "user";
 	public static final String SCRIPT_FIELD_USER_ATTRIBUTES         = "userAttributes";
 	public static final String SCRIPT_FIELD_USER_GROUPS             = "userGroups";
 	public static final String SCRIPT_FIELD_USER_GROUP_ATTRIBUTES   = "userGroupAttributes";
+	public static final String SCRIPT_FIELD_UGA                     = "uga";
 	public static final String SCRIPT_FIELD_USER_ROLES              = "userRoles";
-	public static final String SCRIPT_FIELD_REQUEST                 = "request";
 }
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
new file mode 100644
index 0000000..b0782ec
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
@@ -0,0 +1,82 @@
+/*
+ * 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.util;
+
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.plugin.classloader.RangerPluginClassLoader;
+import org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import java.util.List;
+
+public class ScriptEngineUtil {
+    private static final Log LOG = LogFactory.getLog(RangerScriptConditionEvaluator.class);
+
+
+    public static ScriptEngine createScriptEngine(String engineName, String serviceType) {
+        ScriptEngine ret = null;
+
+        try {
+            ScriptEngineManager manager = new ScriptEngineManager();
+
+            if (LOG.isDebugEnabled()) {
+                List<ScriptEngineFactory> factories = manager.getEngineFactories();
+
+                if (CollectionUtils.isEmpty(factories)) {
+                    LOG.debug("List of scriptEngineFactories is empty!!");
+                } else {
+                    for (ScriptEngineFactory factory : factories) {
+                        LOG.debug("engineName=" + factory.getEngineName() + ", language=" + factory.getLanguageName());
+                    }
+                }
+            }
+
+            ret = manager.getEngineByName(engineName);
+        } catch (Exception exp) {
+            LOG.error("RangerScriptConditionEvaluator.init() failed with exception=" + exp);
+        }
+
+        if (ret == null) {
+            LOG.warn("failed to initialize script engine '" + engineName + "' in a default manner." +
+                     " Will try to get script-engine from plugin-class-loader");
+
+            RangerPluginClassLoader pluginClassLoader;
+
+            try {
+                pluginClassLoader = RangerPluginClassLoader.getInstance(serviceType, null);
+
+                if (pluginClassLoader != null) {
+                    ret = pluginClassLoader.getScriptEngine(engineName);
+                } else {
+                    LOG.error("Cannot get script-engine from null pluginClassLoader");
+                }
+            } catch (Throwable exp) {
+                LOG.error("RangerScriptConditionEvaluator.init() failed with exception=", exp);
+            }
+        }
+
+        return ret;
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java
index 36504c7..d5f1564 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java
@@ -21,6 +21,7 @@ package org.apache.ranger.plugin.conditionevaluator;
 
 
 import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
+import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef;
 import org.apache.ranger.plugin.model.RangerTag;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition;
@@ -61,13 +62,24 @@ public class RangerCustomConditionMatcherTest {
 		RangerScriptConditionEvaluator accessTypeCondition     = createScriptConditionEvaluator("_ctx.request.accessType.equals('select')");
 		RangerScriptConditionEvaluator actionCondition         = createScriptConditionEvaluator("_ctx.request.action.equals('query')");
 		RangerScriptConditionEvaluator userCondition           = createScriptConditionEvaluator("_ctx.request.user.equals('test-user')");
-		RangerScriptConditionEvaluator userGroupsLenCondition  = createScriptConditionEvaluator("_ctx.request.userGroups.length == 1");
-		RangerScriptConditionEvaluator userRolesLenCondition   = createScriptConditionEvaluator("_ctx.request.userRoles.length == 1");
+		RangerScriptConditionEvaluator userGroupsLenCondition  = createScriptConditionEvaluator("_ctx.request.userGroups.length == 2");
+		RangerScriptConditionEvaluator userGroupsHas1Condition = createScriptConditionEvaluator("_ctx.request.userGroups.indexOf('test-group1') != -1");
+		RangerScriptConditionEvaluator userGroupsHas2Condition = createScriptConditionEvaluator("_ctx.request.userGroups.indexOf('test-group2') != -1");
+		RangerScriptConditionEvaluator userRolesLenCondition   = createScriptConditionEvaluator("_ctx.request.userRoles.length == 2");
+		RangerScriptConditionEvaluator userRolesHas1Condition  = createScriptConditionEvaluator("_ctx.request.userRoles.indexOf('test-role1') != -1");
+		RangerScriptConditionEvaluator userRolesHas2Condition  = createScriptConditionEvaluator("_ctx.request.userRoles.indexOf('test-role2') != -1");
+		RangerScriptConditionEvaluator userAttrLenCondition    = createScriptConditionEvaluator("Object.keys(_ctx.request.userAttributes).length == 3");
+		RangerScriptConditionEvaluator userAttr1Condition      = createScriptConditionEvaluator("_ctx.request.userAttributes['attr1'].equals('test-user-value1')");
+		RangerScriptConditionEvaluator userAttr2Condition      = createScriptConditionEvaluator("_ctx.request.userAttributes['attr2'].equals('test-user-value2')");
+		RangerScriptConditionEvaluator userGroup1Attr1Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group1']['attr1'].equals('test-group1-value1')");
+		RangerScriptConditionEvaluator userGroup1Attr2Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group1']['attr2'].equals('test-group1-value2')");
+		RangerScriptConditionEvaluator userGroup2Attr1Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group2']['attr1'].equals('test-group2-value1')");
+		RangerScriptConditionEvaluator userGroup2Attr2Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group2']['attr2'].equals('test-group2-value2')");
 		RangerScriptConditionEvaluator tagsLengthCondition     = createScriptConditionEvaluator("_ctx.tags.length == 2");
-		RangerScriptConditionEvaluator tagTypeCondition        = createScriptConditionEvaluator("_ctx.tag.type.equals('PCI')");
-		RangerScriptConditionEvaluator tagAttributesCondition  = createScriptConditionEvaluator("_ctx.tag.attributes.attr1.equals('PCI_value')");
-		RangerScriptConditionEvaluator tagsTypeCondition       = createScriptConditionEvaluator("switch(_ctx.tags[0].type) { case 'PCI': _ctx.tags[1].type.equals('PII'); break; case 'PII': _ctx.tags[1].type.equals('PCI'); break; default: false; }");
-		RangerScriptConditionEvaluator tagsAttributesCondition = createScriptConditionEvaluator("switch(_ctx.tags[0].type) { case 'PCI': _ctx.tags[0].attributes.attr1.equals('PCI_value') && _ctx.tags[1].attributes.attr1.equals('PII_value'); break; case 'PII': _ctx.tags[0].attributes.attr1.equals('PII_value') && _ctx.tags[1].attributes.attr1.equals('PCI_value'); break; default: false; }");
+		RangerScriptConditionEvaluator tagTypeCondition        = createScriptConditionEvaluator("_ctx.tag._type.equals('PCI')");
+		RangerScriptConditionEvaluator tagAttributesCondition  = createScriptConditionEvaluator("_ctx.tag.attr1.equals('PCI_value')");
+		RangerScriptConditionEvaluator tagsTypeCondition       = createScriptConditionEvaluator("switch(_ctx.tags[0]._type) { case 'PCI': _ctx.tags[1]._type.equals('PII'); break; case 'PII': _ctx.tags[1]._type.equals('PCI'); break; default: false; }");
+		RangerScriptConditionEvaluator tagsAttributesCondition = createScriptConditionEvaluator("switch(_ctx.tags[0]._type) { case 'PCI': _ctx.tags[0].attr1.equals('PCI_value') && _ctx.tags[1].attr1.equals('PII_value'); break; case 'PII': _ctx.tags[0].attr1.equals('PII_value') && _ctx.tags[1].attr1.equals('PCI_value'); break; default: false; }");
 
 		Assert.assertTrue("request.resource.database should be db1", resourceDbCondition.isMatched(request));
 		Assert.assertTrue("request.resource.database should not be db2", resourceDbCondition2.isMatched(request));
@@ -76,10 +88,21 @@ public class RangerCustomConditionMatcherTest {
 		Assert.assertTrue("request.accessType should be select", accessTypeCondition.isMatched(request));
 		Assert.assertTrue("request.action should be query", actionCondition.isMatched(request));
 		Assert.assertTrue("request.user should be testUser", userCondition.isMatched(request));
-		Assert.assertTrue("request.userGroups should have 1 entry", userGroupsLenCondition.isMatched(request));
-		Assert.assertTrue("request.userRoles should have 1 entry", userRolesLenCondition.isMatched(request));
-		Assert.assertTrue("tag.type should be PCI", tagTypeCondition.isMatched(request));
-		Assert.assertTrue("tag.attributes.attr1 should be PCI_value", tagAttributesCondition.isMatched(request));
+		Assert.assertTrue("request.userGroups should have 2 entries", userGroupsLenCondition.isMatched(request));
+		Assert.assertTrue("request.userGroups should have test-group1", userGroupsHas1Condition.isMatched(request));
+		Assert.assertTrue("request.userGroups should have test-group2", userGroupsHas2Condition.isMatched(request));
+		Assert.assertTrue("request.userRoles should have 2 entries", userRolesLenCondition.isMatched(request));
+		Assert.assertTrue("request.userRoles should have test-role1", userRolesHas1Condition.isMatched(request));
+		Assert.assertTrue("request.userRoles should have test-role2", userRolesHas2Condition.isMatched(request));
+		Assert.assertTrue("request.userAttributes should have 3 entries", userAttrLenCondition.isMatched(request));
+		Assert.assertTrue("request.userAttributes[attr1] should be test-user-value1", userAttr1Condition.isMatched(request));
+		Assert.assertTrue("request.userAttributes[attr2] should be test-user-value2", userAttr2Condition.isMatched(request));
+		Assert.assertTrue("request.userGroup1Attributes[attr1] should be test-group1-value1", userGroup1Attr1Condition.isMatched(request));
+		Assert.assertTrue("request.userGroup1Attributes[attr2] should be test-group1-value2", userGroup1Attr2Condition.isMatched(request));
+		Assert.assertTrue("request.userGroup2Attributes[attr1] should be test-group2-value1", userGroup2Attr1Condition.isMatched(request));
+		Assert.assertTrue("request.userGroup2Attributes[attr2] should be test-group2-value2", userGroup2Attr2Condition.isMatched(request));
+		Assert.assertTrue("tag._type should be PCI", tagTypeCondition.isMatched(request));
+		Assert.assertTrue("tag.attr1 should be PCI_value", tagAttributesCondition.isMatched(request));
 		Assert.assertTrue("should have 2 tags", tagsLengthCondition.isMatched(request));
 		Assert.assertTrue("tags PCI and PII should be found", tagsTypeCondition.isMatched(request));
 		Assert.assertTrue("tag attributes for PCI and PII should be found", tagsAttributesCondition.isMatched(request));
@@ -204,12 +227,15 @@ public class RangerCustomConditionMatcherTest {
 	RangerScriptConditionEvaluator createScriptConditionEvaluator(String script) {
 		RangerScriptConditionEvaluator ret = new RangerScriptConditionEvaluator();
 
+		RangerServiceDef          serviceDef   = mock(RangerServiceDef.class);
 		RangerPolicyConditionDef  conditionDef = mock(RangerPolicyConditionDef.class);
 		RangerPolicyItemCondition condition    = mock(RangerPolicyItemCondition.class);
 
+		when(serviceDef.getName()).thenReturn("test");
 		when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap(SCRIPT_OPTION_ENABLE_JSON_CTX, "true"));
 		when(condition.getValues()).thenReturn(Arrays.asList(script));
 
+		ret.setServiceDef(serviceDef);
 		ret.setConditionDef(conditionDef);
 		ret.setPolicyItemCondition(condition);
 
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
new file mode 100644
index 0000000..798da05
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.conditionevaluator;
+
+import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
+import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResource;
+import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator;
+import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher;
+import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
+import org.apache.ranger.plugin.util.RangerUserStore;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RangerRequestScriptEvaluatorTest {
+    final String              engineName   = "JavaScript";
+    final ScriptEngineManager manager      = new ScriptEngineManager();
+    final ScriptEngine        scriptEngine = manager.getEngineByName(engineName);
+
+    @Test
+    public void testRequestAttributes() {
+        RangerAccessRequest          request   = createRequest(Arrays.asList("PII", "PCI"));
+        RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request);
+
+        Assert.assertTrue("test: USER._name is 'test-user'", (Boolean) evaluator.evaluateScript(scriptEngine, "USER._name == 'test-user'"));
+        Assert.assertTrue("test: USER['state'] is 'CA'", (Boolean) evaluator.evaluateScript(scriptEngine, "USER['state'] == 'CA'"));
+        Assert.assertTrue("test: USER.state is 'CA'", (Boolean) evaluator.evaluateScript(scriptEngine, "USER.state == 'CA'"));
+
+        Assert.assertTrue("test: UGROUPS has test-group1", (Boolean)evaluator.evaluateScript(scriptEngine, "UGROUPS.indexOf('test-group1') != -1"));
+        Assert.assertTrue("test: UGROUPS has test-group2", (Boolean)evaluator.evaluateScript(scriptEngine, "UGROUPS.indexOf('test-group2') != -1"));
+        Assert.assertTrue("test: UGROUPS doesn't have test-group3", (Boolean)evaluator.evaluateScript(scriptEngine, "UGROUPS.indexOf('test-group3') == -1"));
+        Assert.assertTrue("test: UGROUP['test-group1'].dept is 'ENGG'", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group1'].dept == 'ENGG'"));
+        Assert.assertTrue("test: UGROUP['test-group1'].site is 10", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group1'].site == 10"));
+        Assert.assertTrue("test: UGROUP['test-group2'].dept is 'PROD'", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group2'].dept == 'PROD'"));
+        Assert.assertTrue("test: UGROUP['test-group2'].site is 20", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group2'].site == 20"));
+        Assert.assertTrue("test: UGROUP['test-group3'] is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group3'] == null"));
+        Assert.assertTrue("test: UGROUP['test-group1'].notExists is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group1'].notExists == null"));
+
+        Assert.assertTrue("test: UROLES has test-role1", (Boolean)evaluator.evaluateScript(scriptEngine, "UROLES.indexOf('test-role1') != -1"));
+        Assert.assertTrue("test: UROLES has test-role2", (Boolean)evaluator.evaluateScript(scriptEngine, "UROLES.indexOf('test-role2') != -1"));
+        Assert.assertTrue("test: UROLES doesn't have test-role3", (Boolean)evaluator.evaluateScript(scriptEngine, "UROLES.indexOf('test-role3') == -1"));
+
+        Assert.assertTrue("test: UGA.sVal['dept'] is 'ENGG'", (Boolean)evaluator.evaluateScript(scriptEngine, "UGA.sVal['dept'] == 'ENGG'"));
+        Assert.assertTrue("test: UGA.sVal['site'] is 10", (Boolean) evaluator.evaluateScript(scriptEngine, "UGA.sVal['site'] == 10"));
+        Assert.assertTrue("test: UGA.sVal['notExists'] is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UGA.sVal['notExists'] == null"));
+        Assert.assertTrue("test: UGA.mVal['dept'] is [\"ENGG\", \"PROD\"]", (Boolean) evaluator.evaluateScript(scriptEngine, "J(UGA.mVal['dept']) == '[\"ENGG\",\"PROD\"]'"));
+        Assert.assertTrue("test: UGA.mVal['site'] is [10, 20]", (Boolean) evaluator.evaluateScript(scriptEngine, "J(UGA.mVal['site']) == '[\"10\",\"20\"]'"));
+        Assert.assertTrue("test: UGA.mVal['notExists'] is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UGA.mVal['notExists'] == null"));
+        Assert.assertTrue("test: UGA.mVal['dept'] has 'ENGG'", (Boolean) evaluator.evaluateScript(scriptEngine, "UGA.mVal['dept'].indexOf('ENGG') != -1"));
+        Assert.assertTrue("test: UGA.mVal['dept'] has 'PROD'", (Boolean) evaluator.evaluateScript(scriptEngine, "UGA.mVal['dept'].indexOf('PROD') != -1"));
+        Assert.assertTrue("test: UGA.mVal['dept'] doesn't have 'EXEC'", (Boolean) evaluator.evaluateScript(scriptEngine, "UGA.mVal['dept'].indexOf('EXEC') == -1"));
+
+        Assert.assertTrue("test: REQ.accessTyp is 'select'", (Boolean) evaluator.evaluateScript(scriptEngine, "REQ.accessType == 'select'"));
+        Assert.assertTrue("test: REQ.action is 'query'", (Boolean) evaluator.evaluateScript(scriptEngine, "REQ.action == 'query'"));
+
+        Assert.assertTrue("test: RES._ownerUser is 'testUser'", (Boolean) evaluator.evaluateScript(scriptEngine, "RES._ownerUser == 'testUser'"));
+        Assert.assertTrue("test: RES.database is 'db1'", (Boolean) evaluator.evaluateScript(scriptEngine, "RES.database == 'db1'"));
+        Assert.assertTrue("test: RES.table is 'tbl1'", (Boolean) evaluator.evaluateScript(scriptEngine, "RES.table == 'tbl1'"));
+        Assert.assertTrue("test: RES.column is 'col1'", (Boolean) evaluator.evaluateScript(scriptEngine, "RES.column == 'col1'"));
+
+        Assert.assertTrue("test: TAG._type is 'PII'", (Boolean) evaluator.evaluateScript(scriptEngine, "TAG._type == 'PII'"));
+        Assert.assertTrue("test: TAG.attr1 is 'PII_value'", (Boolean) evaluator.evaluateScript(scriptEngine, "TAG.attr1 == 'PII_value'"));
+        Assert.assertTrue("test: TAGS.length is 2", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGS.length == 2"));
+        Assert.assertTrue("test: TAGS has PII and PCI", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGS[0]._type == 'PCI' ? TAGS[1]._type == 'PII' : TAGS[0]._type == 'PII' && TAGS[1]._type == 'PCI'"));
+        Assert.assertTrue("test: TAGS has PII.attr1=PII_value and PCI.attr1=PCI_value", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGS[0]._type == 'PCI' ? (TAGS[0].attr1 == 'PCI_value' && TAGS[1].attr1 == 'PII_value') : (TAGS[0].attr1 == 'PII_value' && TAGS[1].attr1 == 'PCI_value')"));
+
+        Assert.assertTrue("test: TAGNAMES.length is 2", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGNAMES.length == 2"));
+        Assert.assertTrue("test: TAGNAMES has 'PII'", (Boolean)evaluator.evaluateScript(scriptEngine, "TAGNAMES.indexOf('PII') != -1"));
+        Assert.assertTrue("test: TAGNAMES has 'PCI'", (Boolean)evaluator.evaluateScript(scriptEngine, "TAGNAMES.indexOf('PCI') != -1"));
+    }
+
+
+    RangerAccessRequest createRequest(List<String> resourceTags) {
+        RangerAccessResource resource = mock(RangerAccessResource.class);
+
+        Map<String, Object> resourceMap = new HashMap<>();
+
+        resourceMap.put("database", "db1");
+        resourceMap.put("table", "tbl1");
+        resourceMap.put("column", "col1");
+
+        when(resource.getAsString()).thenReturn("db1/tbl1/col1");
+        when(resource.getOwnerUser()).thenReturn("testUser");
+        when(resource.getAsMap()).thenReturn(resourceMap);
+        when(resource.getReadOnlyCopy()).thenReturn(resource);
+
+        RangerAccessRequestImpl request = new RangerAccessRequestImpl();
+
+        request.setResource(resource);
+        request.setResourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF);
+        request.setAccessType("select");
+        request.setAction("query");
+        request.setUser("test-user");
+        request.setUserGroups(new HashSet<>(Arrays.asList("test-group1", "test-group2")));
+        request.setUserRoles(new HashSet<>(Arrays.asList("test-role1", "test-role2")));
+
+        RangerAccessRequestUtil.setCurrentResourceInContext(request.getContext(), resource);
+
+        if (resourceTags != null) {
+            Set<RangerTagForEval> rangerTagForEvals = new HashSet<>();
+            RangerTagForEval      currentTag        = null;
+
+            for (String resourceTag : resourceTags) {
+                RangerTag tag        = new RangerTag(UUID.randomUUID().toString(), resourceTag, Collections.singletonMap("attr1", resourceTag + "_value"), null, null, null);
+                RangerTagForEval tagForEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF);
+
+                rangerTagForEvals.add(tagForEval);
+
+                if (currentTag == null) {
+                    currentTag = tagForEval;
+                }
+            }
+
+            RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), rangerTagForEvals);
+            RangerAccessRequestUtil.setCurrentTagInContext(request.getContext(), currentTag);
+        }  else {
+            RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), null);
+        }
+
+        RangerUserStore userStore = mock(RangerUserStore.class);
+
+        RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore);
+
+        Map<String, Map<String, String>> userAttrMapping  = Collections.singletonMap("test-user", Collections.singletonMap("state", "CA"));
+        Map<String, Map<String, String>> groupAttrMapping = new HashMap<>();
+
+        groupAttrMapping.put("test-group1", new HashMap<String, String>() {{ put("dept", "ENGG"); put("site", "10"); }});
+        groupAttrMapping.put("test-group2", new HashMap<String, String>() {{ put("dept", "PROD"); put("site", "20"); }});
+
+        when(userStore.getUserAttrMapping()).thenReturn(userAttrMapping);
+        when(userStore.getGroupAttrMapping()).thenReturn(groupAttrMapping);
+
+        return request;
+    }
+
+}
\ No newline at end of file

[ranger] 02/02: RANGER-3550: enhancement to support use of user/tag attributes in row-filter/condition expressions

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 52a855892fab138652255b9f492c06725c82d7be
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Sun Nov 28 16:59:27 2021 -0800

    RANGER-3550: enhancement to support use of user/tag attributes in row-filter/condition expressions
    
    (cherry picked from commit e198d01e3c23b89c9fdbcb2915931a4714185b4e)
---
 .../RangerScriptConditionEvaluator.java            |  12 +-
 .../policyengine/RangerRequestScriptEvaluator.java |  20 +--
 .../RangerDefaultRowFilterPolicyItemEvaluator.java |  30 +++-
 .../ranger/plugin/util/RangerCommonConstants.java  |   6 +-
 .../plugin/util/RangerRequestExprResolver.java     |  88 +++++++++++
 .../ranger/plugin/util/ScriptEngineUtil.java       |   2 +-
 .../RangerCustomConditionMatcherTest.java          |  35 ++++-
 .../RangerRequestScriptEvaluatorTest.java          |  35 +++--
 .../plugin/util/RangerRequestExprResolverTest.java | 170 +++++++++++++++++++++
 9 files changed, 357 insertions(+), 41 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java
index b94225b..2b6b75f 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java
@@ -38,7 +38,7 @@ public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvalu
 	private static final Log LOG = LogFactory.getLog(RangerScriptConditionEvaluator.class);
 
 	private ScriptEngine scriptEngine;
-	private boolean      enableJsonCtx = false;
+	private Boolean      enableJsonCtx = null;
 
 	@Override
 	public void init() {
@@ -54,7 +54,11 @@ public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvalu
 		if (MapUtils.isNotEmpty(evalOptions)) {
 			engineName = evalOptions.get("engineName");
 
-			enableJsonCtx = Boolean.parseBoolean(evalOptions.getOrDefault(SCRIPT_OPTION_ENABLE_JSON_CTX, Boolean.toString(enableJsonCtx)));
+			String strEnableJsonCtx = evalOptions.get(SCRIPT_OPTION_ENABLE_JSON_CTX);
+
+			if (StringUtils.isNotEmpty(strEnableJsonCtx)) {
+				enableJsonCtx = Boolean.parseBoolean(strEnableJsonCtx);
+			}
 		}
 
 		if (StringUtils.isBlank(engineName)) {
@@ -98,6 +102,10 @@ public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvalu
 
 				RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request);
 
+				if (enableJsonCtx == null) { // if not specified in evaluatorOptions, set it on first call to isMatched()
+					enableJsonCtx = RangerRequestScriptEvaluator.needsJsonCtxEnabled(script);
+				}
+
 				evaluator.evaluateConditionScript(scriptEngine, script, enableJsonCtx);
 
 				result = evaluator.getResult();
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 64f01c9..d99a7d5 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
@@ -69,10 +69,10 @@ public final class RangerRequestScriptEvaluator {
                                                                                  SCRIPT_VAR_REQ + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_REQUEST + ";" +
                                                                                  SCRIPT_VAR_RES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_RESOURCE + ";" +
                                                                                  SCRIPT_VAR_USER + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_ATTRIBUTES + ";" +
-                                                                                 SCRIPT_VAR_UGROUPS + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUPS + ";" +
-                                                                                 SCRIPT_VAR_UGROUP + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUP_ATTRIBUTES + ";" +
+                                                                                 SCRIPT_VAR_UGNAMES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUPS + ";" +
+                                                                                 SCRIPT_VAR_UG + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUP_ATTRIBUTES + ";" +
                                                                                  SCRIPT_VAR_UGA + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_UGA + ";" +
-                                                                                 SCRIPT_VAR_UROLES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_ROLES + ";" +
+                                                                                 SCRIPT_VAR_URNAMES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_ROLES + ";" +
                                                                                  SCRIPT_VAR_TAG + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAG + ";" +
                                                                                  SCRIPT_VAR_TAGS + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAGS + ";" +
                                                                                  SCRIPT_VAR_TAGNAMES + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAG_NAMES + ";";
@@ -262,11 +262,11 @@ public final class RangerRequestScriptEvaluator {
 		Set<RangerTagForEval> requestTags = RangerAccessRequestUtil.getRequestTagsFromContext(getRequestContext());
 
 		if (CollectionUtils.isNotEmpty(requestTags)) {
-			Set<Map<String, Object>> tags     = new HashSet<>();
-			Set<String>              tagNames = new HashSet<>();
+			Map<String, Map<String, Object>> tags     = new HashMap();
+			Set<String>                      tagNames = new HashSet<>();
 
 			for (RangerTagForEval tag : requestTags) {
-				tags.add(toMap(tag));
+				tags.put(tag.getType(), toMap(tag));
 				tagNames.add(tag.getType());
 			}
 
@@ -279,7 +279,7 @@ public final class RangerRequestScriptEvaluator {
 				ret.put(SCRIPT_FIELD_TAG, toMap(currentTag));
 			}
 		} else {
-			ret.put(SCRIPT_FIELD_TAGS, Collections.emptySet());
+			ret.put(SCRIPT_FIELD_TAGS, Collections.emptyMap());
 			ret.put(SCRIPT_FIELD_TAG_NAMES, Collections.emptySet());
 		}
 
@@ -659,9 +659,9 @@ public final class RangerRequestScriptEvaluator {
 		varNames.add(SCRIPT_VAR_TAGNAMES);
 		varNames.add(SCRIPT_VAR_TAGS);
 		varNames.add(SCRIPT_VAR_UGA);
-		varNames.add(SCRIPT_VAR_UGROUP);
-		varNames.add(SCRIPT_VAR_UGROUPS);
-		varNames.add(SCRIPT_VAR_UROLES);
+		varNames.add(SCRIPT_VAR_UG);
+		varNames.add(SCRIPT_VAR_UGNAMES);
+		varNames.add(SCRIPT_VAR_URNAMES);
 		varNames.add(SCRIPT_VAR_USER);
 
 		return ".*(" + StringUtils.join(varNames, '|') + ").*";
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java
index 0831dde..841fee3 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java
@@ -25,15 +25,32 @@ import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.policyengine.RangerAccessResult;
 import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions;
 import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher;
+import org.apache.ranger.plugin.util.RangerRequestExprResolver;
 
 
 public class RangerDefaultRowFilterPolicyItemEvaluator extends RangerDefaultPolicyItemEvaluator implements RangerRowFilterPolicyItemEvaluator {
 	final private RangerRowFilterPolicyItem rowFilterPolicyItem;
+	final private String                    rowFilterExpr;
+	final private RangerRequestExprResolver exprResolver;
 
 	public RangerDefaultRowFilterPolicyItemEvaluator(RangerServiceDef serviceDef, RangerPolicy policy, RangerRowFilterPolicyItem policyItem, int policyItemIndex, RangerPolicyEngineOptions options) {
 		super(serviceDef, policy, policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DATAMASK, policyItemIndex, options);
 
 		rowFilterPolicyItem = policyItem;
+
+		RangerPolicyItemRowFilterInfo rowFilterInfo = getRowFilterInfo();
+
+		if (rowFilterInfo != null && rowFilterInfo.getFilterExpr() != null) {
+			rowFilterExpr = rowFilterInfo.getFilterExpr();
+		} else {
+			rowFilterExpr = null;
+		}
+
+		if (rowFilterExpr != null && RangerRequestExprResolver.hasExpressions(rowFilterExpr)) {
+			exprResolver = new RangerRequestExprResolver(rowFilterExpr, getServiceType());
+		} else {
+			exprResolver = null;
+		}
 	}
 
 	@Override
@@ -43,11 +60,16 @@ public class RangerDefaultRowFilterPolicyItemEvaluator extends RangerDefaultPoli
 
 	@Override
 	public void updateAccessResult(RangerPolicyEvaluator policyEvaluator, RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType) {
-		RangerPolicyItemRowFilterInfo rowFilterInfo = getRowFilterInfo();
+		if (result.getFilterExpr() == null) {
+			if (exprResolver != null) {
+				result.setFilterExpr(exprResolver.resolveExpressions(result.getAccessRequest()));
+			} else if (rowFilterExpr != null) {
+				result.setFilterExpr(rowFilterExpr);
+			}
 
-		if (result.getFilterExpr() == null && rowFilterInfo != null) {
-			result.setFilterExpr(rowFilterInfo.getFilterExpr());
-			policyEvaluator.updateAccessResult(result, matchType, true, getComments());
+			if (result.getFilterExpr() != null) {
+				policyEvaluator.updateAccessResult(result, matchType, true, getComments());
+			}
 		}
 	}
 }
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
index 63bed50..4152170 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java
@@ -63,9 +63,9 @@ public class RangerCommonConstants {
 	public static final String SCRIPT_VAR_TAGNAMES                  = "TAGNAMES";
 	public static final String SCRIPT_VAR_TAGS                      = "TAGS";
 	public static final String SCRIPT_VAR_UGA                       = "UGA";
-	public static final String SCRIPT_VAR_UGROUP                    = "UGROUP";
-	public static final String SCRIPT_VAR_UGROUPS                   = "UGROUPS";
-	public static final String SCRIPT_VAR_UROLES                    = "UROLES";
+	public static final String SCRIPT_VAR_UG                        = "UG";
+	public static final String SCRIPT_VAR_UGNAMES                   = "UGNAMES";
+	public static final String SCRIPT_VAR_URNAMES                   = "URNAMES";
 	public static final String SCRIPT_VAR_USER                      = "USER";
 
 	public static final String SCRIPT_FIELD_ACCESS_TIME             = "accessTime";
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
new file mode 100644
index 0000000..d70430b
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java
@@ -0,0 +1,88 @@
+/*
+ * 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.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator;
+
+import javax.script.ScriptEngine;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class RangerRequestExprResolver {
+    private static final Log LOG = LogFactory.getLog(RangerRequestExprResolver.class);
+
+    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 + ">.*?)\\}\\}");
+
+    private final String  str;
+    private final String  serviceType;
+    private final boolean hasTokens;
+
+
+    public RangerRequestExprResolver(String str, String serviceType) {
+        this.str         = str;
+        this.serviceType = serviceType;
+        this.hasTokens   = hasExpressions(str);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("RangerRequestExprResolver(" + str + "): hasTokens=" + hasTokens);
+        }
+    }
+
+    public String resolveExpressions(RangerAccessRequest request) {
+        String ret = str;
+
+        if (hasTokens) {
+            RangerRequestScriptEvaluator scriptEvaluator = new RangerRequestScriptEvaluator(request);
+            ScriptEngine                 scriptEngine    = ScriptEngineUtil.createScriptEngine(SCRIPT_ENGINE_NAME, serviceType);
+            StringBuffer                 sb              = new StringBuffer();
+            Matcher                      matcher         = PATTERN.matcher(str);
+
+            while (matcher.find()) {
+                String expr = matcher.group(REGEX_GROUP_EXPR);
+                String val  = Objects.toString(scriptEvaluator.evaluateScript(scriptEngine, expr));
+
+                matcher.appendReplacement(sb, val);
+            }
+
+            matcher.appendTail(sb);
+
+            ret = sb.toString();
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("RangerRequestExprResolver.processExpressions(" + str + "): ret=" + ret);
+            }
+        }
+
+        return ret;
+    }
+
+    public static boolean hasExpressions(String str) {
+        Matcher matcher = PATTERN.matcher(str);
+
+        return matcher.find();
+    }
+}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
index b0782ec..983c578 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
@@ -58,7 +58,7 @@ public class ScriptEngineUtil {
             LOG.error("RangerScriptConditionEvaluator.init() failed with exception=" + exp);
         }
 
-        if (ret == null) {
+        if (ret == null && serviceType != null) {
             LOG.warn("failed to initialize script engine '" + engineName + "' in a default manner." +
                      " Will try to get script-engine from plugin-class-loader");
 
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java
index d5f1564..5b98574 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java
@@ -30,6 +30,7 @@ import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
 import org.apache.ranger.plugin.policyengine.RangerAccessResource;
 import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
+import org.apache.ranger.plugin.util.RangerUserStore;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -75,11 +76,11 @@ public class RangerCustomConditionMatcherTest {
 		RangerScriptConditionEvaluator userGroup1Attr2Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group1']['attr2'].equals('test-group1-value2')");
 		RangerScriptConditionEvaluator userGroup2Attr1Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group2']['attr1'].equals('test-group2-value1')");
 		RangerScriptConditionEvaluator userGroup2Attr2Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group2']['attr2'].equals('test-group2-value2')");
-		RangerScriptConditionEvaluator tagsLengthCondition     = createScriptConditionEvaluator("_ctx.tags.length == 2");
+		RangerScriptConditionEvaluator tagsLengthCondition     = createScriptConditionEvaluator("Object.keys(_ctx.tags).length == 2");
 		RangerScriptConditionEvaluator tagTypeCondition        = createScriptConditionEvaluator("_ctx.tag._type.equals('PCI')");
 		RangerScriptConditionEvaluator tagAttributesCondition  = createScriptConditionEvaluator("_ctx.tag.attr1.equals('PCI_value')");
-		RangerScriptConditionEvaluator tagsTypeCondition       = createScriptConditionEvaluator("switch(_ctx.tags[0]._type) { case 'PCI': _ctx.tags[1]._type.equals('PII'); break; case 'PII': _ctx.tags[1]._type.equals('PCI'); break; default: false; }");
-		RangerScriptConditionEvaluator tagsAttributesCondition = createScriptConditionEvaluator("switch(_ctx.tags[0]._type) { case 'PCI': _ctx.tags[0].attr1.equals('PCI_value') && _ctx.tags[1].attr1.equals('PII_value'); break; case 'PII': _ctx.tags[0].attr1.equals('PII_value') && _ctx.tags[1].attr1.equals('PCI_value'); break; default: false; }");
+		RangerScriptConditionEvaluator tagsTypeCondition       = createScriptConditionEvaluator("_ctx.tags['PII']._type == 'PII' && _ctx.tags['PCI']._type == 'PCI'");
+		RangerScriptConditionEvaluator tagsAttributesCondition = createScriptConditionEvaluator("_ctx.tags['PII'].attr1.equals('PII_value') && _ctx.tags['PCI'].attr1.equals('PCI_value')");
 
 		Assert.assertTrue("request.resource.database should be db1", resourceDbCondition.isMatched(request));
 		Assert.assertTrue("request.resource.database should not be db2", resourceDbCondition2.isMatched(request));
@@ -265,8 +266,8 @@ public class RangerCustomConditionMatcherTest {
 		request.setAccessType("select");
 		request.setAction("query");
 		request.setUser("test-user");
-		request.setUserGroups(Collections.singleton("test-group"));
-		request.setUserRoles(Collections.singleton("test-role"));
+		request.setUserGroups(new HashSet<>(Arrays.asList("test-group1", "test-group2")));
+		request.setUserRoles(new HashSet<>(Arrays.asList("test-role1", "test-role2")));
 
 		if (resourceTags != null) {
 			Set<RangerTagForEval> rangerTagForEvals = new HashSet<>();
@@ -289,6 +290,30 @@ public class RangerCustomConditionMatcherTest {
 			RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), null);
 		}
 
+		Map<String, Map<String, String>> userAttrMapping  = new HashMap<>();
+		Map<String, Map<String, String>> groupAttrMapping = new HashMap<>();
+		Map<String, String>              testUserAttrs    = new HashMap<>();
+		Map<String, String>              testGroup1Attrs  = new HashMap<>();
+		Map<String, String>              testGroup2Attrs  = new HashMap<>();
+
+		testUserAttrs.put("attr1", "test-user-value1");
+		testUserAttrs.put("attr2", "test-user-value2");
+		testGroup1Attrs.put("attr1", "test-group1-value1");
+		testGroup1Attrs.put("attr2", "test-group1-value2");
+		testGroup2Attrs.put("attr1", "test-group2-value1");
+		testGroup2Attrs.put("attr2", "test-group2-value2");
+
+		userAttrMapping.put("test-user", testUserAttrs);
+		groupAttrMapping.put("test-group1", testGroup1Attrs);
+		groupAttrMapping.put("test-group2", testGroup2Attrs);
+
+		RangerUserStore userStore = mock(RangerUserStore.class);
+
+		when(userStore.getUserAttrMapping()).thenReturn(userAttrMapping);
+		when(userStore.getGroupAttrMapping()).thenReturn(groupAttrMapping);
+
+		RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore);
+
 		return request;
 	}
 }
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
index 798da05..ff638b2 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java
@@ -59,19 +59,20 @@ public class RangerRequestScriptEvaluatorTest {
         Assert.assertTrue("test: USER['state'] is 'CA'", (Boolean) evaluator.evaluateScript(scriptEngine, "USER['state'] == 'CA'"));
         Assert.assertTrue("test: USER.state is 'CA'", (Boolean) evaluator.evaluateScript(scriptEngine, "USER.state == 'CA'"));
 
-        Assert.assertTrue("test: UGROUPS has test-group1", (Boolean)evaluator.evaluateScript(scriptEngine, "UGROUPS.indexOf('test-group1') != -1"));
-        Assert.assertTrue("test: UGROUPS has test-group2", (Boolean)evaluator.evaluateScript(scriptEngine, "UGROUPS.indexOf('test-group2') != -1"));
-        Assert.assertTrue("test: UGROUPS doesn't have test-group3", (Boolean)evaluator.evaluateScript(scriptEngine, "UGROUPS.indexOf('test-group3') == -1"));
-        Assert.assertTrue("test: UGROUP['test-group1'].dept is 'ENGG'", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group1'].dept == 'ENGG'"));
-        Assert.assertTrue("test: UGROUP['test-group1'].site is 10", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group1'].site == 10"));
-        Assert.assertTrue("test: UGROUP['test-group2'].dept is 'PROD'", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group2'].dept == 'PROD'"));
-        Assert.assertTrue("test: UGROUP['test-group2'].site is 20", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group2'].site == 20"));
-        Assert.assertTrue("test: UGROUP['test-group3'] is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group3'] == null"));
-        Assert.assertTrue("test: UGROUP['test-group1'].notExists is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UGROUP['test-group1'].notExists == null"));
-
-        Assert.assertTrue("test: UROLES has test-role1", (Boolean)evaluator.evaluateScript(scriptEngine, "UROLES.indexOf('test-role1') != -1"));
-        Assert.assertTrue("test: UROLES has test-role2", (Boolean)evaluator.evaluateScript(scriptEngine, "UROLES.indexOf('test-role2') != -1"));
-        Assert.assertTrue("test: UROLES doesn't have test-role3", (Boolean)evaluator.evaluateScript(scriptEngine, "UROLES.indexOf('test-role3') == -1"));
+        Assert.assertTrue("test: UGNAMES has test-group1", (Boolean)evaluator.evaluateScript(scriptEngine, "UGNAMES.indexOf('test-group1') != -1"));
+        Assert.assertTrue("test: UGNAMES has test-group2", (Boolean)evaluator.evaluateScript(scriptEngine, "UGNAMES.indexOf('test-group2') != -1"));
+        Assert.assertTrue("test: UGNAMES doesn't have test-group3", (Boolean)evaluator.evaluateScript(scriptEngine, "UGNAMES.indexOf('test-group3') == -1"));
+
+        Assert.assertTrue("test: UG['test-group1'].dept is 'ENGG'", (Boolean) evaluator.evaluateScript(scriptEngine, "UG['test-group1'].dept == 'ENGG'"));
+        Assert.assertTrue("test: UG['test-group1'].site is 10", (Boolean) evaluator.evaluateScript(scriptEngine, "UG['test-group1'].site == 10"));
+        Assert.assertTrue("test: UG['test-group2'].dept is 'PROD'", (Boolean) evaluator.evaluateScript(scriptEngine, "UG['test-group2'].dept == 'PROD'"));
+        Assert.assertTrue("test: UG['test-group2'].site is 20", (Boolean) evaluator.evaluateScript(scriptEngine, "UG['test-group2'].site == 20"));
+        Assert.assertTrue("test: UG['test-group3'] is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UG['test-group3'] == null"));
+        Assert.assertTrue("test: UG['test-group1'].notExists is null", (Boolean) evaluator.evaluateScript(scriptEngine, "UG['test-group1'].notExists == null"));
+
+        Assert.assertTrue("test: URNAMES has test-role1", (Boolean)evaluator.evaluateScript(scriptEngine, "URNAMES.indexOf('test-role1') != -1"));
+        Assert.assertTrue("test: URNAMES has test-role2", (Boolean)evaluator.evaluateScript(scriptEngine, "URNAMES.indexOf('test-role2') != -1"));
+        Assert.assertTrue("test: URNAMES doesn't have test-role3", (Boolean)evaluator.evaluateScript(scriptEngine, "URNAMES.indexOf('test-role3') == -1"));
 
         Assert.assertTrue("test: UGA.sVal['dept'] is 'ENGG'", (Boolean)evaluator.evaluateScript(scriptEngine, "UGA.sVal['dept'] == 'ENGG'"));
         Assert.assertTrue("test: UGA.sVal['site'] is 10", (Boolean) evaluator.evaluateScript(scriptEngine, "UGA.sVal['site'] == 10"));
@@ -93,9 +94,11 @@ public class RangerRequestScriptEvaluatorTest {
 
         Assert.assertTrue("test: TAG._type is 'PII'", (Boolean) evaluator.evaluateScript(scriptEngine, "TAG._type == 'PII'"));
         Assert.assertTrue("test: TAG.attr1 is 'PII_value'", (Boolean) evaluator.evaluateScript(scriptEngine, "TAG.attr1 == 'PII_value'"));
-        Assert.assertTrue("test: TAGS.length is 2", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGS.length == 2"));
-        Assert.assertTrue("test: TAGS has PII and PCI", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGS[0]._type == 'PCI' ? TAGS[1]._type == 'PII' : TAGS[0]._type == 'PII' && TAGS[1]._type == 'PCI'"));
-        Assert.assertTrue("test: TAGS has PII.attr1=PII_value and PCI.attr1=PCI_value", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGS[0]._type == 'PCI' ? (TAGS[0].attr1 == 'PCI_value' && TAGS[1].attr1 == 'PII_value') : (TAGS[0].attr1 == 'PII_value' && TAGS[1].attr1 == 'PCI_value')"));
+        Assert.assertTrue("test: TAGS.length is 2", (Boolean) evaluator.evaluateScript(scriptEngine, "Object.keys(TAGS).length == 2"));
+        Assert.assertEquals("test: TAGS has PII", evaluator.evaluateScript(scriptEngine, "TAGS.PII._type"), "PII");
+        Assert.assertEquals("test: TAGS has PCI", evaluator.evaluateScript(scriptEngine, "TAGS.PCI._type"), "PCI");
+        Assert.assertEquals("test: TAGS has PII.attr1=PII_value", evaluator.evaluateScript(scriptEngine, "TAGS['PII'].attr1"), "PII_value");
+        Assert.assertEquals("test: TAGS has PCI.attr1=PCI_value", evaluator.evaluateScript(scriptEngine, "TAGS['PCI'].attr1"), "PCI_value");
 
         Assert.assertTrue("test: TAGNAMES.length is 2", (Boolean) evaluator.evaluateScript(scriptEngine, "TAGNAMES.length == 2"));
         Assert.assertTrue("test: TAGNAMES has 'PII'", (Boolean)evaluator.evaluateScript(scriptEngine, "TAGNAMES.indexOf('PII') != -1"));
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerRequestExprResolverTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerRequestExprResolverTest.java
new file mode 100644
index 0000000..6d5edc0
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerRequestExprResolverTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.util;
+
+import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
+import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResource;
+import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.*;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RangerRequestExprResolverTest {
+    @Test
+    public void testRequestAttributes() {
+        RangerAccessRequest request = createRequest(Arrays.asList("PII", "PCI"));
+
+        Map<String, String> exprValue = new HashMap<>();
+
+        exprValue.put("s3://mybucket/users/${{USER._name}}/${{USER.state}}/test.txt", "s3://mybucket/users/test-user/CA/test.txt");
+        exprValue.put("state == '${{USER.state}}' AND dept == '${{UGA.sVal.dept}}'", "state == 'CA' AND dept == 'ENGG'");
+        exprValue.put("attr1 == '${{TAG.attr1}}'", "attr1 == 'PII_value'");
+
+        exprValue.put("${{USER._name}}", "test-user");
+        exprValue.put("${{USER['state']}}", "CA");
+        exprValue.put("${{USER.state}}", "CA");
+
+        exprValue.put("${{UGNAMES.indexOf('test-group1') != -1}}", "true");
+        exprValue.put("${{UGNAMES.indexOf('test-group2') != -1}}", "true");
+        exprValue.put("${{UGNAMES.indexOf('test-group3') == -1}}", "true");
+
+        exprValue.put("${{UG['test-group1'].dept}}", "ENGG");
+        exprValue.put("${{UG['test-group1'].site}}", "10");
+        exprValue.put("${{UG['test-group2'].dept}}", "PROD");
+        exprValue.put("${{UG['test-group2'].site}}", "20");
+        exprValue.put("${{UG['test-group3']}}", "null");
+        exprValue.put("${{UG['test-group1'].notExists}}", "null");
+
+        exprValue.put("${{URNAMES.indexOf('test-role1') != -1}}", "true");
+        exprValue.put("${{URNAMES.indexOf('test-role2') != -1}}", "true");
+        exprValue.put("${{URNAMES.indexOf('test-role3') == -1}}", "true");
+
+        exprValue.put("${{UGA.sVal.dept}}", "ENGG");
+        exprValue.put("${{UGA.sVal.site}}", "10");
+        exprValue.put("${{UGA.sVal.notExists}}", "null");
+        exprValue.put("${{J(UGA.mVal.dept)}}", "[\"ENGG\",\"PROD\"]");
+        exprValue.put("${{J(UGA.mVal.site)}}", "[\"10\",\"20\"]");
+        exprValue.put("${{J(UGA.mVal.notExists)}}", "null");
+        exprValue.put("${{UGA.mVal['dept'].indexOf('ENGG') != -1}}", "true");
+        exprValue.put("${{UGA.mVal['dept'].indexOf('PROD') != -1}}", "true");
+        exprValue.put("${{UGA.mVal['dept'].indexOf('EXEC') == -1}}", "true");
+
+        exprValue.put("${{REQ.accessType}}", "select");
+        exprValue.put("${{REQ.action}}", "query");
+
+        exprValue.put("${{RES._ownerUser}}", "testUser");
+        exprValue.put("${{RES.database}}", "db1");
+        exprValue.put("${{RES.table}}", "tbl1");
+        exprValue.put("${{RES.column}}", "col1");
+
+        exprValue.put("${{TAG._type}}", "PII");
+        exprValue.put("${{TAG.attr1}}", "PII_value");
+        exprValue.put("${{Object.keys(TAGS).length}}", "2");
+        exprValue.put("${{TAGS.PCI._type}}", "PCI");
+        exprValue.put("${{TAGS.PCI.attr1}}", "PCI_value");
+        exprValue.put("${{TAGS.PII._type}}", "PII");
+        exprValue.put("${{TAGS.PII.attr1}}", "PII_value");
+
+        exprValue.put("${{TAGNAMES.length}}", "2");
+        exprValue.put("${{TAGNAMES.indexOf('PII') != -1}}", "true");
+        exprValue.put("${{TAGNAMES.indexOf('PCI') != -1}}", "true");
+
+        for (Map.Entry<String, String> entry : exprValue.entrySet()) {
+            String                    expr        = entry.getKey();
+            String                    value       = entry.getValue();
+            RangerRequestExprResolver resolver    = new RangerRequestExprResolver(expr, null);
+            String                    resolvedVal = resolver.resolveExpressions(request);
+
+            Assert.assertEquals(expr, value, resolvedVal);
+        }
+    }
+
+
+    RangerAccessRequest createRequest(List<String> resourceTags) {
+        RangerAccessResource resource = mock(RangerAccessResource.class);
+
+        Map<String, Object> resourceMap = new HashMap<>();
+
+        resourceMap.put("database", "db1");
+        resourceMap.put("table", "tbl1");
+        resourceMap.put("column", "col1");
+
+        when(resource.getAsString()).thenReturn("db1/tbl1/col1");
+        when(resource.getOwnerUser()).thenReturn("testUser");
+        when(resource.getAsMap()).thenReturn(resourceMap);
+        when(resource.getReadOnlyCopy()).thenReturn(resource);
+
+        RangerAccessRequestImpl request = new RangerAccessRequestImpl();
+
+        request.setResource(resource);
+        request.setResourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF);
+        request.setAccessType("select");
+        request.setAction("query");
+        request.setUser("test-user");
+        request.setUserGroups(new HashSet<>(Arrays.asList("test-group1", "test-group2")));
+        request.setUserRoles(new HashSet<>(Arrays.asList("test-role1", "test-role2")));
+
+        RangerAccessRequestUtil.setCurrentResourceInContext(request.getContext(), resource);
+
+        if (resourceTags != null) {
+            Set<RangerTagForEval> rangerTagForEvals = new HashSet<>();
+            RangerTagForEval      currentTag        = null;
+
+            for (String resourceTag : resourceTags) {
+                RangerTag tag        = new RangerTag(UUID.randomUUID().toString(), resourceTag, Collections.singletonMap("attr1", resourceTag + "_value"), null, null, null);
+                RangerTagForEval tagForEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF);
+
+                rangerTagForEvals.add(tagForEval);
+
+                if (currentTag == null) {
+                    currentTag = tagForEval;
+                }
+            }
+
+            RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), rangerTagForEvals);
+            RangerAccessRequestUtil.setCurrentTagInContext(request.getContext(), currentTag);
+        }  else {
+            RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), null);
+        }
+
+        RangerUserStore userStore = mock(RangerUserStore.class);
+
+        RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore);
+
+        Map<String, Map<String, String>> userAttrMapping  = Collections.singletonMap("test-user", Collections.singletonMap("state", "CA"));
+        Map<String, Map<String, String>> groupAttrMapping = new HashMap<>();
+
+        groupAttrMapping.put("test-group1", new HashMap<String, String>() {{ put("dept", "ENGG"); put("site", "10"); }});
+        groupAttrMapping.put("test-group2", new HashMap<String, String>() {{ put("dept", "PROD"); put("site", "20"); }});
+
+        when(userStore.getUserAttrMapping()).thenReturn(userAttrMapping);
+        when(userStore.getGroupAttrMapping()).thenReturn(groupAttrMapping);
+
+        return request;
+    }
+
+}
\ No newline at end of file