diff --git a/src/integrationTest/java/org/opensearch/security/systemindex/AbstractSystemIndexTests.java b/src/integrationTest/java/org/opensearch/security/systemindex/AbstractSystemIndexTests.java index db88ec6f68..f4ab842f54 100644 --- a/src/integrationTest/java/org/opensearch/security/systemindex/AbstractSystemIndexTests.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/AbstractSystemIndexTests.java @@ -10,6 +10,7 @@ package org.opensearch.security.systemindex; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -81,6 +82,73 @@ public void testPluginShouldBeAbleToIndexDocumentIntoItsSystemIndex() { } } + @Test + public void testPluginShouldBeAbleSearchOnItsSystemIndex() { + JsonNode searchResponse1; + JsonNode searchResponse2; + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.put("try-create-and-bulk-index/" + SYSTEM_INDEX_1); + + assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + + HttpResponse searchResponse = client.get("search-on-system-index/" + SYSTEM_INDEX_1); + + assertThat(searchResponse.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + assertThat(searchResponse.getIntFromJsonBody("/hits/total/value"), equalTo(2)); + + searchResponse1 = searchResponse.bodyAsJsonNode(); + } + + try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) { + HttpResponse searchResponse = client.get(SYSTEM_INDEX_1 + "/_search"); + + assertThat(searchResponse.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + assertThat(searchResponse.getIntFromJsonBody("/hits/total/value"), equalTo(2)); + + searchResponse2 = searchResponse.bodyAsJsonNode(); + } + + JsonNode hits1 = searchResponse1.get("hits"); + JsonNode hits2 = searchResponse2.get("hits"); + assertThat(hits1.toPrettyString(), equalTo(hits2.toPrettyString())); + } + + @Test + public void testPluginShouldBeAbleGetOnItsSystemIndex() { + JsonNode getResponse1; + JsonNode getResponse2; + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.put("try-create-and-bulk-index/" + SYSTEM_INDEX_1); + + assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + + HttpResponse searchResponse = client.get("search-on-system-index/" + SYSTEM_INDEX_1); + + assertThat(searchResponse.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + assertThat(searchResponse.getIntFromJsonBody("/hits/total/value"), equalTo(2)); + + String docId = searchResponse.getTextFromJsonBody("/hits/hits/0/_id"); + + HttpResponse getResponse = client.get("get-on-system-index/" + SYSTEM_INDEX_1 + "/" + docId); + + getResponse1 = getResponse.bodyAsJsonNode(); + } + + try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) { + HttpResponse searchResponse = client.get(SYSTEM_INDEX_1 + "/_search"); + + assertThat(searchResponse.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + assertThat(searchResponse.getIntFromJsonBody("/hits/total/value"), equalTo(2)); + + String docId = searchResponse.getTextFromJsonBody("/hits/hits/0/_id"); + + HttpResponse getResponse = client.get(SYSTEM_INDEX_1 + "/_doc/" + docId); + + getResponse2 = getResponse.bodyAsJsonNode(); + } + assertThat(getResponse1.toPrettyString(), equalTo(getResponse2.toPrettyString())); + } + @Test public void testPluginShouldNotBeAbleToIndexDocumentIntoSystemIndexRegisteredByOtherPlugin() { try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { diff --git a/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexDisabledTests.java b/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexDisabledTests.java index 2668a2c5cc..611b880d10 100644 --- a/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexDisabledTests.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexDisabledTests.java @@ -1,3 +1,13 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + */ + package org.opensearch.security.systemindex; import java.util.List; diff --git a/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestGetOnSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestGetOnSystemIndexAction.java new file mode 100644 index 0000000000..86ed4ce4d7 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestGetOnSystemIndexAction.java @@ -0,0 +1,62 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + */ + +package org.opensearch.security.systemindex.sampleplugin; + +import java.util.List; + +import org.opensearch.action.get.GetRequest; +import org.opensearch.client.node.NodeClient; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestRequest; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.GET; + +public class RestGetOnSystemIndexAction extends BaseRestHandler { + + private final RunAsSubjectClient pluginClient; + + public RestGetOnSystemIndexAction(RunAsSubjectClient pluginClient) { + this.pluginClient = pluginClient; + } + + @Override + public List routes() { + return singletonList(new Route(GET, "/get-on-system-index/{index}/{docId}")); + } + + @Override + public String getName() { + return "test_get_on_system_index_action"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { + String indexName = request.param("index"); + String docId = request.param("docId"); + return new RestChannelConsumer() { + + @Override + public void accept(RestChannel channel) throws Exception { + GetRequest getRequest = new GetRequest(indexName); + getRequest.id(docId); + pluginClient.get(getRequest, ActionListener.wrap(r -> { + channel.sendResponse(new BytesRestResponse(RestStatus.OK, r.toXContent(channel.newBuilder(), ToXContent.EMPTY_PARAMS))); + }, fr -> { channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, String.valueOf(fr))); })); + } + }; + } +} diff --git a/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestSearchOnSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestSearchOnSystemIndexAction.java new file mode 100644 index 0000000000..4f9bcf6cba --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestSearchOnSystemIndexAction.java @@ -0,0 +1,65 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + */ + +package org.opensearch.security.systemindex.sampleplugin; + +import java.util.List; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.client.node.NodeClient; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestRequest; +import org.opensearch.search.builder.SearchSourceBuilder; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.GET; + +public class RestSearchOnSystemIndexAction extends BaseRestHandler { + + private final RunAsSubjectClient pluginClient; + + public RestSearchOnSystemIndexAction(RunAsSubjectClient pluginClient) { + this.pluginClient = pluginClient; + } + + @Override + public List routes() { + return singletonList(new Route(GET, "/search-on-system-index/{index}")); + } + + @Override + public String getName() { + return "test_search_on_system_index_action"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { + String indexName = request.param("index"); + return new RestChannelConsumer() { + + @Override + public void accept(RestChannel channel) throws Exception { + SearchRequest searchRequest = new SearchRequest(indexName); + SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); + sourceBuilder.query(QueryBuilders.matchAllQuery()); + searchRequest.source(sourceBuilder); + pluginClient.search(searchRequest, ActionListener.wrap(r -> { + channel.sendResponse(new BytesRestResponse(RestStatus.OK, r.toXContent(channel.newBuilder(), ToXContent.EMPTY_PARAMS))); + }, fr -> { channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, String.valueOf(fr))); })); + } + }; + } +} diff --git a/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin1.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin1.java index 9113a50a5f..edd90d0568 100644 --- a/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin1.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin1.java @@ -88,7 +88,9 @@ public List getRestHandlers( new RestIndexDocumentIntoSystemIndexAction(client), new RestRunClusterHealthAction(client), new RestBulkIndexDocumentIntoSystemIndexAction(client, pluginClient), - new RestBulkIndexDocumentIntoMixOfSystemIndexAction(client, pluginClient) + new RestBulkIndexDocumentIntoMixOfSystemIndexAction(client, pluginClient), + new RestSearchOnSystemIndexAction(pluginClient), + new RestGetOnSystemIndexAction(pluginClient) ); } diff --git a/src/main/java/org/opensearch/security/configuration/SystemIndexSearcherWrapper.java b/src/main/java/org/opensearch/security/configuration/SystemIndexSearcherWrapper.java index a5db15f2d0..fcbc563bd4 100644 --- a/src/main/java/org/opensearch/security/configuration/SystemIndexSearcherWrapper.java +++ b/src/main/java/org/opensearch/security/configuration/SystemIndexSearcherWrapper.java @@ -162,7 +162,7 @@ protected final boolean isBlockedSystemIndexRequest() { if (systemIndexPermissionEnabled) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - if (user == null) { + if (HeaderHelper.isInternalOrPluginRequest(threadContext)) { // allow request without user from plugin. return systemIndexMatcher.test(index.getName()) || matchesSystemIndexRegisteredWithCore; } @@ -178,8 +178,7 @@ protected final boolean isBlockedSystemIndexRequest() { protected final boolean isAdminDnOrPluginRequest() { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - if (user == null) { - // allow request without user from plugin. + if (HeaderHelper.isInternalOrPluginRequest(threadContext)) { return true; } else if (adminDns.isAdmin(user)) { return true; diff --git a/src/main/java/org/opensearch/security/support/HeaderHelper.java b/src/main/java/org/opensearch/security/support/HeaderHelper.java index cb2e2c4262..1711607429 100644 --- a/src/main/java/org/opensearch/security/support/HeaderHelper.java +++ b/src/main/java/org/opensearch/security/support/HeaderHelper.java @@ -33,6 +33,7 @@ import com.google.common.base.Strings; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.security.user.User; public class HeaderHelper { @@ -50,6 +51,18 @@ public static boolean isExtensionRequest(final ThreadContext context) { return context.getTransient(ConfigConstants.OPENDISTRO_SECURITY_SSL_TRANSPORT_EXTENSION_REQUEST) == Boolean.TRUE; } + public static boolean isInternalOrPluginRequest(final ThreadContext threadContext) { + // If user is empty, this indicates a system-level request which should be permitted + // If the requests originates from a plugin this will also return true + final User user = (User) threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + + if (user == null || user.isPluginUser()) { + return true; + } + + return false; + } + public static String getSafeFromHeader(final ThreadContext context, final String headerName) { if (context == null || headerName == null || headerName.isEmpty()) {