You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2022/02/04 04:05:38 UTC

[ranger] branch master updated: RANGER-3605: added support for macros in row-filter/condition expressions

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

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


The following commit(s) were added to refs/heads/master by this push:
     new a2e1ec4  RANGER-3605: added support for macros in row-filter/condition expressions
a2e1ec4 is described below

commit a2e1ec4d55d9d87367c5da75763e0a44b5231fcc
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Wed Feb 2 16:56:40 2022 -0800

    RANGER-3605: added support for macros in row-filter/condition expressions
---
 .../policyengine/RangerRequestScriptEvaluator.java | 36 ++++++++-
 .../apache/ranger/plugin/util/MacroProcessor.java  | 86 ++++++++++++++++++++++
 .../ranger/plugin/util/RangerCommonConstants.java  | 17 +++++
 .../RangerRequestScriptEvaluatorTest.java          | 38 +++++-----
 .../ranger/plugin/util/MacroProcessorTest.java     | 56 ++++++++++++++
 5 files changed, 213 insertions(+), 20 deletions(-)

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 e3ce559..9b72a1b 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
@@ -25,6 +25,7 @@ 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.util.MacroProcessor;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 import org.apache.ranger.plugin.util.RangerPerfTracer;
 import org.apache.ranger.plugin.util.RangerUserStore;
@@ -65,11 +66,12 @@ public final class RangerRequestScriptEvaluator {
                                                                                  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 final Character CHAR_QUOTE = '\'';
 	private static final Character CHAR_COMMA = ',';
 
+	private static final MacroProcessor MACRO_PROCESSOR = new MacroProcessor(getMacrosMap());
+
 	private static String[] dateFormatStrings = null;
 
 	private final RangerAccessRequest              accessRequest;
@@ -120,15 +122,23 @@ public final class RangerRequestScriptEvaluator {
 		return ret;
 	}
 
+	public static String expandMacros(String script) {
+		return MACRO_PROCESSOR.expandMacros(script);
+	}
+
 	public RangerRequestScriptEvaluator(final RangerAccessRequest accessRequest) {
 		this.accessRequest = accessRequest.getReadOnlyCopy();
 	}
 
 	public Object evaluateScript(ScriptEngine scriptEngine, String script) {
+		script = expandMacros(script);
+
 		return evaluateScript(scriptEngine, script, needsJsonCtxEnabled(script));
 	}
 
 	public Object evaluateConditionScript(ScriptEngine scriptEngine, String script, boolean enableJsonCtx) {
+		script = expandMacros(script);
+
 		Object ret = evaluateScript(scriptEngine, script, enableJsonCtx);
 
 		if (ret == null) {
@@ -872,6 +882,30 @@ public final class RangerRequestScriptEvaluator {
 		return ".*(" + StringUtils.join(varNames, '|') + ").*";
 	}
 
+	private static Map<String, String> getMacrosMap() {
+		Map<String, String> ret = new HashMap<>();
+
+		ret.put(SCRIPT_MACRO_GET_TAG_ATTR_CSV,      "ctx.tagAttrCsv");
+		ret.put(SCRIPT_MACRO_GET_TAG_ATTR_Q_CSV,    "ctx.tagAttrCsvQ");
+		ret.put(SCRIPT_MACRO_GET_UG_ATTR_CSV,       "ctx.ugAttrCsv");
+		ret.put(SCRIPT_MACRO_GET_UG_ATTR_Q_CSV,     "ctx.ugAttrCsvQ");
+		ret.put(SCRIPT_MACRO_TAG_ATTR_NAMES_CSV,    "ctx.tagAttrNamesCsv()");
+		ret.put(SCRIPT_MACRO_TAG_ATTR_NAMES_Q_CSV,  "ctx.tagAttrNamesCsvQ()");
+		ret.put(SCRIPT_MACRO_TAG_NAMES_CSV,         "ctx.tagNamesCsv()");
+		ret.put(SCRIPT_MACRO_TAG_NAMES_Q_CSV,       "ctx.tagNamesCsvQ()");
+		ret.put(SCRIPT_MACRO_UG_ATTR_NAMES_CSV,     "ctx.ugAttrNamesCsv()");
+		ret.put(SCRIPT_MACRO_UG_ATTR_NAMES_Q_CSV,   "ctx.ugAttrNamesCsvQ()");
+		ret.put(SCRIPT_MACRO_UG_NAMES_CSV,          "ctx.ugNamesCsv()");
+		ret.put(SCRIPT_MACRO_UG_NAMES_Q_CSV,        "ctx.ugNamesCsvQ()");
+		ret.put(SCRIPT_MACRO_UR_NAMES_CSV,          "ctx.urNamesCsv()");
+		ret.put(SCRIPT_MACRO_UR_NAMES_Q_CSV,        "ctx.urNamesCsvQ()");
+		ret.put(SCRIPT_MACRO_USER_ATTR_NAMES_CSV,   "ctx.userAttrNamesCsv()");
+		ret.put(SCRIPT_MACRO_USER_ATTR_NAMES_Q_CSV, "ctx.userAttrNamesCsvQ()");
+
+		return ret;
+	}
+
+
 	public void logDebug(Object msg) {
 		LOG.debug(Objects.toString(msg));
 	}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/MacroProcessor.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/MacroProcessor.java
new file mode 100644
index 0000000..7709133
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/MacroProcessor.java
@@ -0,0 +1,86 @@
+/*
+ * 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MacroProcessor {
+    private static final Logger LOG = LoggerFactory.getLogger(MacroProcessor.class);
+
+    private final Map<String, String> macrosMap;
+    private final Pattern             macrosPattern;
+
+    public MacroProcessor(Map<String, String> macrosMap) {
+        this.macrosMap     = macrosMap;
+        this.macrosPattern = getMacrosPattern(this.macrosMap);
+    }
+
+    public String expandMacros(String expr) {
+        StringBuffer ret     = null;
+        Matcher      matcher = macrosPattern.matcher(expr);
+
+        while (matcher.find()) {
+            if (ret == null) {
+                ret = new StringBuffer();
+            }
+
+            String keyword  = matcher.group();
+            String replacer = macrosMap.get(keyword);
+
+            matcher.appendReplacement(ret, replacer);
+        }
+
+        if (ret == null) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("expandMacros({}): no match found!", expr);
+            }
+
+            return expr;
+        } else {
+            matcher.appendTail(ret);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("expandMacros({}): match found. ret={}", expr, ret);
+            }
+
+            return ret.toString();
+        }
+    }
+
+    private Pattern getMacrosPattern(Map<String, String> macros) {
+        StringBuilder sb  = new StringBuilder();
+        String        sep = "\\b(";
+
+        for (String macro : macros.keySet()) {
+            sb.append(sep).append(macro);
+
+            sep = "|";
+        }
+
+        sb.append(")\\b");
+
+        return Pattern.compile(sb.toString());
+    }
+}
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 ab0e3b7..c98cce0 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
@@ -98,4 +98,21 @@ public class RangerCommonConstants {
 	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_MACRO_GET_TAG_ATTR_CSV      = "GET_TAG_ATTR_CSV";
+	public static final String SCRIPT_MACRO_GET_TAG_ATTR_Q_CSV    = "GET_TAG_ATTR_Q_CSV";
+	public static final String SCRIPT_MACRO_GET_UG_ATTR_CSV       = "GET_UG_ATTR_CSV";
+	public static final String SCRIPT_MACRO_GET_UG_ATTR_Q_CSV     = "GET_UG_ATTR_Q_CSV";
+	public static final String SCRIPT_MACRO_TAG_ATTR_NAMES_CSV    = "TAG_ATTR_NAMES_CSV";
+	public static final String SCRIPT_MACRO_TAG_ATTR_NAMES_Q_CSV  = "TAG_ATTR_NAMES_Q_CSV";
+	public static final String SCRIPT_MACRO_TAG_NAMES_CSV         = "TAG_NAMES_CSV";
+	public static final String SCRIPT_MACRO_TAG_NAMES_Q_CSV       = "TAG_NAMES_Q_CSV";
+	public static final String SCRIPT_MACRO_UG_ATTR_NAMES_CSV     = "UG_ATTR_NAMES_CSV";
+	public static final String SCRIPT_MACRO_UG_ATTR_NAMES_Q_CSV   = "UG_ATTR_NAMES_Q_CSV";
+	public static final String SCRIPT_MACRO_UG_NAMES_CSV          = "UG_NAMES_CSV";
+	public static final String SCRIPT_MACRO_UG_NAMES_Q_CSV        = "UG_NAMES_Q_CSV";
+	public static final String SCRIPT_MACRO_UR_NAMES_CSV          = "UR_NAMES_CSV";
+	public static final String SCRIPT_MACRO_UR_NAMES_Q_CSV        = "UR_NAMES_Q_CSV";
+	public static final String SCRIPT_MACRO_USER_ATTR_NAMES_CSV   = "USER_ATTR_NAMES_CSV";
+	public static final String SCRIPT_MACRO_USER_ATTR_NAMES_Q_CSV = "USER_ATTR_NAMES_Q_CSV";
 }
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 2a0b027..7bbad78 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
@@ -55,25 +55,25 @@ public class RangerRequestScriptEvaluatorTest {
         RangerAccessRequest          request   = createRequest(Arrays.asList("PII", "PCI"));
         RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request);
 
-        Assert.assertEquals("test: ctx.ugNamesCsv()", "test-group1,test-group2", evaluator.evaluateScript(scriptEngine, "ctx.ugNamesCsv()"));
-        Assert.assertEquals("test: ctx.urNamesCsv()", "test-role1,test-role2", evaluator.evaluateScript(scriptEngine, "ctx.urNamesCsv()"));
-        Assert.assertEquals("test: ctx.tagNamesCsv()", "PCI,PII", evaluator.evaluateScript(scriptEngine, "ctx.tagNamesCsv()"));
-        Assert.assertEquals("test: ctx.userAttrNamesCsv()", "state", evaluator.evaluateScript(scriptEngine, "ctx.userAttrNamesCsv()"));
-        Assert.assertEquals("test: ctx.ugAttrNamesCsv()", "dept,site", evaluator.evaluateScript(scriptEngine, "ctx.ugAttrNamesCsv()"));
-        Assert.assertEquals("test: ctx.tagAttrNamesCsv()", "attr1", evaluator.evaluateScript(scriptEngine, "ctx.tagAttrNamesCsv()"));
-        Assert.assertEquals("test: ctx.ugAttrCsv('dept')", "ENGG,PROD", evaluator.evaluateScript(scriptEngine, "ctx.ugAttrCsv('dept')"));
-        Assert.assertEquals("test: ctx.ugAttrCsv('site')", "10,20", evaluator.evaluateScript(scriptEngine, "ctx.ugAttrCsv('site')"));
-        Assert.assertEquals("test: ctx.tagAttrCsv('attr1')", "PCI_value,PII_value", evaluator.evaluateScript(scriptEngine, "ctx.tagAttrCsv('attr1')"));
-
-        Assert.assertEquals("test: ctx.ugNamesCsvQ()", "'test-group1','test-group2'", evaluator.evaluateScript(scriptEngine, "ctx.ugNamesCsvQ()"));
-        Assert.assertEquals("test: ctx.urNamesCsvQ()", "'test-role1','test-role2'", evaluator.evaluateScript(scriptEngine, "ctx.urNamesCsvQ()"));
-        Assert.assertEquals("test: ctx.tagNamesCsvQ()", "'PCI','PII'", evaluator.evaluateScript(scriptEngine, "ctx.tagNamesCsvQ()"));
-        Assert.assertEquals("test: ctx.userAttrNamesCsvQ()", "'state'", evaluator.evaluateScript(scriptEngine, "ctx.userAttrNamesCsvQ()"));
-        Assert.assertEquals("test: ctx.ugAttrNamesCsvQ()", "'dept','site'", evaluator.evaluateScript(scriptEngine, "ctx.ugAttrNamesCsvQ()"));
-        Assert.assertEquals("test: ctx.tagAttrNamesCsvQ()", "'attr1'", evaluator.evaluateScript(scriptEngine, "ctx.tagAttrNamesCsvQ()"));
-        Assert.assertEquals("test: ctx.ugAttrCsvQ('dept')", "'ENGG','PROD'", evaluator.evaluateScript(scriptEngine, "ctx.ugAttrCsvQ('dept')"));
-        Assert.assertEquals("test: ctx.ugAttrCsvQ('site')", "'10','20'", evaluator.evaluateScript(scriptEngine, "ctx.ugAttrCsvQ('site')"));
-        Assert.assertEquals("test: ctx.tagAttrCsvQ('attr1')", "'PCI_value','PII_value'", evaluator.evaluateScript(scriptEngine, "ctx.tagAttrCsvQ('attr1')"));
+        Assert.assertEquals("test: UG_NAMES_CSV", "test-group1,test-group2", evaluator.evaluateScript(scriptEngine, "UG_NAMES_CSV"));
+        Assert.assertEquals("test: UR_NAMES_CSV", "test-role1,test-role2", evaluator.evaluateScript(scriptEngine, "UR_NAMES_CSV"));
+        Assert.assertEquals("test: TAG_NAMES_CSV", "PCI,PII", evaluator.evaluateScript(scriptEngine, "TAG_NAMES_CSV"));
+        Assert.assertEquals("test: USER_ATTR_NAMES_CSV", "state", evaluator.evaluateScript(scriptEngine, "USER_ATTR_NAMES_CSV"));
+        Assert.assertEquals("test: UG_ATTR_NAMES_CSV", "dept,site", evaluator.evaluateScript(scriptEngine, "UG_ATTR_NAMES_CSV"));
+        Assert.assertEquals("test: TAG_ATTR_NAMES_CSV", "attr1", evaluator.evaluateScript(scriptEngine, "TAG_ATTR_NAMES_CSV"));
+        Assert.assertEquals("test: GET_UG_ATTR_CSV('dept')", "ENGG,PROD", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_CSV('dept')"));
+        Assert.assertEquals("test: GET_UG_ATTR_CSV('site')", "10,20", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_CSV('site')"));
+        Assert.assertEquals("test: GET_TAG_ATTR_CSV('attr1')", "PCI_value,PII_value", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_CSV('attr1')"));
+
+        Assert.assertEquals("test: UG_NAMES_Q_CSV", "'test-group1','test-group2'", evaluator.evaluateScript(scriptEngine, "UG_NAMES_Q_CSV"));
+        Assert.assertEquals("test: UR_NAMES_Q_CSV", "'test-role1','test-role2'", evaluator.evaluateScript(scriptEngine, "UR_NAMES_Q_CSV"));
+        Assert.assertEquals("test: TAG_NAMES_Q_CSV", "'PCI','PII'", evaluator.evaluateScript(scriptEngine, "TAG_NAMES_Q_CSV"));
+        Assert.assertEquals("test: USER_ATTR_NAMES_Q_CSV", "'state'", evaluator.evaluateScript(scriptEngine, "USER_ATTR_NAMES_Q_CSV"));
+        Assert.assertEquals("test: UG_ATTR_NAMES_Q_CSV", "'dept','site'", evaluator.evaluateScript(scriptEngine, "UG_ATTR_NAMES_Q_CSV"));
+        Assert.assertEquals("test: TAG_ATTR_NAMES_Q_CSV", "'attr1'", evaluator.evaluateScript(scriptEngine, "TAG_ATTR_NAMES_Q_CSV"));
+        Assert.assertEquals("test: GET_UG_ATTR_Q_CSV('dept')", "'ENGG','PROD'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q_CSV('dept')"));
+        Assert.assertEquals("test: GET_UG_ATTR_Q_CSV('site')", "'10','20'", evaluator.evaluateScript(scriptEngine, "GET_UG_ATTR_Q_CSV('site')"));
+        Assert.assertEquals("test: GET_TAG_ATTR_Q_CSV('attr1')", "'PCI_value','PII_value'", evaluator.evaluateScript(scriptEngine, "GET_TAG_ATTR_Q_CSV('attr1')"));
 
         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'"));
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/MacroProcessorTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/MacroProcessorTest.java
new file mode 100644
index 0000000..7573c7f
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/MacroProcessorTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public class MacroProcessorTest {
+
+	@Test
+	public void testMacros() {
+		Map<String, String> macros = new HashMap<>();
+
+		macros.put("USER", "getUser()");
+		macros.put("USER_NAME", "getUserName()");
+		macros.put("GROUPS", "getGroups()");
+		macros.put("GET_GROUP_ATTR", "getGroupAttr");
+
+		MacroProcessor processor = new MacroProcessor(macros);
+
+		Map<String, String> testInputOutput = new HashMap<>();
+
+		testInputOutput.putAll(macros);
+		testInputOutput.put("USER != null", "getUser() != null");
+		testInputOutput.put("USER_NAME=='testUser'", "getUserName()=='testUser'");
+		testInputOutput.put("USER != null && USER_NAME=='testUser'", "getUser() != null && getUserName()=='testUser'");
+		testInputOutput.put("GROUPS.length() > 1", "getGroups().length() > 1");
+		testInputOutput.put("GET_GROUP_ATTR('group1', 'attr1')", "getGroupAttr('group1', 'attr1')");
+
+		for (Map.Entry<String, String> inputOutput : testInputOutput.entrySet()) {
+			String input  = inputOutput.getKey();
+			String output = inputOutput.getValue();
+
+			assertEquals(output, processor.expandMacros(input));;
+		}
+	}
+}