mirror of https://github.com/apache/lucene.git
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
This commit is contained in:
parent
a22099a398
commit
faa0586b31
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
@ -61,6 +63,11 @@ public class ExtractingRequestHandler extends ContentStreamHandlerBase implement
|
|||
protected SolrContentHandlerFactory factory;
|
||||
|
||||
|
||||
@Override
|
||||
public PermissionNameProvider.Name getPermissionName(AuthorizationContext request) {
|
||||
return PermissionNameProvider.Name.READ_PERM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(NamedList args) {
|
||||
super.init(args);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -33,17 +33,31 @@ 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) {
|
||||
this.cores = coreContainer;
|
||||
}
|
||||
|
||||
@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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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())));
|
||||
|
||||
}
|
||||
|
|
|
@ -984,6 +984,11 @@ public class HttpSolrCall {
|
|||
return getReq().getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder response = new StringBuilder("userPrincipal: [").append(getUserPrincipal()).append("]")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]}
|
||||
}
|
|
@ -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" +
|
||||
" }}";
|
||||
|
|
|
@ -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")))));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
@ -253,6 +332,12 @@ public class TestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
|||
return (String) values.get("httpMethod");
|
||||
}
|
||||
|
||||
@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");
|
||||
|
|
Loading…
Reference in New Issue