You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2016/03/18 09:30:34 UTC

[47/50] lucene-solr git commit: SOLR-8842: security rules made more foolproof by asking the requesthandler about the well known permission name.
 The APIs are also modified to ue 'index' as the unique identifier instead of name. Name is an optional

SOLR-8842: security rules made more foolproof by asking the requesthandler  about the well known
  permission name.
  The APIs are also modified to ue 'index' as the unique identifier instead of name.
  Name is an optional attribute
  now and only to be used when specifying well-known permissions


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/faa0586b
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/faa0586b
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/faa0586b

Branch: refs/heads/apiv2
Commit: faa0586b31d5644360646010ceaf530cbe227498
Parents: a22099a
Author: Noble Paul <no...@apache.org>
Authored: Thu Mar 17 23:34:50 2016 +0530
Committer: Noble Paul <no...@apache.org>
Committed: Thu Mar 17 23:34:50 2016 +0530

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   7 +
 .../extraction/ExtractingRequestHandler.java    |   9 +-
 .../org/apache/solr/handler/SQLHandler.java     |   9 +-
 .../org/apache/solr/handler/SchemaHandler.java  |  16 +-
 .../apache/solr/handler/SolrConfigHandler.java  |  18 +-
 .../org/apache/solr/handler/StreamHandler.java  |   9 +-
 .../solr/handler/UpdateRequestHandler.java      |   9 +-
 .../solr/handler/admin/CollectionsHandler.java  |  15 +-
 .../solr/handler/admin/SecurityConfHandler.java |  16 +-
 .../solr/handler/component/SearchHandler.java   |  11 +-
 .../solr/security/AuthorizationContext.java     |   3 +
 .../security/AutorizationEditOperation.java     | 176 +++++++++++
 .../org/apache/solr/security/Permission.java    | 119 ++++++++
 .../solr/security/PermissionNameProvider.java   |  72 +++++
 .../security/RuleBasedAuthorizationPlugin.java  | 294 ++-----------------
 .../org/apache/solr/servlet/HttpSolrCall.java   |   5 +
 .../org/apache/solr/util/CommandOperation.java  |  27 ++
 .../src/resources/WellKnownPermissions.json     |  47 ---
 .../handler/admin/SecurityConfHandlerTest.java  |  30 +-
 .../solr/security/BasicAuthIntegrationTest.java |   3 +-
 .../solr/security/MockAuthorizationPlugin.java  |  10 +-
 .../TestRuleBasedAuthorizationPlugin.java       |  99 ++++++-
 22 files changed, 651 insertions(+), 353 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 3863ceb..6f86b52 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -30,6 +30,9 @@ Detailed Change List
 * SOLR-8765: Enforce required parameters at query construction time in the SolrJ
   Collections API, add static factory methods, and deprecate old setter methods.
   (Alan Woodward, Jason Gerlowski)
+* SOLR-8842: authorization APIs do not use name as an identifier for  a permission
+  for update, delete
  commands and 'before' attribute (noble)
+
 
 New Features
 ----------------------
@@ -69,6 +72,10 @@ Other Changes
 * SOLR-8866: UpdateLog will now throw an exception if it doesn't know how to serialize a value.
   (David Smiley)
 
+* SOLR-8842: security rules made more foolproof by asking the requesthandler  about the well known
+  permission name.
  The APIs are also modified to ue 'index' as the unique identifier instead of name.
+  Name is an optional attribute
  now and only to be used when specifying well-known permissions (noble)
+
 ==================  6.0.0 ==================
 
 Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/ExtractingRequestHandler.java
----------------------------------------------------------------------
diff --git a/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/ExtractingRequestHandler.java b/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/ExtractingRequestHandler.java
index d1bd4c7..af70c62 100644
--- a/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/ExtractingRequestHandler.java
+++ b/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/ExtractingRequestHandler.java
@@ -23,6 +23,8 @@ import org.apache.solr.common.util.DateUtil;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.solr.update.processor.UpdateRequestProcessor;
 import org.apache.solr.util.plugin.SolrCoreAware;
 import org.apache.solr.handler.ContentStreamHandlerBase;
@@ -45,7 +47,7 @@ import java.util.Map;
  * Handler for rich documents like PDF or Word or any other file format that Tika handles that need the text to be extracted
  * first from the document.
  */
-public class ExtractingRequestHandler extends ContentStreamHandlerBase implements SolrCoreAware {
+public class ExtractingRequestHandler extends ContentStreamHandlerBase implements SolrCoreAware , PermissionNameProvider {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
@@ -62,6 +64,11 @@ public class ExtractingRequestHandler extends ContentStreamHandlerBase implement
 
 
   @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext request) {
+    return PermissionNameProvider.Name.READ_PERM;
+  }
+
+  @Override
   public void init(NamedList args) {
     super.init(args);
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/SQLHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/SQLHandler.java b/solr/core/src/java/org/apache/solr/handler/SQLHandler.java
index fe83187..11fee93 100644
--- a/solr/core/src/java/org/apache/solr/handler/SQLHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SQLHandler.java
@@ -59,6 +59,8 @@ import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.solr.util.plugin.SolrCoreAware;
 
 import java.util.List;
@@ -70,7 +72,7 @@ import org.slf4j.LoggerFactory;
 
 import com.facebook.presto.sql.parser.SqlParser;
 
-public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
+public class SQLHandler extends RequestHandlerBase implements SolrCoreAware , PermissionNameProvider {
 
   private static String defaultZkhost = null;
   private static String defaultWorkerCollection = null;
@@ -93,6 +95,11 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
     }
   }
 
+  @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext request) {
+    return PermissionNameProvider.Name.READ_PERM;
+  }
+
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
     SolrParams params = req.getParams();
     params = adjustParams(params);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
index 4279864..da24c25 100644
--- a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
@@ -42,13 +42,15 @@ import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.ManagedIndexSchema;
 import org.apache.solr.schema.SchemaManager;
 import org.apache.solr.schema.ZkIndexSchemaReader;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.solr.util.plugin.SolrCoreAware;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static org.apache.solr.common.params.CommonParams.JSON;
 
-public class SchemaHandler extends RequestHandlerBase implements SolrCoreAware {
+public class SchemaHandler extends RequestHandlerBase implements SolrCoreAware, PermissionNameProvider {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private boolean isImmutableConfigSet = false;
 
@@ -99,6 +101,18 @@ public class SchemaHandler extends RequestHandlerBase implements SolrCoreAware {
     }
   }
 
+  @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) {
+    switch (ctx.getHttpMethod()) {
+      case "GET":
+        return PermissionNameProvider.Name.SCHEMA_READ_PERM;
+      case "POST":
+        return PermissionNameProvider.Name.SCHEMA_EDIT_PERM;
+      default:
+        return null;
+    }
+  }
+
   private void handleGET(SolrQueryRequest req, SolrQueryResponse rsp) {
     try {
       String path = (String) req.getContext().get("path");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
index b73273a..eac9b11 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -37,7 +37,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
@@ -68,6 +67,9 @@ import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestHandler;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.schema.SchemaManager;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
+import org.apache.solr.security.PermissionNameProvider.Name;
 import org.apache.solr.util.CommandOperation;
 import org.apache.solr.util.DefaultSolrThreadFactory;
 import org.apache.solr.util.RTimer;
@@ -88,7 +90,7 @@ import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME;
 import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME_IN_OVERLAY;
 import static org.apache.solr.schema.FieldType.CLASS_NAME;
 
-public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAware {
+public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAware, PermissionNameProvider {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   public static final String CONFIGSET_EDITING_DISABLED_ARG = "disable.configEdit";
   public static final boolean configEditing_disabled = Boolean.getBoolean(CONFIGSET_EDITING_DISABLED_ARG);
@@ -745,6 +747,18 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
     return activeReplicaCoreUrls;
   }
 
+  @Override
+  public Name getPermissionName(AuthorizationContext ctx) {
+    switch (ctx.getHttpMethod()) {
+      case "GET":
+        return Name.CONFIG_READ_PERM;
+      case "POST":
+        return Name.CONFIG_EDIT_PERM;
+      default:
+        return null;
+    }
+  }
+
   private static class PerReplicaCallable extends SolrRequest implements Callable<Boolean> {
     String coreUrl;
     String prop;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/StreamHandler.java b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
index 113fa93..c7bac9f 100644
--- a/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
@@ -50,11 +50,13 @@ import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.solr.util.plugin.SolrCoreAware;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class StreamHandler extends RequestHandlerBase implements SolrCoreAware {
+public class StreamHandler extends RequestHandlerBase implements SolrCoreAware, PermissionNameProvider {
 
   static SolrClientCache clientCache = new SolrClientCache();
   private StreamFactory streamFactory = new StreamFactory();
@@ -62,6 +64,11 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware {
   private String coreName;
   private Map<String, DaemonStream> daemons = new HashMap();
 
+  @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext request) {
+    return PermissionNameProvider.Name.READ_PERM;
+  }
+
   public void inform(SolrCore core) {
     
     /* The stream factory will always contain the zkUrl for the given collection

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java
index b8d2a89..7c97331 100644
--- a/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java
@@ -36,14 +36,17 @@ import org.apache.solr.handler.loader.JsonLoader;
 import org.apache.solr.handler.loader.XMLLoader;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.solr.update.processor.UpdateRequestProcessor;
 
 import static org.apache.solr.common.params.CommonParams.PATH;
+import static org.apache.solr.security.PermissionNameProvider.Name.UPDATE_PERM;
 
 /**
  * UpdateHandler that uses content-type to pick the right Loader
  */
-public class UpdateRequestHandler extends ContentStreamHandlerBase {
+public class UpdateRequestHandler extends ContentStreamHandlerBase implements PermissionNameProvider {
 
   // XML Constants
   public static final String ADD = "add";
@@ -150,6 +153,10 @@ public class UpdateRequestHandler extends ContentStreamHandlerBase {
     return registry;
   }
 
+  @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) {
+    return UPDATE_PERM;
+  }
 
   @Override
   protected ContentStreamLoader newLoader(SolrQueryRequest req, final UpdateRequestProcessor processor) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index b4d0a1d..f0a332e 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -62,6 +62,7 @@ import org.apache.solr.common.cloud.ZkCoreNodeProps;
 import org.apache.solr.common.cloud.ZkNodeProps;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.params.CollectionAdminParams;
+import org.apache.solr.common.params.CollectionParams;
 import org.apache.solr.common.params.CollectionParams.CollectionAction;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.CoreAdminParams;
@@ -77,6 +78,8 @@ import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.component.ShardHandler;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
@@ -122,7 +125,7 @@ import static org.apache.solr.common.params.CoreAdminParams.INSTANCE_DIR;
 import static org.apache.solr.common.params.ShardParams._ROUTE_;
 import static org.apache.solr.common.util.StrUtils.formatString;
 
-public class CollectionsHandler extends RequestHandlerBase {
+public class CollectionsHandler extends RequestHandlerBase implements PermissionNameProvider {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   protected final CoreContainer coreContainer;
@@ -144,6 +147,16 @@ public class CollectionsHandler extends RequestHandlerBase {
     this.coreContainer = coreContainer;
   }
 
+  @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) {
+    String action = ctx.getParams().get("action");
+    if (action == null) return null;
+    CollectionParams.CollectionAction collectionAction = CollectionParams.CollectionAction.get(action);
+    if (collectionAction == null) return null;
+    return collectionAction.isWrite ?
+        PermissionNameProvider.Name.COLL_EDIT_PERM :
+        PermissionNameProvider.Name.COLL_READ_PERM;
+  }
 
   @Override
   final public void init(NamedList args) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
index 285ea65..0f4dd7b 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
@@ -33,11 +33,13 @@ import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.SolrConfigHandler;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
 import org.apache.solr.security.ConfigEditablePlugin;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.solr.util.CommandOperation;
 import org.apache.zookeeper.KeeperException;
 
-public class SecurityConfHandler extends RequestHandlerBase {
+public class SecurityConfHandler extends RequestHandlerBase implements PermissionNameProvider {
   private CoreContainer cores;
 
   public SecurityConfHandler(CoreContainer coreContainer) {
@@ -45,6 +47,18 @@ public class SecurityConfHandler extends RequestHandlerBase {
   }
 
   @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) {
+    switch (ctx.getHttpMethod()) {
+      case "GET":
+        return PermissionNameProvider.Name.SECURITY_READ_PERM;
+      case "POST":
+        return PermissionNameProvider.Name.SECURITY_EDIT_PERM;
+      default:
+        return null;
+    }
+  }
+
+  @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
     SolrConfigHandler.setWt(req, CommonParams.JSON);
     String httpMethod = (String) req.getContext().get("httpMethod");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java b/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
index d882033..eb15cee 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
@@ -26,7 +26,6 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.lucene.index.ExitableDirectoryReader;
-import org.apache.lucene.util.Version;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.cloud.ZkController;
 import org.apache.solr.common.SolrDocumentList;
@@ -45,6 +44,8 @@ import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.search.SolrQueryTimeoutImpl;
 import org.apache.solr.search.facet.FacetModule;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
 import org.apache.solr.util.RTimerTree;
 import org.apache.solr.util.SolrPluginUtils;
 import org.apache.solr.util.plugin.PluginInfoInitialized;
@@ -60,8 +61,7 @@ import static org.apache.solr.common.params.CommonParams.PATH;
  * Refer SOLR-281
  *
  */
-public class SearchHandler extends RequestHandlerBase implements SolrCoreAware , PluginInfoInitialized
-{
+public class SearchHandler extends RequestHandlerBase implements SolrCoreAware , PluginInfoInitialized, PermissionNameProvider {
   static final String INIT_COMPONENTS = "components";
   static final String INIT_FIRST_COMPONENTS = "first-components";
   static final String INIT_LAST_COMPONENTS = "last-components";
@@ -98,6 +98,11 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware ,
     }
   }
 
+  @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) {
+    return PermissionNameProvider.Name.READ_PERM;
+  }
+
   /**
    * Initialize the components based on name.  Note, if using <code>INIT_FIRST_COMPONENTS</code> or <code>INIT_LAST_COMPONENTS</code>,
    * then the {@link DebugComponent} will always occur last.  If this is not desired, then one must explicitly declare all components using

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java b/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java
index 7e65a63..8f0fa57 100644
--- a/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java
+++ b/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java
@@ -21,6 +21,7 @@ import java.util.Enumeration;
 import java.util.List;
 
 import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.request.SolrRequestHandler;
 
 /**
  * Request context for Solr to be used by Authorization plugin.
@@ -57,4 +58,6 @@ public abstract class AuthorizationContext {
 
   public enum RequestType {READ, WRITE, ADMIN, UNKNOWN}
 
+  public abstract Object getHandler();
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/security/AutorizationEditOperation.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/AutorizationEditOperation.java b/solr/core/src/java/org/apache/solr/security/AutorizationEditOperation.java
new file mode 100644
index 0000000..9deae6d
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/security/AutorizationEditOperation.java
@@ -0,0 +1,176 @@
+package org.apache.solr.security;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.solr.util.CommandOperation;
+
+import static org.apache.solr.common.params.CommonParams.NAME;
+import static org.apache.solr.common.util.Utils.getDeepCopy;
+import static org.apache.solr.handler.admin.SecurityConfHandler.getListValue;
+import static org.apache.solr.handler.admin.SecurityConfHandler.getMapValue;
+
+/*
+ * 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.
+ */
+
+
+enum AutorizationEditOperation {
+  SET_USER_ROLE("set-user-role") {
+    @Override
+    public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+      Map<String, Object> roleMap = getMapValue(latestConf, "user-role");
+      Map<String, Object> map = op.getDataMap();
+      if (op.hasError()) return null;
+      for (Map.Entry<String, Object> e : map.entrySet()) {
+        if (e.getValue() == null) {
+          roleMap.remove(e.getKey());
+          continue;
+        }
+        if (e.getValue() instanceof String || e.getValue() instanceof List) {
+          roleMap.put(e.getKey(), e.getValue());
+        } else {
+          op.addError("Unexpected value ");
+          return null;
+        }
+      }
+      return latestConf;
+    }
+  },
+  SET_PERMISSION("set-permission") {
+    @Override
+    public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+      Integer index = op.getInt("index", null);
+      Integer beforeIdx = op.getInt("before",null);
+      Map<String, Object> dataMap = op.getDataMap();
+      if (op.hasError()) return null;
+      dataMap = getDeepCopy(dataMap, 3);
+      dataMap.remove("before");
+      if (beforeIdx != null && index != null) {
+        op.addError("Cannot use 'index' and 'before together ");
+        return null;
+      }
+
+      for (String key : dataMap.keySet()) {
+        if (!Permission.knownKeys.contains(key)) op.addError("Unknown key, " + key);
+      }
+      try {
+        Permission.load(dataMap);
+      } catch (Exception e) {
+        op.addError(e.getMessage());
+        return null;
+      }
+      if(op.hasError()) return null;
+      List<Map> permissions = getListValue(latestConf, "permissions");
+      setIndex(permissions);
+      List<Map> permissionsCopy = new ArrayList<>();
+      boolean beforeSatisfied = beforeIdx == null;
+      boolean indexSatisfied = index == null;
+      for (int i = 0; i < permissions.size(); i++) {
+        Map perm = permissions.get(i);
+        Integer thisIdx = (int) perm.get("index");
+        if (thisIdx.equals(beforeIdx)) {
+          beforeSatisfied = true;
+          permissionsCopy.add(dataMap);
+          permissionsCopy.add(perm);
+        } else if (thisIdx.equals(index)) {
+          //overwriting an existing one
+          indexSatisfied = true;
+          permissionsCopy.add(dataMap);
+        } else {
+          permissionsCopy.add(perm);
+        }
+      }
+
+      if (!beforeSatisfied) {
+        op.addError("Invalid 'before' :" + beforeIdx);
+        return null;
+      }
+      if (!indexSatisfied) {
+        op.addError("Invalid 'index' :" + index);
+        return null;
+      }
+
+      if (!permissionsCopy.contains(dataMap)) permissionsCopy.add(dataMap);
+      latestConf.put("permissions", permissionsCopy);
+      setIndex(permissionsCopy);
+      return latestConf;
+    }
+
+  },
+  UPDATE_PERMISSION("update-permission") {
+    @Override
+    public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+      Integer index = op.getInt("index");
+      if (op.hasError()) return null;
+      List<Map> permissions = (List<Map>) getListValue(latestConf, "permissions");
+      setIndex(permissions);
+      for (Map permission : permissions) {
+        if (index.equals(permission.get("index"))) {
+          LinkedHashMap copy = new LinkedHashMap<>(permission);
+          copy.putAll(op.getDataMap());
+          op.setCommandData(copy);
+          return SET_PERMISSION.edit(latestConf, op);
+        }
+      }
+      op.addError("No such permission " + name);
+      return null;
+    }
+  },
+  DELETE_PERMISSION("delete-permission") {
+    @Override
+    public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+      Integer id = op.getInt("");
+      if(op.hasError()) return null;
+      List<Map> p = getListValue(latestConf, "permissions");
+      setIndex(p);
+      List<Map> c = p.stream().filter(map -> !id.equals(map.get("index"))).collect(Collectors.toList());
+      if(c.size() == p.size()){
+        op.addError("No such index :"+ id);
+        return null;
+      }
+      latestConf.put("permissions", c);
+      return latestConf;
+    }
+  };
+
+  public abstract Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op);
+
+  public final String name;
+
+  public String getOperationName() {
+    return name;
+  }
+
+  AutorizationEditOperation(String s) {
+    this.name = s;
+  }
+
+  public static AutorizationEditOperation get(String name) {
+    for (AutorizationEditOperation o : values()) if (o.name.equals(name)) return o;
+    return null;
+  }
+
+  static void setIndex(List<Map> permissionsCopy) {
+    AtomicInteger counter = new AtomicInteger(0);
+    permissionsCopy.stream().forEach(map -> map.put("index", counter.incrementAndGet()));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/security/Permission.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/Permission.java b/solr/core/src/java/org/apache/solr/security/Permission.java
new file mode 100644
index 0000000..2ec1fcf
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/security/Permission.java
@@ -0,0 +1,119 @@
+package org.apache.solr.security;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CommonParams;
+
+import static java.util.Collections.singleton;
+import static org.apache.solr.common.params.CommonParams.NAME;
+
+/*
+ * 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.
+ */
+
+
+class Permission {
+  String name;
+  Set<String> path, role, collections, method;
+  Map<String, Object> params;
+  PermissionNameProvider.Name wellknownName;
+
+  private Permission() {
+  }
+
+  static Permission load(Map m) {
+    Permission p = new Permission();
+    String name = (String) m.get(NAME);
+    if (!m.containsKey("role")) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "role not specified");
+    p.role = readValueAsSet(m, "role");
+    if (PermissionNameProvider.Name.get(name)!= null) {
+      p.wellknownName = PermissionNameProvider.Name.get(name);
+      HashSet<String> disAllowed = new HashSet<>(knownKeys);
+      disAllowed.remove("role");//these are the only
+      disAllowed.remove(NAME);//allowed keys for well-known permissions
+      disAllowed.remove("collection");//allowed keys for well-known permissions
+      disAllowed.remove("index");
+      for (String s : disAllowed) {
+        if (m.containsKey(s))
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, s + " is not a valid key for the permission : " + name);
+      }
+
+    }
+    p.name = name;
+    p.path = readSetSmart(name, m, "path");
+    p.collections = readSetSmart(name, m, "collection");
+    p.method = readSetSmart(name, m, "method");
+    p.params = (Map<String, Object>) m.get("params");
+    return p;
+  }
+
+  /**
+   * This checks for the defaults available other rules for the keys
+   */
+  private static Set<String> readSetSmart(String permissionName, Map m, String key) {
+    if(PermissionNameProvider.values.containsKey(permissionName) && !m.containsKey(key) && "collection".equals(key)) {
+      return PermissionNameProvider.Name.get(permissionName).collName;
+    }
+    Set<String> set = readValueAsSet(m, key);
+    if ("method".equals(key)) {
+      if (set != null) {
+        for (String s : set) if (!HTTP_METHODS.contains(s)) return null;
+      }
+      return set;
+    }
+    return set == null ? singleton(null) : set;
+  }
+  /**
+   * read a key value as a set. if the value is a single string ,
+   * return a singleton set
+   *
+   * @param m   the map from which to lookup
+   * @param key the key with which to do lookup
+   */
+  static Set<String> readValueAsSet(Map m, String key) {
+    Set<String> result = new HashSet<>();
+    Object val = m.get(key);
+    if (val == null) {
+      if("collection".equals(key)) {
+        //for collection collection: null means a core admin/ collection admin request
+        // otherwise it means a request where collection name is ignored
+        return m.containsKey(key) ? singleton(null) : singleton("*");
+      }
+      return null;
+    }
+    if (val instanceof Collection) {
+      Collection list = (Collection) val;
+      for (Object o : list) result.add(String.valueOf(o));
+    } else if (val instanceof String) {
+      result.add((String) val);
+    } else {
+      throw new RuntimeException("Bad value for : " + key);
+    }
+    return result.isEmpty() ? null : Collections.unmodifiableSet(result);
+  }
+
+
+  static final Set<String> knownKeys = ImmutableSet.of("collection", "role", "params", "path", "method", NAME,"index");
+  public static final Set<String> HTTP_METHODS = ImmutableSet.of("GET", "POST", "DELETE", "PUT", "HEAD");
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java b/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
new file mode 100644
index 0000000..2dec433
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
@@ -0,0 +1,72 @@
+package org.apache.solr.security;
+
+/*
+ * 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.
+ */
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singleton;
+import static java.util.Collections.unmodifiableMap;
+import static java.util.Collections.unmodifiableSet;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+
+/**
+ * A requestHandler should implement this interface to provide the well known permission
+ * at request time
+ */
+public interface PermissionNameProvider {
+  enum Name {
+    COLL_EDIT_PERM("collection-admin-edit", null),
+    COLL_READ_PERM("collection-admin-read", null),
+    READ_PERM("read", "*"),
+    UPDATE_PERM("update", "*"),
+    CONFIG_EDIT_PERM("config-edit", "*"),
+    CONFIG_READ_PERM("config-read", "*"),
+    SCHEMA_READ_PERM("schema-read", "*"),
+    SCHEMA_EDIT_PERM("schema-edit", "*"),
+    SECURITY_EDIT_PERM("security-edit", null),
+    SECURITY_READ_PERM("security-read", null),
+    ALL("all", unmodifiableSet(new HashSet<>(asList("*", null))))
+    ;
+    final String name;
+    final Set<String> collName;
+
+    Name(String s, Object collName) {
+      name = s;
+      this.collName = collName instanceof Set? (Set<String>)collName : singleton((String)collName);
+    }
+
+    public static Name get(String s) {
+      return values.get(s);
+    }
+
+    public String getPermissionName() {
+      return name;
+    }
+  }
+
+  Set<String> NULL = singleton(null);
+  Set<String> ANY = singleton("*");
+
+  Map<String, Name> values = unmodifiableMap(asList(Name.values()).stream().collect(toMap(Name::getPermissionName, identity())));
+
+  Name getPermissionName(AuthorizationContext request);
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java
----------------------------------------------------------------------
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 1d4a9b8..3145113 100644
--- a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java
@@ -20,31 +20,23 @@ import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.security.Principal;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Predicate;
 
-import com.google.common.collect.ImmutableSet;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.common.util.Utils;
 import org.apache.solr.util.CommandOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static java.util.Collections.singleton;
-import static org.apache.solr.common.util.Utils.fromJSONResource;
+import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableMap;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
 import static org.apache.solr.handler.admin.SecurityConfHandler.getListValue;
 import static org.apache.solr.handler.admin.SecurityConfHandler.getMapValue;
-import static org.apache.solr.common.params.CommonParams.NAME;
-import static org.apache.solr.common.util.Utils.getDeepCopy;
 
 
 public class RuleBasedAuthorizationPlugin implements AuthorizationPlugin, ConfigEditablePlugin {
@@ -120,21 +112,30 @@ public class RuleBasedAuthorizationPlugin implements AuthorizationPlugin, Config
     loopPermissions:
     for (int i = 0; i < permissions.size(); i++) {
       Permission permission = permissions.get(i);
-      if (permission.method != null && !permission.method.contains(context.getHttpMethod())) {
-        //this permissions HTTP method does not match this rule. try other rules
-        continue;
-      }
-      if(permission.predicate != null){
-        if(!permission.predicate.test(context)) continue ;
-      }
-
-      if (permission.params != null) {
-        for (Map.Entry<String, Object> 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;
+      if (PermissionNameProvider.values.containsKey(permission.name)) {
+        if (context.getHandler() instanceof PermissionNameProvider) {
+          PermissionNameProvider handler = (PermissionNameProvider) context.getHandler();
+          PermissionNameProvider.Name permissionName = handler.getPermissionName(context);
+          if (permissionName == null || !permission.name.equals(permissionName.name)) {
+            continue;
+          }
+        } else {
+          //all is special. it can match any
+          if(permission.wellknownName != PermissionNameProvider.Name.ALL) continue;
+        }
+      } else {
+        if (permission.method != null && !permission.method.contains(context.getHttpMethod())) {
+          //this permissions HTTP method does not match this rule. try other rules
+          continue;
+        }
+        if (permission.params != null) {
+          for (Map.Entry<String, Object> 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;
+          }
         }
       }
 
@@ -169,7 +170,7 @@ public class RuleBasedAuthorizationPlugin implements AuthorizationPlugin, Config
     for (Object o : map.entrySet()) {
       Map.Entry e = (Map.Entry) o;
       String roleName = (String) e.getKey();
-      usersVsRoles.put(roleName, readValueAsSet(map, roleName));
+      usersVsRoles.put(roleName, Permission.readValueAsSet(map, roleName));
     }
     List<Map> perms = getListValue(initInfo, "permissions");
     for (Map o : perms) {
@@ -198,74 +199,10 @@ public class RuleBasedAuthorizationPlugin implements AuthorizationPlugin, Config
     }
   }
 
-  /**
-   * read a key value as a set. if the value is a single string ,
-   * return a singleton set
-   *
-   * @param m   the map from which to lookup
-   * @param key the key with which to do lookup
-   */
-  static Set<String> readValueAsSet(Map m, String key) {
-    Set<String> result = new HashSet<>();
-    Object val = m.get(key);
-    if (val == null) {
-      if("collection".equals(key)){
-        //for collection collection: null means a core admin/ collection admin request
-        // otherwise it means a request where collection name is ignored
-        return m.containsKey(key) ? singleton(null) : singleton("*");
-      }
-      return null;
-    }
-    if (val instanceof Collection) {
-      Collection list = (Collection) val;
-      for (Object o : list) result.add(String.valueOf(o));
-    } else if (val instanceof String) {
-      result.add((String) val);
-    } else {
-      throw new RuntimeException("Bad value for : " + key);
-    }
-    return result.isEmpty() ? null : Collections.unmodifiableSet(result);
-  }
 
   @Override
   public void close() throws IOException { }
 
-  static class Permission {
-    String name;
-    Set<String> path, role, collections, method;
-    Map<String, Object> params;
-    Predicate<AuthorizationContext> predicate;
-
-    private Permission() {
-    }
-
-    static Permission load(Map m) {
-      Permission p = new Permission();
-      String name = (String) m.get(NAME);
-      if (!m.containsKey("role")) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "role not specified");
-      p.role = readValueAsSet(m, "role");
-      if (well_known_permissions.containsKey(name)) {
-        HashSet<String> disAllowed = new HashSet<>(knownKeys);
-        disAllowed.remove("role");//these are the only
-        disAllowed.remove(NAME);//allowed keys for well-known permissions
-        for (String s : disAllowed) {
-          if (m.containsKey(s))
-            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, s + " is not a valid key for the permission : " + name);
-        }
-        p.predicate = (Predicate<AuthorizationContext>) ((Map) well_known_permissions.get(name)).get(Predicate.class.getName());
-        m = well_known_permissions.get(name);
-      }
-      p.name = name;
-      p.path = readSetSmart(name, m, "path");
-      p.collections = readSetSmart(name, m, "collection");
-      p.method = readSetSmart(name, m, "method");
-      p.params = (Map<String, Object>) m.get("params");
-      return p;
-    }
-
-    static final Set<String> knownKeys = ImmutableSet.of("collection", "role", "params", "path", "method", NAME);
-  }
-
   enum MatchStatus {
     USER_REQUIRED(AuthorizationResponse.PROMPT),
     NO_PERMISSIONS_FOUND(AuthorizationResponse.OK),
@@ -279,33 +216,12 @@ public class RuleBasedAuthorizationPlugin implements AuthorizationPlugin, Config
     }
   }
 
-  /**
-   * This checks for the defaults available other rules for the keys
-   */
-  private static Set<String> readSetSmart(String permissionName, Map m, String key) {
-    Set<String> set = readValueAsSet(m, key);
-    if (set == null && well_known_permissions.containsKey(permissionName)) {
-      set = readValueAsSet((Map) well_known_permissions.get(permissionName), key);
-    }
-    if ("method".equals(key)) {
-      if (set != null) {
-        for (String s : set) if (!HTTP_METHODS.contains(s)) return null;
-      }
-      return set;
-    }
-    return set == null ? singleton(null) : set;
-  }
+
 
   @Override
   public Map<String, Object> edit(Map<String, Object> latestConf, List<CommandOperation> commands) {
     for (CommandOperation op : commands) {
-      OPERATION operation = null;
-      for (OPERATION o : OPERATION.values()) {
-        if (o.name.equals(op.name)) {
-          operation = o;
-          break;
-        }
-      }
+      AutorizationEditOperation operation = ops.get(op.name);
       if (operation == null) {
         op.unknownOperation();
         return null;
@@ -317,152 +233,6 @@ public class RuleBasedAuthorizationPlugin implements AuthorizationPlugin, Config
     return latestConf;
   }
 
-  enum OPERATION {
-    SET_USER_ROLE("set-user-role") {
-      @Override
-      public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
-        Map<String, Object> roleMap = getMapValue(latestConf, "user-role");
-        Map<String, Object> map = op.getDataMap();
-        if (op.hasError()) return null;
-        for (Map.Entry<String, Object> e : map.entrySet()) {
-          if (e.getValue() == null) {
-            roleMap.remove(e.getKey());
-            continue;
-          }
-          if (e.getValue() instanceof String || e.getValue() instanceof List) {
-            roleMap.put(e.getKey(), e.getValue());
-          } else {
-            op.addError("Unexpected value ");
-            return null;
-          }
-        }
-        return latestConf;
-      }
-    },
-    SET_PERMISSION("set-permission") {
-      @Override
-      public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
-        String name = op.getStr(NAME);
-        Map<String, Object> dataMap = op.getDataMap();
-        if (op.hasError()) return null;
-        dataMap = getDeepCopy(dataMap, 3);
-        String before = (String) dataMap.remove("before");
-        for (String key : dataMap.keySet()) {
-          if (!Permission.knownKeys.contains(key)) op.addError("Unknown key, " + key);
-        }
-        try {
-          Permission.load(dataMap);
-        } catch (Exception e) {
-          op.addError(e.getMessage());
-          return null;
-        }
-        List<Map> permissions = getListValue(latestConf, "permissions");
-        List<Map> permissionsCopy = new ArrayList<>();
-        boolean added = false;
-        for (Map e : permissions) {
-          Object n = e.get(NAME);
-          if (n.equals(before) || n.equals(name)) {
-            added = true;
-            permissionsCopy.add(dataMap);
-          }
-          if (!n.equals(name)) permissionsCopy.add(e);
-        }
-        if (!added && before != null) {
-          op.addError("Invalid 'before' :" + before);
-          return null;
-        }
-        if (!added) permissionsCopy.add(dataMap);
-        latestConf.put("permissions", permissionsCopy);
-        return latestConf;
-      }
-    },
-    UPDATE_PERMISSION("update-permission") {
-      @Override
-      public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
-        String name = op.getStr(NAME);
-        if (op.hasError()) return null;
-        for (Map permission : (List<Map>) getListValue(latestConf, "permissions")) {
-          if (name.equals(permission.get(NAME))) {
-            LinkedHashMap copy = new LinkedHashMap<>(permission);
-            copy.putAll(op.getDataMap());
-            op.setCommandData(copy);
-            return SET_PERMISSION.edit(latestConf, op);
-          }
-        }
-        op.addError("No such permission " + name);
-        return null;
-      }
-    },
-    DELETE_PERMISSION("delete-permission") {
-      @Override
-      public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
-        List<String> names = op.getStrs("");
-        if (names == null || names.isEmpty()) {
-          op.addError("Invalid command");
-          return null;
-        }
-        names = new ArrayList<>(names);
-        List<Map> copy = new ArrayList<>();
-        List<Map> p = getListValue(latestConf, "permissions");
-        for (Map map : p) {
-          Object n = map.get(NAME);
-          if (names.contains(n)) {
-            names.remove(n);
-            continue;
-          } else {
-            copy.add(map);
-          }
-        }
-        if (!names.isEmpty()) {
-          op.addError("Unknown permission name(s) " + names);
-          return null;
-        }
-        latestConf.put("permissions", copy);
-        return latestConf;
-      }
-    };
-
-    public abstract Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op);
-
-    public final String name;
-
-    OPERATION(String s) {
-      this.name = s;
-    }
-
-    public static OPERATION get(String name) {
-      for (OPERATION o : values()) if (o.name.equals(name)) return o;
-      return null;
-    }
-  }
-
-  public static final Set<String> HTTP_METHODS = ImmutableSet.of("GET", "POST", "DELETE", "PUT", "HEAD");
-
-  private static final Map<String, Map<String,Object>>  well_known_permissions =
-      (Map<String, Map<String, Object>>) fromJSONResource("WellKnownPermissions.json");
-
-  static {
-    ((Map) well_known_permissions.get("collection-admin-edit")).put(Predicate.class.getName(), getCollectionActionPredicate(true));
-    ((Map) well_known_permissions.get("collection-admin-read")).put(Predicate.class.getName(), getCollectionActionPredicate(false));
-  }
-
-  private static Predicate<AuthorizationContext> getCollectionActionPredicate(final boolean isEdit) {
-    return new Predicate<AuthorizationContext>() {
-      @Override
-      public boolean test(AuthorizationContext context) {
-        String action = context.getParams().get("action");
-        if (action == null) return false;
-        CollectionParams.CollectionAction collectionAction = CollectionParams.CollectionAction.get(action);
-        if (collectionAction == null) return false;
-        return isEdit ? collectionAction.isWrite : !collectionAction.isWrite;
-      }
-    };
-  }
-
-
-  public static void main(String[] args) {
-    System.out.println(Utils.toJSONString(well_known_permissions));
-
-  }
+  private static final Map<String, AutorizationEditOperation> ops = unmodifiableMap(asList(AutorizationEditOperation.values()).stream().collect(toMap(AutorizationEditOperation::getOperationName, identity())));
 
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
index f291b2f..63cfb7c 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -985,6 +985,11 @@ public class HttpSolrCall {
       }
 
       @Override
+      public Object getHandler() {
+        return handler;
+      }
+
+      @Override
       public String toString() {
         StringBuilder response = new StringBuilder("userPrincipal: [").append(getUserPrincipal()).append("]")
             .append(" type: [").append(requestType.toString()).append("], collections: [");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/java/org/apache/solr/util/CommandOperation.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/CommandOperation.java b/solr/core/src/java/org/apache/solr/util/CommandOperation.java
index b6a7868..b1fda00 100644
--- a/solr/core/src/java/org/apache/solr/util/CommandOperation.java
+++ b/solr/core/src/java/org/apache/solr/util/CommandOperation.java
@@ -93,6 +93,12 @@ public class CommandOperation {
   }
 
   private Object getMapVal(String key) {
+    if("".equals(key)){
+      if (commandData instanceof Map) {
+        addError("value of the command is an object should be primitive");
+      }
+      return commandData;
+    }
     if (commandData instanceof Map) {
       Map metaData = (Map) commandData;
       return metaData.get(key);
@@ -297,4 +303,25 @@ public class CommandOperation {
   }
 
 
+  public Integer getInt(String name, Integer def) {
+    Object o = getVal(name);
+    if (o == null) return def;
+    if (o instanceof Number) {
+      Number number = (Number) o;
+      return number.intValue();
+    } else {
+      try {
+        return Integer.parseInt(o.toString());
+      } catch (NumberFormatException e) {
+        addError(StrUtils.formatString("{0} is not a valid integer", name));
+        return null;
+      }
+    }
+  }
+
+  public Integer getInt(String name) {
+    Object o = getVal(name);
+    if(o == null) return null;
+    return getInt(name, null);
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/resources/WellKnownPermissions.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/WellKnownPermissions.json b/solr/core/src/resources/WellKnownPermissions.json
deleted file mode 100644
index 026e511..0000000
--- a/solr/core/src/resources/WellKnownPermissions.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
-  "security-edit":{
-    "path":[
-      "/admin/authentication",
-      "/admin/authorization"],
-    "collection":null,
-    "method":"POST"},
-  "security-read":{
-    "path":[
-      "/admin/authentication",
-      "/admin/authorization"],
-    "collection":null,
-    "method":"GET"},
-  "schema-edit":{
-    "method":"POST",
-    "path":"/schema/*"},
-  "collection-admin-edit":{
-    "collection":null,
-    "path":"/admin/collections"},
-  "collection-admin-read":{
-    "collection":null,
-    "path":"/admin/collections"},
-  "schema-read":{
-    "method":"GET",
-    "path":"/schema/*"},
-  "config-read":{
-    "method":"GET",
-    "path":"/config/*"},
-  "update":{"path":"/update/*"},
-  "read":{"path":[
-      "/select",
-      "/get",
-      "/browse",
-      "/tvrh",
-      "/terms",
-      "/clustering",
-      "/elevate",
-      "/export",
-      "/spell",
-      "/clustering"]},
-  "config-edit":{
-    "method":"POST",
-    "path":"/config/*"},
-  "all":{"collection":[
-      "*",
-      null]}
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java
index a857e77..5e3d407 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java
@@ -70,16 +70,12 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
 
 
     
-    command = "{'set-user-role': { 'tom': ['admin','dev']},\n" +
+    command = "{'set-permission':{ collection : acoll ,\n" +
+        "                      path : '/nonexistentpath',\n" +
+        "                      role :guest },\n" +
+        "'set-user-role': { 'tom': ['admin','dev']},"+
         "'set-permission':{'name': 'security-edit',\n" +
-        "                  'role': 'admin'\n" +
-        "                  },\n" +
-        "'set-permission':{'name':'some-permission',\n" +
-        "                      'collection':'acoll',\n" +
-        "                      'path':'/nonexistentpath',\n" +
-        "                      'role':'guest',\n" +
-        "                      'before':'security-edit'\n" +
-        "                      }\n" +
+        "                  'role': 'admin'}\n" +
         "}";
 
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
@@ -98,13 +94,11 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
     List<Map> permissions = (List<Map>) authzconf.get("permissions");
     assertEquals(2, permissions.size());
     for (Map p : permissions) {
-      assertEquals("some-permission", p.get("name"));
+      assertEquals("acoll", p.get("collection"));
       break;
     }
-
-
     command = "{\n" +
-        "'set-permission':{'name': 'security-edit',\n" +
+        "'set-permission':{index : 2,  name : security-edit,\n" +
         "                  'role': ['admin','dev']\n" +
         "                  }}";
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
@@ -124,7 +118,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
     assertEquals( "dev", rol.get(1));
 
     command = "{\n" +
-        "'update-permission':{'name': 'some-permission',\n" +
+        "'update-permission':{'index': 1,\n" +
         "                  'role': ['guest','admin']\n" +
         "                  }}";
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
@@ -138,7 +132,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
     permissions = (List<Map>) authzconf.get("permissions");
 
     p = permissions.get(0);
-    assertEquals("some-permission", p.get("name"));
+    assertEquals("acoll", p.get("collection"));
     rol = (List) p.get("role");
     assertEquals( "guest", rol.get(0));
     assertEquals( "admin", rol.get(1));
@@ -146,8 +140,8 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
 
 
     command = "{\n" +
-        "'delete-permission': 'some-permission',\n" +
-        "'set-user-role':{'tom':null}\n" +
+        "delete-permission: 1,\n" +
+        " set-user-role : { tom :null}\n" +
         "}";
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
     req.getContext().put("httpMethod","POST");
@@ -167,7 +161,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
       assertFalse("some-permission".equals(permission.get("name")));
     }
     command = "{\n" +
-        "'set-permission':{'name': 'security-edit',\n" +
+        "'set-permission':{index : 2,  'name': 'security-edit',\n" +
         "                  'method':'POST',"+ // -ve test security edit is a well-known permission , only role attribute should be provided
         "                  'role': 'admin'\n" +
         "                  }}";

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
index ab02a3e..96a8c14 100644
--- a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
@@ -140,8 +140,7 @@ public class BasicAuthIntegrationTest extends TestMiniSolrCloudClusterBase {
     httpPost = new HttpPost(baseUrl + authzPrefix);
     setBasicAuthHeader(httpPost, "harry", "HarryIsUberCool");
     httpPost.setEntity(new ByteArrayEntity(Utils.toJSON(singletonMap("set-permission", Utils.makeMap
-        ("name", "x-update",
-            "collection", "x",
+        ("collection", "x",
             "path", "/update/*",
             "role", "dev")))));
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java b/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java
index f307553..1cbe849 100644
--- a/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java
+++ b/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java
@@ -26,7 +26,7 @@ import org.apache.solr.common.SolrException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class MockAuthorizationPlugin implements AuthorizationPlugin{
+public class MockAuthorizationPlugin implements AuthorizationPlugin {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   static final HashSet<String> denyUsers = new HashSet<>();
@@ -34,8 +34,8 @@ public class MockAuthorizationPlugin implements AuthorizationPlugin{
 
   @Override
   public AuthorizationResponse authorize(AuthorizationContext context) {
-    String uname = context.getUserPrincipal()== null? null : context.getUserPrincipal().getName();
-    if(predicate != null){
+    String uname = context.getUserPrincipal() == null ? null : context.getUserPrincipal().getName();
+    if (predicate != null) {
       try {
         predicate.test(context);
         return new AuthorizationResponse(200);
@@ -45,9 +45,9 @@ public class MockAuthorizationPlugin implements AuthorizationPlugin{
     }
 
 
-    if(uname == null) uname = context.getParams().get("uname");
+    if (uname == null) uname = context.getParams().get("uname");
     log.info("User request: " + uname);
-    if(denyUsers.contains(uname))
+    if (denyUsers.contains(uname))
       return new AuthorizationResponse(403);
     else
       return new AuthorizationResponse(200);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faa0586b/solr/core/src/test/org/apache/solr/security/TestRuleBasedAuthorizationPlugin.java
----------------------------------------------------------------------
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 62f4db4..7a92f8f 100644
--- a/solr/core/src/test/org/apache/solr/security/TestRuleBasedAuthorizationPlugin.java
+++ b/solr/core/src/test/org/apache/solr/security/TestRuleBasedAuthorizationPlugin.java
@@ -16,9 +16,13 @@
  */
 package org.apache.solr.security;
 
+import java.io.IOException;
+import java.io.StringReader;
 import java.security.Principal;
+import java.util.Collection;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -27,12 +31,20 @@ 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.ReplicationHandler;
+import org.apache.solr.handler.SchemaHandler;
+import org.apache.solr.handler.UpdateRequestHandler;
+import org.apache.solr.handler.admin.CollectionsHandler;
+import org.apache.solr.handler.component.SearchHandler;
 import org.apache.solr.security.AuthorizationContext.CollectionRequest;
 import org.apache.solr.security.AuthorizationContext.RequestType;
+import org.apache.solr.util.CommandOperation;
 
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
+import static org.apache.solr.common.util.Utils.getObjectByPath;
 import static org.apache.solr.common.util.Utils.makeMap;
+import static org.apache.solr.util.CommandOperation.captureErrors;
 
 public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
   String permissions = "{" +
@@ -67,43 +79,51 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
     checkRules(makeMap("resource", "/update/json/docs",
         "httpMethod", "POST",
         "userPrincipal", "unknownuser",
-        "collectionRequests", "freeforall" )
+        "collectionRequests", "freeforall",
+        "handler", new UpdateRequestHandler())
         , STATUS_OK);
 
     checkRules(makeMap("resource", "/update/json/docs",
         "httpMethod", "POST",
         "userPrincipal", "tim",
-        "collectionRequests", "mycoll")
+        "collectionRequests", "mycoll",
+        "handler", new UpdateRequestHandler())
         , STATUS_OK);
 
 
     checkRules(makeMap("resource", "/update/json/docs",
         "httpMethod", "POST",
-        "collectionRequests", "mycoll" )
+        "collectionRequests", "mycoll",
+        "handler", new UpdateRequestHandler())
         , PROMPT_FOR_CREDENTIALS);
 
     checkRules(makeMap("resource", "/schema",
         "userPrincipal", "somebody",
         "collectionRequests", "mycoll",
-        "httpMethod", "POST")
+        "httpMethod", "POST",
+        "handler", new SchemaHandler())
         , FORBIDDEN);
 
     checkRules(makeMap("resource", "/schema",
         "userPrincipal", "somebody",
         "collectionRequests", "mycoll",
-        "httpMethod", "GET")
+        "httpMethod", "GET",
+        "handler", new SchemaHandler()
+    )
         , STATUS_OK);
 
     checkRules(makeMap("resource", "/schema/fields",
         "userPrincipal", "somebody",
         "collectionRequests", "mycoll",
-        "httpMethod", "GET")
+        "httpMethod", "GET",
+        "handler", new SchemaHandler())
         , STATUS_OK);
 
     checkRules(makeMap("resource", "/schema",
         "userPrincipal", "somebody",
         "collectionRequests", "mycoll",
-        "httpMethod", "POST" )
+        "httpMethod", "POST",
+        "handler", new SchemaHandler())
         , FORBIDDEN);
 
     checkRules(makeMap("resource", "/admin/collections",
@@ -111,6 +131,7 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
         "requestType", RequestType.ADMIN,
         "collectionRequests", null,
         "httpMethod", "GET",
+        "handler", new CollectionsHandler(),
         "params", new MapSolrParams(singletonMap("action", "LIST")))
         , STATUS_OK);
 
@@ -119,6 +140,7 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
         "requestType", RequestType.ADMIN,
         "collectionRequests", null,
         "httpMethod", "GET",
+        "handler", new CollectionsHandler(),
         "params", new MapSolrParams(singletonMap("action", "LIST")))
         , STATUS_OK);
 
@@ -126,6 +148,7 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
         "userPrincipal", null,
         "requestType", RequestType.ADMIN,
         "collectionRequests", null,
+        "handler", new CollectionsHandler(),
         "params", new MapSolrParams(singletonMap("action", "CREATE")))
         , PROMPT_FOR_CREDENTIALS);
 
@@ -133,6 +156,7 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
         "userPrincipal", null,
         "requestType", RequestType.ADMIN,
         "collectionRequests", null,
+        "handler", new CollectionsHandler(),
         "params", new MapSolrParams(singletonMap("action", "RELOAD")))
         , PROMPT_FOR_CREDENTIALS);
 
@@ -141,6 +165,7 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
         "userPrincipal", "somebody",
         "requestType", RequestType.ADMIN,
         "collectionRequests", null,
+        "handler", new CollectionsHandler(),
         "params", new MapSolrParams(singletonMap("action", "CREATE")))
         , FORBIDDEN);
 
@@ -148,11 +173,13 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
         "userPrincipal", "tim",
         "requestType", RequestType.ADMIN,
         "collectionRequests", null,
+        "handler", new CollectionsHandler(),
         "params", new MapSolrParams(singletonMap("action", "CREATE")))
         , STATUS_OK);
 
     checkRules(makeMap("resource", "/select",
         "httpMethod", "GET",
+        "handler", new SearchHandler(),
         "collectionRequests", singletonList(new CollectionRequest("mycoll")),
         "userPrincipal", "joe")
         , FORBIDDEN);
@@ -165,12 +192,14 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
     checkRules(makeMap("resource", "/replication",
         "httpMethod", "POST",
         "userPrincipal", "tim",
+        "handler", new ReplicationHandler(),
         "collectionRequests", singletonList(new CollectionRequest("mycoll")) )
         , FORBIDDEN, rules);
 
     checkRules(makeMap("resource", "/replication",
         "httpMethod", "POST",
         "userPrincipal", "cio",
+        "handler", new ReplicationHandler(),
         "collectionRequests", singletonList(new CollectionRequest("mycoll")) )
         , STATUS_OK, rules);
 
@@ -178,11 +207,61 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
         "userPrincipal", "tim",
         "requestType", AuthorizationContext.RequestType.ADMIN,
         "collectionRequests", null,
+        "handler", new CollectionsHandler(),
         "params", new MapSolrParams(singletonMap("action", "CREATE")))
         , STATUS_OK, rules);
 
   }
 
+  public void testEditRules() throws IOException {
+    Perms perms =  new Perms();
+    perms.runCmd("{set-permission : {name: config-edit, role: admin } }", true);
+    assertEquals("config-edit",  getObjectByPath(perms.conf, false, "permissions[0]/name"));
+    assertEquals(1 , perms.getVal("permissions[0]/index"));
+    assertEquals("admin" ,  perms.getVal("permissions[0]/role"));
+    perms.runCmd("{set-permission : {name: config-edit, role: [admin, dev], index:2 } }", false);
+    perms.runCmd("{set-permission : {name: config-edit, role: [admin, dev], index:1}}", true);
+    Collection roles = (Collection) perms.getVal("permissions[0]/role");
+    assertEquals(2, roles.size());
+    assertTrue(roles.contains("admin"));
+    assertTrue(roles.contains("dev"));
+    perms.runCmd("{set-permission : {role: [admin, dev], collection: x , path: '/a/b' , method :[GET, POST] }}", true);
+    assertNotNull(perms.getVal("permissions[1]"));
+    assertEquals("x", perms.getVal("permissions[1]/collection"));
+    assertEquals("/a/b", perms.getVal("permissions[1]/path"));
+    perms.runCmd("{update-permission : {index : 2, method : POST }}", true);
+    assertEquals("POST" , perms.getVal("permissions[1]/method"));
+    perms.runCmd("{set-permission : {name : read, collection : y, role:[guest, dev] ,  before :2}}", true);
+    assertNotNull(perms.getVal("permissions[2]"));
+    assertEquals("y", perms.getVal("permissions[1]/collection"));
+    assertEquals("read", perms.getVal("permissions[1]/name"));
+    perms.runCmd("{delete-permission : 3}", true);
+    assertTrue(captureErrors(perms.parsedCommands).isEmpty());
+    assertEquals("y",perms.getVal("permissions[1]/collection"));
+  }
+
+  static class  Perms {
+    Map conf =  new HashMap<>();
+    RuleBasedAuthorizationPlugin plugin = new RuleBasedAuthorizationPlugin();
+    List<CommandOperation> parsedCommands;
+
+    public void runCmd(String cmds, boolean failOnError) throws IOException {
+      parsedCommands = CommandOperation.parse(new StringReader(cmds));
+      LinkedList ll = new LinkedList();
+      Map<String, Object> edited = plugin.edit(conf, parsedCommands);
+      if(edited!= null) conf = edited;
+      List<Map> maps = captureErrors(parsedCommands);
+      if(failOnError){
+        assertTrue("unexpected error ,"+maps , maps.isEmpty());
+      } else {
+        assertFalse("expected error", maps.isEmpty());
+      }
+    }
+    public Object getVal(String path){
+      return getObjectByPath(conf,false, path);
+    }
+  }
+
   private void checkRules(Map<String, Object> values, int expected) {
     checkRules(values,expected,(Map) Utils.fromJSONString(permissions));
   }
@@ -254,6 +333,12 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
     }
 
     @Override
+    public Object getHandler() {
+      Object handler = values.get("handler");
+      return handler instanceof String ? (PermissionNameProvider) request -> PermissionNameProvider.Name.get((String) handler) : handler;
+    }
+
+    @Override
     public String getResource() {
       return (String) values.get("resource");
     }