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/23 20:18:47 UTC
lucene-solr:apiv2: SOLR-8029: enable schema enforcement for commands
Repository: lucene-solr
Updated Branches:
refs/heads/apiv2 38d542226 -> ba5dc7503
SOLR-8029: enable schema enforcement for commands
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/ba5dc750
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/ba5dc750
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/ba5dc750
Branch: refs/heads/apiv2
Commit: ba5dc7503e9e86ae636ef5f9d9e2852c4ddeb619
Parents: 38d5422
Author: Noble Paul <no...@apache.org>
Authored: Thu Mar 24 00:48:31 2016 +0530
Committer: Noble Paul <no...@apache.org>
Committed: Thu Mar 24 00:48:31 2016 +0530
----------------------------------------------------------------------
solr/core/src/java/org/apache/solr/api/Api.java | 18 +++++++-
.../src/java/org/apache/solr/api/ApiBag.java | 42 ++++++++++++++-----
.../java/org/apache/solr/api/V2HttpCall.java | 6 +++
.../apache/solr/handler/admin/InfoHandler.java | 11 ++---
.../solr/handler/admin/SecurityConfHandler.java | 43 +++++++++++++++-----
.../solr/request/SolrQueryRequestBase.java | 8 +++-
.../org/apache/solr/servlet/HttpSolrCall.java | 9 +++-
.../org/apache/solr/servlet/ResponseUtils.java | 6 +++
solr/core/src/resources/apispec/node.Info.json | 1 -
9 files changed, 112 insertions(+), 32 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/solr/core/src/java/org/apache/solr/api/Api.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/api/Api.java b/solr/core/src/java/org/apache/solr/api/Api.java
index 131b2f4..384a829 100644
--- a/solr/core/src/java/org/apache/solr/api/Api.java
+++ b/solr/core/src/java/org/apache/solr/api/Api.java
@@ -18,19 +18,35 @@ package org.apache.solr.api;
*/
-import java.util.concurrent.Callable;
+import java.util.Map;
+import com.google.common.collect.ImmutableMap;
import org.apache.solr.common.util.Map2;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.util.JsonSchemaValidator;
public abstract class Api implements SpecProvider {
protected SpecProvider spec;
+ protected volatile Map<String, JsonSchemaValidator> commandSchema;
protected Api(SpecProvider spec) {
this.spec = spec;
}
+ public Map<String, JsonSchemaValidator> getCommandSchema() {
+ if (commandSchema == null) {
+ synchronized (this) {
+ if(commandSchema == null) {
+ Map2 commands = getSpec().getMap("commands", null);
+ commandSchema = commands != null ?
+ ImmutableMap.copyOf(ApiBag.getPartsedSchema(commands)) :
+ ImmutableMap.of();
+ }
+ }
+ }
+ return commandSchema;
+ }
public abstract void call(SolrQueryRequest req , SolrQueryResponse rsp);
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/solr/core/src/java/org/apache/solr/api/ApiBag.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/api/ApiBag.java b/solr/core/src/java/org/apache/solr/api/ApiBag.java
index f5bb47f..64c247a 100644
--- a/solr/core/src/java/org/apache/solr/api/ApiBag.java
+++ b/solr/core/src/java/org/apache/solr/api/ApiBag.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -39,6 +40,7 @@ import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.PermissionNameProvider;
import org.apache.solr.util.CommandOperation;
+import org.apache.solr.util.JsonSchemaValidator;
import org.apache.solr.util.PathTrie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -149,11 +151,24 @@ public class ApiBag {
}
};
}
+ public static Map<String, JsonSchemaValidator> getPartsedSchema(Map2 commands) {
+ Map<String,JsonSchemaValidator> validators = new HashMap<>();
+ for (Object o : commands.entrySet()) {
+ Map.Entry cmd = (Map.Entry) o;
+ try {
+ validators.put((String) cmd.getKey(), new JsonSchemaValidator((Map) cmd.getValue()));
+ } catch (Exception e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error in spi spec" , e);
+ }
+ }
+ return validators;
+ }
+
private void verifyCommands(Map2 spec) {
Map2 commands = spec.getMap("commands", null);
if (commands == null) return;
- //TODO do verify
+ getPartsedSchema(commands);
}
@@ -217,7 +232,7 @@ public class ApiBag {
public static class ReqHandlerToApi extends Api implements PermissionNameProvider {
SolrRequestHandler rh;
- protected ReqHandlerToApi(SolrRequestHandler rh, SpecProvider spec) {
+ public ReqHandlerToApi(SolrRequestHandler rh, SpecProvider spec) {
super(spec);
this.rh = rh;
}
@@ -269,26 +284,29 @@ public class ApiBag {
}
}
- public static List<CommandOperation> getCommandOperations(Reader reader, Map2 spec, boolean validate) {
+ public static List<CommandOperation> getCommandOperations(Reader reader, Map<String,JsonSchemaValidator> validators, boolean validate) {
List<CommandOperation> parsedCommands = null;
try {
parsedCommands = CommandOperation.parse(reader);
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
}
- if (spec == null || !validate) { // no validation possible because we do not have a spec
+ if (validators == null || !validate) { // no validation possible because we do not have a spec
return parsedCommands;
}
- Map2 cmds = spec.getMap("commands", NOT_NULL);
List<CommandOperation> commandsCopy = CommandOperation.clone(parsedCommands);
for (CommandOperation cmd : commandsCopy) {
- if (!cmds.containsKey(cmd.name)) {
- cmd.addError(formatString("Unknown operation ''{0}'' in path ''{1}''", cmd.name,
- spec.getMap("url", NOT_NULL).get("paths")));
+ JsonSchemaValidator validator = validators.get(cmd.name);
+ if (validator == null) {
+ cmd.addError(formatString("Unknown operation ''{0}'' available ops are ''{1}''", cmd.name,
+ validators.keySet()));
+ continue;
+ } else {
+ List<String> errs = validator.validateJson(cmd.getCommandData());
+ if(errs != null) for (String err : errs) cmd.addError(err);
}
- //TODO validation
}
List<Map> errs = CommandOperation.captureErrors(commandsCopy);
@@ -298,13 +316,17 @@ public class ApiBag {
return commandsCopy;
}
- static class ExceptionWithErrObject extends SolrException {
+ public static class ExceptionWithErrObject extends SolrException {
private List<Map> errs;
public ExceptionWithErrObject(ErrorCode code, String msg, List<Map> errs) {
super(code, msg);
this.errs = errs;
}
+
+ public List<Map> getErrs(){
+ return errs;
+ }
}
public static class LazyLoadedApi extends Api {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
index 430e014..f513b69 100644
--- a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
+++ b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
@@ -46,6 +46,7 @@ import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.servlet.HttpSolrCall;
import org.apache.solr.servlet.SolrDispatchFilter;
import org.apache.solr.servlet.SolrRequestParsers;
+import org.apache.solr.util.JsonSchemaValidator;
import org.apache.solr.util.PathTrie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -286,4 +287,9 @@ public class V2HttpCall extends HttpSolrCall {
protected Map2 getSpec() {
return api == null ? null : api.getSpec();
}
+
+ @Override
+ protected Map<String, JsonSchemaValidator> getValidators() {
+ return api == null ? null : api.getCommandSchema();
+ }
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
index dcfb1e0..acbb395 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
@@ -22,6 +22,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.solr.api.ApiBag.ReqHandlerToApi;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;
@@ -35,6 +36,8 @@ import org.apache.solr.api.ApiSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static java.util.Collections.singletonList;
+import static org.apache.solr.api.ApiBag.getSpec;
import static org.apache.solr.common.params.CommonParams.PATH;
public class InfoHandler extends RequestHandlerBase implements ApiSupport {
@@ -144,13 +147,7 @@ public class InfoHandler extends RequestHandlerBase implements ApiSupport {
@Override
public Collection<Api> getApis() {
- return Collections.singletonList(new Api(ApiBag.getSpec("node.Info")) {
- @Override
- public void call(SolrQueryRequest req, SolrQueryResponse rsp) {
- handle(req, rsp, req.getPath());
-
- }
- });
+ return singletonList(new ReqHandlerToApi(this, getSpec("node.Info")));
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/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 b06a017..7de562c 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
@@ -44,6 +44,7 @@ import org.apache.solr.util.CommandOperation;
import org.apache.solr.api.Api;
import org.apache.solr.api.ApiBag;
import org.apache.solr.api.SpecProvider;
+import org.apache.solr.util.JsonSchemaValidator;
import org.apache.zookeeper.KeeperException;
public class SecurityConfHandler extends RequestHandlerBase implements PermissionNameProvider {
@@ -184,6 +185,9 @@ public class SecurityConfHandler extends RequestHandlerBase implements Permissio
private Collection<Api> apis;
+ private AuthenticationPlugin authcPlugin;
+ private AuthorizationPlugin authzPlugin;
+
@Override
public Collection<Api> getApis() {
if (apis == null) {
@@ -194,23 +198,40 @@ public class SecurityConfHandler extends RequestHandlerBase implements Permissio
final SpecProvider authzCommands = ApiBag.getSpec("cluster.security.authorization.Commands");
apis.add(ApiBag.wrapRequestHandler(this, ApiBag.getSpec("cluster.security.authentication")));
apis.add(ApiBag.wrapRequestHandler(this, ApiBag.getSpec("cluster.security.authorization")));
- apis.add(ApiBag.wrapRequestHandler(this, () -> {
+ SpecProvider authcSpecProvider = () -> {
AuthenticationPlugin authcPlugin = cores.getAuthenticationPlugin();
return authcPlugin != null && authcPlugin instanceof SpecProvider ?
((SpecProvider) authcPlugin).getSpec() :
- ApiBag.getSpec("cluster.security.authentication.Commands").getSpec();
- }));
-
-// AuthorizationPlugin authzPlugin = cores.getAuthorizationPlugin();
-// apis.add(ApiBag.wrapRequestHandler(this, authzPlugin != null && authzPlugin instanceof SpecProvider ? ((SpecProvider) authzPlugin) : authzCommands));
-
- apis.add(ApiBag.wrapRequestHandler(this, () -> {
+ authcCommands.getSpec();
+ };
+
+ apis.add(new ApiBag.ReqHandlerToApi(this, authcSpecProvider) {
+ @Override
+ public synchronized Map<String, JsonSchemaValidator> getCommandSchema() {
+ //it is possible that the Auhentication plugin is modified since the last call. invalidate the
+ // the cached commandSchema
+ if(SecurityConfHandler.this.authcPlugin != cores.getAuthenticationPlugin()) commandSchema = null;
+ SecurityConfHandler.this.authcPlugin = cores.getAuthenticationPlugin();
+ return super.getCommandSchema();
+ }
+ });
+
+ SpecProvider authzSpecProvider = () -> {
AuthorizationPlugin authzPlugin = cores.getAuthorizationPlugin();
return authzPlugin != null && authzPlugin instanceof SpecProvider ?
((SpecProvider) authzPlugin).getSpec() :
- ApiBag.getSpec("cluster.security.authorization.Commands").getSpec();
- }));
-
+ authzCommands.getSpec();
+ };
+ apis.add(new ApiBag.ReqHandlerToApi(this, authzSpecProvider) {
+ @Override
+ public synchronized Map<String, JsonSchemaValidator> getCommandSchema() {
+ //it is possible that the Authorization plugin is modified since the last call. invalidate the
+ // the cached commandSchema
+ if(SecurityConfHandler.this.authzPlugin != cores.getAuthorizationPlugin()) commandSchema = null;
+ SecurityConfHandler.this.authzPlugin = cores.getAuthorizationPlugin();
+ return super.getCommandSchema();
+ }
+ });
this.apis = ImmutableList.copyOf(apis);
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java b/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
index e94b51d..42d0814 100644
--- a/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
+++ b/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
@@ -24,6 +24,7 @@ import org.apache.solr.common.util.SuppressForbidden;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.servlet.HttpSolrCall;
import org.apache.solr.util.CommandOperation;
+import org.apache.solr.util.JsonSchemaValidator;
import org.apache.solr.util.RTimerTree;
import org.apache.solr.util.RefCounted;
import org.apache.solr.schema.IndexSchema;
@@ -35,6 +36,7 @@ import java.io.Closeable;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Principal;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
@@ -203,7 +205,7 @@ public abstract class SolrQueryRequestBase implements SolrQueryRequest, Closeabl
if (contentStreams == null) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No content stream");
for (ContentStream contentStream : contentStreams) {
parsedCommands = ApiBag.getCommandOperations(new InputStreamReader((InputStream) contentStream, UTF_8),
- getSpec(), validateInput);
+ getValidators(), validateInput);
}
}
@@ -214,4 +216,8 @@ public abstract class SolrQueryRequestBase implements SolrQueryRequest, Closeabl
protected Map2 getSpec() {
return null;
}
+
+ protected Map<String, JsonSchemaValidator> getValidators(){
+ return Collections.EMPTY_MAP;
+ }
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/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 3bd4cea..44cf5a9 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -105,6 +105,7 @@ import org.apache.solr.servlet.cache.HttpCacheHeaderUtil;
import org.apache.solr.servlet.cache.Method;
import org.apache.solr.update.processor.DistributingUpdateProcessorFactory;
import org.apache.solr.util.CommandOperation;
+import org.apache.solr.util.JsonSchemaValidator;
import org.apache.solr.util.RTimerTree;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
@@ -1075,7 +1076,7 @@ public class HttpSolrCall {
if (contentStreams == null) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No content stream");
for (ContentStream contentStream : contentStreams) {
try {
- parsedCommands = ApiBag.getCommandOperations(contentStream.getReader(), getSpec(), validateInput);
+ parsedCommands = ApiBag.getCommandOperations(contentStream.getReader(), getValidators(), validateInput);
} catch (IOException e) {
throw new SolrException(ErrorCode.BAD_REQUEST, "Error reading commands");
}
@@ -1087,4 +1088,10 @@ public class HttpSolrCall {
protected Map2 getSpec() {
return null;
}
+
+ protected Map<String, JsonSchemaValidator> getValidators(){
+ return Collections.EMPTY_MAP;
+ }
+
+
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/solr/core/src/java/org/apache/solr/servlet/ResponseUtils.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/servlet/ResponseUtils.java b/solr/core/src/java/org/apache/solr/servlet/ResponseUtils.java
index a74fa8a..00733f5 100644
--- a/solr/core/src/java/org/apache/solr/servlet/ResponseUtils.java
+++ b/solr/core/src/java/org/apache/solr/servlet/ResponseUtils.java
@@ -15,8 +15,10 @@
* limitations under the License.
*/
package org.apache.solr.servlet;
+import org.apache.solr.api.ApiBag;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.util.CommandOperation;
import org.slf4j.Logger;
import java.io.PrintWriter;
@@ -48,6 +50,10 @@ public class ResponseUtils {
errorMetadata.add(SolrException.ERROR_CLASS, ex.getClass().getName());
errorMetadata.add(SolrException.ROOT_ERROR_CLASS, SolrException.getRootCause(ex).getClass().getName());
info.add("metadata", errorMetadata);
+ if (ex instanceof ApiBag.ExceptionWithErrObject) {
+ ApiBag.ExceptionWithErrObject exception = (ApiBag.ExceptionWithErrObject) ex;
+ info.add(CommandOperation.ERR_MSGS, exception.getErrs() );
+ }
}
for (Throwable th = ex; th != null; th = th.getCause()) {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ba5dc750/solr/core/src/resources/apispec/node.Info.json
----------------------------------------------------------------------
diff --git a/solr/core/src/resources/apispec/node.Info.json b/solr/core/src/resources/apispec/node.Info.json
index 8ed7740..c6ba07d 100644
--- a/solr/core/src/resources/apispec/node.Info.json
+++ b/solr/core/src/resources/apispec/node.Info.json
@@ -2,7 +2,6 @@
"documentation": "https://cwiki.apache.org/confluence/display/solr/Schema+API",
"methods": ["GET"],
"url": {
- "path": "/node",
"paths": [
"/node/properties",
"/node/threads",