From cf16f5d13d41f61a748e6376c85eb4941cc4a293 Mon Sep 17 00:00:00 2001 From: Noble Paul Date: Tue, 3 May 2016 15:56:25 +0530 Subject: [PATCH] SOLR-9049: RuleBasedAuthorizationPlugin supports regex in param values eg: "command" : "REGEX:(i?)create" --- solr/CHANGES.txt | 2 + .../org/apache/solr/security/Permission.java | 42 ++++++++++++++- .../RuleBasedAuthorizationPlugin.java | 11 ++-- .../TestRuleBasedAuthorizationPlugin.java | 54 +++++++++++++++++++ 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 994203e1294..75cba3d32c2 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -122,6 +122,8 @@ New Features * SOLR-5750: Add /admin/collections?action=BACKUP and RESTORE assuming access to a shared file system. (Varun Thacker, David Smiley) +* SOLR-9049: RuleBasedAuthorizationPlugin supports regex in param values eg: "command" : "REGEX:(i?)create" (noble) + Bug Fixes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/security/Permission.java b/solr/core/src/java/org/apache/solr/security/Permission.java index 2ec1fcfbc3c..3ebf283e4a4 100644 --- a/solr/core/src/java/org/apache/solr/security/Permission.java +++ b/solr/core/src/java/org/apache/solr/security/Permission.java @@ -3,15 +3,23 @@ package org.apache.solr.security; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import com.google.common.collect.ImmutableSet; +import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.util.Pair; import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; import static org.apache.solr.common.params.CommonParams.NAME; /* @@ -35,7 +43,7 @@ import static org.apache.solr.common.params.CommonParams.NAME; class Permission { String name; Set path, role, collections, method; - Map params; + Map> params; PermissionNameProvider.Name wellknownName; private Permission() { @@ -63,7 +71,37 @@ class Permission { p.path = readSetSmart(name, m, "path"); p.collections = readSetSmart(name, m, "collection"); p.method = readSetSmart(name, m, "method"); - p.params = (Map) m.get("params"); + Map paramRules = (Map) m.get("params"); + if (paramRules != null) { + p.params = new LinkedHashMap<>(); + for (Map.Entry e : paramRules.entrySet()) { + if (e.getValue() == null) { + p.params.put(e.getKey(), (String[] val) -> val == null); + } else { + List patternStrs = e.getValue() instanceof List ? + (List) e.getValue() : + singletonList(e.getValue().toString()); + List patterns = patternStrs.stream() + .map(it -> it.startsWith("REGEX:") ? + Pattern.compile(String.valueOf(it.substring("REGEX:".length()))) + : it) + .collect(Collectors.toList()); + p.params.put(e.getKey(), val -> { + if (val == null) return false; + for (Object pattern : patterns) { + for (String s : val) { + if (pattern instanceof String) { + if (pattern.equals(s)) return true; + } else if (pattern instanceof Pattern) { + if (((Pattern) pattern).matcher(s).find()) return true; + } + } + } + return false; + }); + } + } + } return p; } diff --git a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java index 3145113c6ea..9f6d4b81655 100644 --- a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java @@ -26,12 +26,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Function; import org.apache.solr.util.CommandOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.Arrays.asList; +import static java.util.Collections.emptyIterator; import static java.util.Collections.unmodifiableMap; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; @@ -129,12 +131,9 @@ public class RuleBasedAuthorizationPlugin implements AuthorizationPlugin, Config continue; } if (permission.params != null) { - for (Map.Entry e : permission.params.entrySet()) { - String paramVal = context.getParams().get(e.getKey()); - Object val = e.getValue(); - if (val instanceof List) { - if (!((List) val).contains(paramVal)) continue loopPermissions; - } else if (!Objects.equals(val, paramVal)) continue loopPermissions; + for (Map.Entry> e : permission.params.entrySet()) { + String[] paramVal = context.getParams().getParams(e.getKey()); + if(!e.getValue().apply(paramVal)) continue loopPermissions; } } } diff --git a/solr/core/src/test/org/apache/solr/security/TestRuleBasedAuthorizationPlugin.java b/solr/core/src/test/org/apache/solr/security/TestRuleBasedAuthorizationPlugin.java index 628b4a80032..0f70d730e8b 100644 --- a/solr/core/src/test/org/apache/solr/security/TestRuleBasedAuthorizationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/TestRuleBasedAuthorizationPlugin.java @@ -19,6 +19,7 @@ package org.apache.solr.security; import java.io.IOException; import java.io.StringReader; import java.security.Principal; +import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; @@ -31,6 +32,7 @@ import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.Utils; +import org.apache.solr.handler.DumpRequestHandler; import org.apache.solr.handler.ReplicationHandler; import org.apache.solr.handler.SchemaHandler; import org.apache.solr.handler.UpdateRequestHandler; @@ -251,6 +253,58 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { "params", new MapSolrParams(singletonMap("action", "CREATE"))) ,STATUS_OK ); + rules = (Map) Utils.fromJSONString(permissions); + List permissions = (List) rules.get("permissions"); + permissions.remove(permissions.size() -1);//remove the 'all' permission + permissions.add(makeMap("name", "test-params", "role", "admin", "path", "/x", "params", + makeMap("key", Arrays.asList("REGEX:(?i)val1", "VAL2")))); + this.permissions = Utils.toJSONString(rules); + + checkRules(makeMap("resource", "/x", + "userPrincipal", null, + "requestType", RequestType.UNKNOWN, + "collectionRequests", "go", + "handler", new DumpRequestHandler(), + "params", new MapSolrParams(singletonMap("key", "VAL1"))) + , PROMPT_FOR_CREDENTIALS); + + checkRules(makeMap("resource", "/x", + "userPrincipal", null, + "requestType", RequestType.UNKNOWN, + "collectionRequests", "go", + "handler", new DumpRequestHandler(), + "params", new MapSolrParams(singletonMap("key", "Val1"))) + , PROMPT_FOR_CREDENTIALS); + + checkRules(makeMap("resource", "/x", + "userPrincipal", null, + "requestType", RequestType.UNKNOWN, + "collectionRequests", "go", + "handler", new DumpRequestHandler(), + "params", new MapSolrParams(singletonMap("key", "Val1"))) + , PROMPT_FOR_CREDENTIALS); + checkRules(makeMap("resource", "/x", + "userPrincipal", "joe", + "requestType", RequestType.UNKNOWN, + "collectionRequests", "go", + "handler", new DumpRequestHandler(), + "params", new MapSolrParams(singletonMap("key", "Val1"))) + , FORBIDDEN); + + checkRules(makeMap("resource", "/x", + "userPrincipal", "joe", + "requestType", RequestType.UNKNOWN, + "collectionRequests", "go", + "handler", new DumpRequestHandler(), + "params", new MapSolrParams(singletonMap("key", "Val2"))) + , STATUS_OK); + checkRules(makeMap("resource", "/x", + "userPrincipal", "joe", + "requestType", RequestType.UNKNOWN, + "collectionRequests", "go", + "handler", new DumpRequestHandler(), + "params", new MapSolrParams(singletonMap("key", "VAL2"))) + , FORBIDDEN); } public void testEditRules() throws IOException {