Skip to content

Commit

Permalink
Ensure that plugin can search on system index when utilizing pluginSu…
Browse files Browse the repository at this point in the history
…bject.runAs

Signed-off-by: Craig Perkins <[email protected]>
  • Loading branch information
cwperks committed Jan 16, 2025
1 parent 0952ac5 commit 3a464c1
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Map;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
Expand Down Expand Up @@ -170,6 +171,37 @@ public void testPluginShouldBeAbleToBulkIndexDocumentIntoItsSystemIndex() {
}
}

@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 testPluginShouldNotBeAbleToBulkIndexDocumentIntoMixOfSystemIndexWhereAtLeastOneDoesNotBelongToPlugin() {
try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Route> 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))); }));
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,14 @@ public List<RestHandler> getRestHandlers(
new RestIndexDocumentIntoSystemIndexAction(client),
new RestRunClusterHealthAction(client),
new RestBulkIndexDocumentIntoSystemIndexAction(client, pluginClient),
new RestBulkIndexDocumentIntoMixOfSystemIndexAction(client, pluginClient)
new RestBulkIndexDocumentIntoMixOfSystemIndexAction(client, pluginClient),
new RestSearchOnSystemIndexAction(pluginClient)
);
}

@Override
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
return Arrays.asList(
new ActionHandler<>(IndexDocumentIntoSystemIndexAction.INSTANCE, TransportIndexDocumentIntoSystemIndexAction.class),
new ActionHandler<>(RunClusterHealthAction.INSTANCE, TransportRunClusterHealthAction.class)
);
return Arrays.asList(new ActionHandler<>(RunClusterHealthAction.INSTANCE, TransportRunClusterHealthAction.class));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public AuditLogImpl(
final Environment environment
) {
super(settings, threadPool, resolver, clusterService, environment);
clusterService.state().metadata().
this.settings = settings;
this.messageRouter = new AuditMessageRouter(settings, clientProvider, threadPool, configPath);
this.messageRouterEnabled = this.messageRouter.isEnabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ public void onConfigModelChanged(ConfigModel cm) {

@Override
public final DirectoryReader apply(DirectoryReader reader) throws IOException {

if (isSecurityIndexRequest() && !isAdminAuthenticatedOrInternalRequest()) {
return new EmptyFilterLeafReader.EmptyDirectoryReader(reader);
}
Expand Down Expand Up @@ -163,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;
}
Expand All @@ -180,8 +179,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public DlsFlsBaseContext(PrivilegesEvaluator privilegesEvaluator, ThreadContext
public PrivilegesEvaluationContext getPrivilegesEvaluationContext() {
User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER);

if (user == null || adminDNs.isAdmin(user)) {
if (HeaderHelper.isInternalOrPluginRequest(threadContext) || adminDNs.isAdmin(user)) {
return null;
}

Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/opensearch/security/support/HeaderHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -52,6 +53,18 @@ public static boolean isExtensionRequest(final ThreadContext context) {
}
// CS-ENFORCE-SINGLE

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()) {
Expand Down

0 comments on commit 3a464c1

Please sign in to comment.