diff --git a/changelog/unreleased/SOLR-18095.yml b/changelog/unreleased/SOLR-18095.yml new file mode 100644 index 000000000000..8884ef9ab2de --- /dev/null +++ b/changelog/unreleased/SOLR-18095.yml @@ -0,0 +1,8 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc +title: Provide NoOpRequestWriter and NoOpRequestHandler that can be used to disable implicitly configured equivalents. +type: added # added, changed, fixed, deprecated, removed, dependency_update, security, other +authors: + - name: Eric Pugh +links: + - name: SOLR-18095 + url: https://issues.apache.org/jira/browse/SOLR-18095 diff --git a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/NoOpRequestHandler.java similarity index 86% rename from solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java rename to solr/core/src/java/org/apache/solr/handler/NoOpRequestHandler.java index 50e2fa1f27ca..6ce2e82d8778 100644 --- a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/NoOpRequestHandler.java @@ -23,12 +23,12 @@ import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.security.AuthorizationContext; -/** Does nothing other than showing a 404 message */ -public class NotFoundRequestHandler extends RequestHandlerBase { +/** Does nothing other than showing a 403 message */ +public class NoOpRequestHandler extends RequestHandlerBase { @Override public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { throw new SolrException( - SolrException.ErrorCode.NOT_FOUND, "" + req.getContext().get(PATH) + " is not found"); + SolrException.ErrorCode.FORBIDDEN, req.getContext().get(PATH) + " has been disabled"); } @Override diff --git a/solr/core/src/java/org/apache/solr/response/NoOpResponseWriter.java b/solr/core/src/java/org/apache/solr/response/NoOpResponseWriter.java new file mode 100644 index 000000000000..125f3338f20e --- /dev/null +++ b/solr/core/src/java/org/apache/solr/response/NoOpResponseWriter.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.response; + +import java.io.IOException; +import java.io.Writer; +import org.apache.solr.request.SolrQueryRequest; + +public class NoOpResponseWriter implements TextQueryResponseWriter { + static String MESSAGE = "noop response writer"; + + @Override + public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { + writer.write(MESSAGE); + } + + @Override + public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { + return QueryResponseWriter.CONTENT_TYPE_TEXT_UTF8; + } +} diff --git a/solr/core/src/test-files/solr/collection1/conf/solrconfig-noop.xml b/solr/core/src/test-files/solr/collection1/conf/solrconfig-noop.xml new file mode 100644 index 000000000000..2e9a5e463d02 --- /dev/null +++ b/solr/core/src/test-files/solr/collection1/conf/solrconfig-noop.xml @@ -0,0 +1,57 @@ + + + + + + + + + ${solr.data.dir:} + + + + ${tests.luceneMatchVersion:LATEST} + + + + ${solr.ulog.dir:} + + + + + + + + + + explicit + true + text + + + + + + + diff --git a/solr/core/src/test/org/apache/solr/handler/NoOpRequestHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/NoOpRequestHandlerTest.java new file mode 100644 index 000000000000..4193a7829bd3 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/NoOpRequestHandlerTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.handler; + +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrException; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Test that demonstrates NoOpRequestHandler can be used to disable implicit handlers like + * SchemaHandler that are loaded via ImplicitPlugins.json. + */ +public class NoOpRequestHandlerTest extends SolrTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig-noop.xml", "schema.xml"); + } + + @Test + public void testSchemaHandlerDisabled() { + // Test that /schema endpoint is disabled and returns 403 FORBIDDEN + SolrException exception = + expectThrows( + SolrException.class, + () -> { + try (SolrQueryRequest req = req("qt", "/schema")) { + SolrQueryResponse rsp = new SolrQueryResponse(); + h.getCore().execute(h.getCore().getRequestHandler("/schema"), req, rsp); + if (rsp.getException() != null) { + throw rsp.getException(); + } + } + }); + + assertEquals( + "Should return FORBIDDEN status code", + SolrException.ErrorCode.FORBIDDEN.code, + exception.code()); + assertTrue( + "Error message should indicate endpoint has been disabled", + exception.getMessage().contains("has been disabled")); + } + + @Test + public void testSchemaHandlerSubPathDisabled() { + // Test that /schema/fields endpoint is also disabled + SolrException exception = + expectThrows( + SolrException.class, + () -> { + try (SolrQueryRequest req = req("qt", "/schema/fields")) { + SolrQueryResponse rsp = new SolrQueryResponse(); + h.getCore().execute(h.getCore().getRequestHandler("/schema"), req, rsp); + if (rsp.getException() != null) { + throw rsp.getException(); + } + } + }); + + assertEquals( + "Should return FORBIDDEN status code", + SolrException.ErrorCode.FORBIDDEN.code, + exception.code()); + } + + @Test + public void testNoOpHandlerRegistered() { + // Verify that the NoOpRequestHandler is actually registered at /schema + assertNotNull("Schema handler should be registered", h.getCore().getRequestHandler("/schema")); + assertTrue( + "Handler at /schema should be NoOpRequestHandler", + h.getCore().getRequestHandler("/schema") instanceof NoOpRequestHandler); + } + + @Test + public void testOtherHandlersStillWork() { + assertQ("Standard query handler should still work", req("q", "*:*"), "//result[@numFound='0']"); + } +} diff --git a/solr/core/src/test/org/apache/solr/response/NoOpResponseWriterTest.java b/solr/core/src/test/org/apache/solr/response/NoOpResponseWriterTest.java new file mode 100644 index 000000000000..fc8888eda829 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/response/NoOpResponseWriterTest.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.response; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import org.apache.solr.SolrTestCaseJ4; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Test that demonstrates NoOpResponseWriter can be used to disable implicit response writers that + * are loaded via ImplicitPlugins.json. + */ +public class NoOpResponseWriterTest extends SolrTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig-noop.xml", "schema.xml"); + } + + @Test + public void testWrite() throws IOException { + NoOpResponseWriter writer = new NoOpResponseWriter(); + + Writer stringWriter = new StringWriter(); + + writer.write(stringWriter, null, null); + + assertEquals(NoOpResponseWriter.MESSAGE, stringWriter.toString()); + } + + @Test + public void testGetContentType() { + NoOpResponseWriter writer = new NoOpResponseWriter(); + + String contentType = writer.getContentType(null, null); + assertEquals(QueryResponseWriter.CONTENT_TYPE_TEXT_UTF8, contentType); + } + + @Test + public void testCsvResponseWriterDisabled() throws Exception { + QueryResponseWriter csvWriter = h.getCore().getQueryResponseWriter("csv"); + + assertNotNull("CSV response writer should be registered", csvWriter); + assertTrue( + "CSV response writer should be NoOpResponseWriter, not the implicit CSVResponseWriter", + csvWriter instanceof NoOpResponseWriter); + + // Verify it returns the NoOp message when used + ByteArrayOutputStream out = new ByteArrayOutputStream(); + csvWriter.write(out, null, null); + String output = out.toString(StandardCharsets.UTF_8); + assertEquals("CSV writer should return NoOp message", NoOpResponseWriter.MESSAGE, output); + } +} diff --git a/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc b/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc index 85f25610a0ac..0b82acf0902d 100644 --- a/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc +++ b/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc @@ -367,3 +367,15 @@ The response will look similar to: Because implicit request handlers are not present in `solrconfig.xml`, configuration of their associated `default`, `invariant` and `appends` parameters may be edited via the xref:request-parameters-api.adoc[] using the paramset listed in the above table. However, other parameters, including SearchHandler components, may not be modified. The invariants and appends specified in the implicit configuration cannot be overridden. + +== How to Disable an Implicit Handler + +You may want to disable the loading of an implicit handler. +This is supported by remapping the name of the handler to the `NoOpRequestHandler` in `solrconfig.xml`, which will return a 403 FORBIDDEN status code. + +For example, to disable the `/update/csv` handler you would re-define it in `solrconfig.xml` as: + +[source,xml] +---- + +---- diff --git a/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc b/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc index b4f29b8e6815..0aff169b739a 100644 --- a/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc +++ b/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc @@ -32,6 +32,7 @@ The list below describe shows the most common settings for the `wt` parameter, w * <> * <> * <> +* <> == JSON Response Writer @@ -386,3 +387,15 @@ else: == Smile Response Writer The Smile format is a JSON-compatible binary format, described in detail here: https://en.wikipedia.org/wiki/Smile_%28data_interchange_format%29[https://en.wikipedia.org/wiki/Smile_(data_interchange_format)] + +== NoOp Response Writer + +You may want to disable a specific response writer. +This is supported by remapping the name of the response writer to the `NoOpResponseWriter` in `solrconfig.xml`, which will return a 200 OK status code with the plain text message `noop response writer`. + +For example, to disable the `csv` handler you would re-define it in `solrconfig.xml` as: + +[source,xml] +---- + +----