From 779d9d3d3a93b45f72cf745856f73e2ef5825c58 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 11 Nov 2024 14:46:36 -0500 Subject: [PATCH 01/14] Rebasing security subject Signed-off-by: Craig Perkins --- .../IndexDocumentIntoSystemIndexAction.java | 22 ++++ .../IndexDocumentIntoSystemIndexRequest.java | 48 ++++++++ .../IndexDocumentIntoSystemIndexResponse.java | 48 ++++++++ ...dexDocumentIntoMixOfSystemIndexAction.java | 78 +++++++++++++ ...ulkIndexDocumentIntoSystemIndexAction.java | 80 +++++++++++++ ...estIndexDocumentIntoSystemIndexAction.java | 49 ++++++++ .../plugin/RestRunClusterHealthAction.java | 51 ++++++++ .../plugin/RunClusterHealthAction.java | 22 ++++ .../plugin/RunClusterHealthRequest.java | 40 +++++++ .../plugin/RunClusterHealthResponse.java | 42 +++++++ .../security/plugin/SystemIndexPlugin1.java | 110 ++++++++++++++++++ .../security/plugin/SystemIndexPlugin2.java | 61 ++++++++++ ...ortIndexDocumentIntoSystemIndexAction.java | 99 ++++++++++++++++ .../TransportRunClusterHealthAction.java | 84 +++++++++++++ .../security/auth/SecurityUser.java | 51 ++++++++ .../ContextProvidingPluginSubject.java | 51 ++++++++ .../identity/PluginContextSwitcher.java | 35 ++++++ .../security/auth/SecurityUserTests.java | 54 +++++++++ .../ContextProvidingPluginSubjectTests.java | 91 +++++++++++++++ 19 files changed, 1116 insertions(+) create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexAction.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexRequest.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexResponse.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoSystemIndexAction.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/RestIndexDocumentIntoSystemIndexAction.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/RestRunClusterHealthAction.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthAction.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthRequest.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthResponse.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin1.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin2.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java create mode 100644 src/integrationTest/java/org/opensearch/security/plugin/TransportRunClusterHealthAction.java create mode 100644 src/main/java/org/opensearch/security/auth/SecurityUser.java create mode 100644 src/main/java/org/opensearch/security/identity/ContextProvidingPluginSubject.java create mode 100644 src/main/java/org/opensearch/security/identity/PluginContextSwitcher.java create mode 100644 src/test/java/org/opensearch/security/auth/SecurityUserTests.java create mode 100644 src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java diff --git a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexAction.java new file mode 100644 index 0000000000..9a60de201c --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexAction.java @@ -0,0 +1,22 @@ +/* + * 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.plugin; + +import org.opensearch.action.ActionType; + +public class IndexDocumentIntoSystemIndexAction extends ActionType { + public static final IndexDocumentIntoSystemIndexAction INSTANCE = new IndexDocumentIntoSystemIndexAction(); + public static final String NAME = "cluster:mock/systemindex/index"; + + private IndexDocumentIntoSystemIndexAction() { + super(NAME, IndexDocumentIntoSystemIndexResponse::new); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexRequest.java b/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexRequest.java new file mode 100644 index 0000000000..d1b644ad11 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexRequest.java @@ -0,0 +1,48 @@ +/* + * 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.plugin; + +import java.io.IOException; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; + +public class IndexDocumentIntoSystemIndexRequest extends ActionRequest { + + private final String indexName; + + private final String runAs; + + public IndexDocumentIntoSystemIndexRequest(String indexName, String runAs) { + this.indexName = indexName; + this.runAs = runAs; + } + + public IndexDocumentIntoSystemIndexRequest(StreamInput in) throws IOException { + super(in); + this.indexName = in.readString(); + this.runAs = in.readOptionalString(); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + public String getIndexName() { + return this.indexName; + } + + public String getRunAs() { + return this.runAs; + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexResponse.java b/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexResponse.java new file mode 100644 index 0000000000..9779cca252 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexResponse.java @@ -0,0 +1,48 @@ +/* + * 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.plugin; + +// CS-SUPPRESS-SINGLE: RegexpSingleline It is not possible to use phrase "cluster manager" instead of master here +import java.io.IOException; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +// CS-ENFORCE-SINGLE + +public class IndexDocumentIntoSystemIndexResponse extends AcknowledgedResponse implements ToXContentObject { + + private String plugin; + + public IndexDocumentIntoSystemIndexResponse(boolean status, String plugin) { + super(status); + this.plugin = plugin; + } + + public IndexDocumentIntoSystemIndexResponse(StreamInput in) throws IOException { + super(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(plugin); + } + + @Override + public void addCustomFields(XContentBuilder builder, ToXContent.Params params) throws IOException { + super.addCustomFields(builder, params); + builder.field("plugin", plugin); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java new file mode 100644 index 0000000000..0d1dc4fe01 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java @@ -0,0 +1,78 @@ +/* + * 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.plugin; + +import java.util.List; + +import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.bulk.BulkRequestBuilder; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.client.Client; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.xcontent.XContentType; +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 org.opensearch.security.identity.PluginContextSwitcher; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.PUT; +import static org.opensearch.security.plugin.SystemIndexPlugin1.SYSTEM_INDEX_1; +import static org.opensearch.security.plugin.SystemIndexPlugin2.SYSTEM_INDEX_2; + +public class RestBulkIndexDocumentIntoMixOfSystemIndexAction extends BaseRestHandler { + + private final Client client; + private final PluginContextSwitcher contextSwitcher; + + public RestBulkIndexDocumentIntoMixOfSystemIndexAction(Client client, PluginContextSwitcher contextSwitcher) { + this.client = client; + this.contextSwitcher = contextSwitcher; + } + + @Override + public List routes() { + return singletonList(new Route(PUT, "/try-create-and-bulk-mixed-index")); + } + + @Override + public String getName() { + return "test_bulk_index_document_into_mix_of_system_index_action"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { + return new RestChannelConsumer() { + + @Override + public void accept(RestChannel channel) throws Exception { + contextSwitcher.runAs(() -> { + BulkRequestBuilder builder = client.prepareBulk(); + builder.add(new IndexRequest(SYSTEM_INDEX_1).source("{\"content\":1}", XContentType.JSON)); + builder.add(new IndexRequest(SYSTEM_INDEX_2).source("{\"content\":1}", XContentType.JSON)); + builder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + BulkRequest bulkRequest = builder.request(); + client.bulk(bulkRequest, 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))); })); + return null; + }); + } + }; + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoSystemIndexAction.java new file mode 100644 index 0000000000..56fa3ef2a9 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoSystemIndexAction.java @@ -0,0 +1,80 @@ +/* + * 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.plugin; + +import java.util.List; + +import org.opensearch.action.admin.indices.create.CreateIndexRequest; +import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.bulk.BulkRequestBuilder; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.client.Client; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.xcontent.XContentType; +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 org.opensearch.security.identity.PluginContextSwitcher; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.PUT; + +public class RestBulkIndexDocumentIntoSystemIndexAction extends BaseRestHandler { + + private final Client client; + private final PluginContextSwitcher contextSwitcher; + + public RestBulkIndexDocumentIntoSystemIndexAction(Client client, PluginContextSwitcher contextSwitcher) { + this.client = client; + this.contextSwitcher = contextSwitcher; + } + + @Override + public List routes() { + return singletonList(new Route(PUT, "/try-create-and-bulk-index/{index}")); + } + + @Override + public String getName() { + return "test_bulk_index_document_into_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 { + contextSwitcher.runAs(() -> { + client.admin().indices().create(new CreateIndexRequest(indexName), ActionListener.wrap(r -> { + BulkRequestBuilder builder = client.prepareBulk(); + builder.add(new IndexRequest(indexName).source("{\"content\":1}", XContentType.JSON)); + builder.add(new IndexRequest(indexName).source("{\"content\":2}", XContentType.JSON)); + builder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + BulkRequest bulkRequest = builder.request(); + client.bulk(bulkRequest, ActionListener.wrap(r2 -> { + channel.sendResponse( + new BytesRestResponse(RestStatus.OK, r.toXContent(channel.newBuilder(), ToXContent.EMPTY_PARAMS)) + ); + }, fr -> { channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, String.valueOf(fr))); })); + }, fr -> { channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, String.valueOf(fr))); })); + return null; + }); + } + }; + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestIndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/plugin/RestIndexDocumentIntoSystemIndexAction.java new file mode 100644 index 0000000000..e668e8bccc --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/RestIndexDocumentIntoSystemIndexAction.java @@ -0,0 +1,49 @@ +/* + * 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.plugin; + +import java.util.List; + +import org.opensearch.client.Client; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestToXContentListener; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.PUT; + +public class RestIndexDocumentIntoSystemIndexAction extends BaseRestHandler { + + private final Client client; + + public RestIndexDocumentIntoSystemIndexAction(Client client) { + this.client = client; + } + + @Override + public List routes() { + return singletonList(new Route(PUT, "/try-create-and-index/{index}")); + } + + @Override + public String getName() { + return "test_index_document_into_system_index_action"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { + String runAs = request.param("runAs"); + String indexName = request.param("index"); + IndexDocumentIntoSystemIndexRequest indexRequest = new IndexDocumentIntoSystemIndexRequest(indexName, runAs); + return channel -> client.execute(IndexDocumentIntoSystemIndexAction.INSTANCE, indexRequest, new RestToXContentListener<>(channel)); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestRunClusterHealthAction.java b/src/integrationTest/java/org/opensearch/security/plugin/RestRunClusterHealthAction.java new file mode 100644 index 0000000000..755f3278f0 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/RestRunClusterHealthAction.java @@ -0,0 +1,51 @@ +/* + * 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.plugin; + +import java.util.List; + +import org.opensearch.client.Client; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestToXContentListener; +import org.opensearch.security.identity.PluginContextSwitcher; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.GET; + +public class RestRunClusterHealthAction extends BaseRestHandler { + + private final Client client; + private final PluginContextSwitcher contextSwitcher; + + public RestRunClusterHealthAction(Client client, PluginContextSwitcher contextSwitcher) { + this.client = client; + this.contextSwitcher = contextSwitcher; + } + + @Override + public List routes() { + return singletonList(new Route(GET, "/try-cluster-health/{runAs}")); + } + + @Override + public String getName() { + return "test_run_cluster_health_action"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { + String runAs = request.param("runAs"); + RunClusterHealthRequest runRequest = new RunClusterHealthRequest(runAs); + return channel -> client.execute(RunClusterHealthAction.INSTANCE, runRequest, new RestToXContentListener<>(channel)); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthAction.java b/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthAction.java new file mode 100644 index 0000000000..4234879bb8 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthAction.java @@ -0,0 +1,22 @@ +/* + * 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.plugin; + +import org.opensearch.action.ActionType; + +public class RunClusterHealthAction extends ActionType { + public static final RunClusterHealthAction INSTANCE = new RunClusterHealthAction(); + public static final String NAME = "cluster:mock/monitor/health"; + + private RunClusterHealthAction() { + super(NAME, RunClusterHealthResponse::new); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthRequest.java b/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthRequest.java new file mode 100644 index 0000000000..8ae08bd6ff --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthRequest.java @@ -0,0 +1,40 @@ +/* + * 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.plugin; + +import java.io.IOException; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; + +public class RunClusterHealthRequest extends ActionRequest { + + private final String runAs; + + public RunClusterHealthRequest(String runAs) { + this.runAs = runAs; + } + + public RunClusterHealthRequest(StreamInput in) throws IOException { + super(in); + this.runAs = in.readString(); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + public String getRunAs() { + return this.runAs; + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthResponse.java b/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthResponse.java new file mode 100644 index 0000000000..7a855744dc --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthResponse.java @@ -0,0 +1,42 @@ +/* + * 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.plugin; + +// CS-SUPPRESS-SINGLE: RegexpSingleline It is not possible to use phrase "cluster manager" instead of master here +import java.io.IOException; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +// CS-ENFORCE-SINGLE + +public class RunClusterHealthResponse extends AcknowledgedResponse implements ToXContentObject { + + public RunClusterHealthResponse(boolean status) { + super(status); + } + + public RunClusterHealthResponse(StreamInput in) throws IOException { + super(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } + + @Override + public void addCustomFields(XContentBuilder builder, Params params) throws IOException { + super.addCustomFields(builder, params); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin1.java b/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin1.java new file mode 100644 index 0000000000..3f1112c4b6 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin1.java @@ -0,0 +1,110 @@ +/* + * 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.plugin; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +import org.opensearch.action.ActionRequest; +import org.opensearch.client.Client; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.IndexScopedSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.env.Environment; +import org.opensearch.env.NodeEnvironment; +import org.opensearch.identity.PluginSubject; +import org.opensearch.indices.SystemIndexDescriptor; +import org.opensearch.plugins.IdentityAwarePlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.SystemIndexPlugin; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestHandler; +import org.opensearch.script.ScriptService; +import org.opensearch.security.identity.PluginContextSwitcher; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.watcher.ResourceWatcherService; + +public class SystemIndexPlugin1 extends Plugin implements SystemIndexPlugin, IdentityAwarePlugin { + public static final String SYSTEM_INDEX_1 = ".system-index1"; + + private PluginContextSwitcher contextSwitcher; + + private Client client; + + @Override + public Collection createComponents( + Client client, + ClusterService clusterService, + ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, + ScriptService scriptService, + NamedXContentRegistry xContentRegistry, + Environment environment, + NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier + ) { + this.client = client; + this.contextSwitcher = new PluginContextSwitcher(); + return List.of(contextSwitcher); + } + + @Override + public Collection getSystemIndexDescriptors(Settings settings) { + final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(SYSTEM_INDEX_1, "System index 1"); + return Collections.singletonList(systemIndexDescriptor); + } + + @Override + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { + return List.of( + new RestIndexDocumentIntoSystemIndexAction(client), + new RestRunClusterHealthAction(client, contextSwitcher), + new RestBulkIndexDocumentIntoSystemIndexAction(client, contextSwitcher), + new RestBulkIndexDocumentIntoMixOfSystemIndexAction(client, contextSwitcher) + ); + } + + @Override + public List> getActions() { + return Arrays.asList( + new ActionHandler<>(IndexDocumentIntoSystemIndexAction.INSTANCE, TransportIndexDocumentIntoSystemIndexAction.class), + new ActionHandler<>(RunClusterHealthAction.INSTANCE, TransportRunClusterHealthAction.class) + ); + } + + @Override + public void assignSubject(PluginSubject pluginSystemSubject) { + if (contextSwitcher != null) { + this.contextSwitcher.initialize(pluginSystemSubject); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin2.java b/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin2.java new file mode 100644 index 0000000000..8fcf23e3db --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin2.java @@ -0,0 +1,61 @@ +/* + * 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.plugin; + +import java.util.Collection; +import java.util.Collections; +import java.util.function.Supplier; + +import org.opensearch.client.Client; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.env.Environment; +import org.opensearch.env.NodeEnvironment; +import org.opensearch.indices.SystemIndexDescriptor; +import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.SystemIndexPlugin; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.script.ScriptService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.watcher.ResourceWatcherService; + +public class SystemIndexPlugin2 extends Plugin implements SystemIndexPlugin { + public static final String SYSTEM_INDEX_2 = ".system-index2"; + + private Client client; + + @Override + public Collection createComponents( + Client client, + ClusterService clusterService, + ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, + ScriptService scriptService, + NamedXContentRegistry xContentRegistry, + Environment environment, + NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier + ) { + this.client = client; + return Collections.emptyList(); + } + + @Override + public Collection getSystemIndexDescriptors(Settings settings) { + final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(SYSTEM_INDEX_2, "System index 2"); + return Collections.singletonList(systemIndexDescriptor); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java new file mode 100644 index 0000000000..19ec06ca7d --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java @@ -0,0 +1,99 @@ +/* + * 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.plugin; + +import org.opensearch.action.admin.indices.create.CreateIndexRequest; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.client.Client; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.identity.IdentityService; +import org.opensearch.identity.Subject; +import org.opensearch.security.identity.PluginContextSwitcher; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.tasks.Task; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +public class TransportIndexDocumentIntoSystemIndexAction extends HandledTransportAction< + IndexDocumentIntoSystemIndexRequest, + IndexDocumentIntoSystemIndexResponse> { + + private final Client client; + private final ThreadPool threadPool; + private final PluginContextSwitcher contextSwitcher; + private final IdentityService identityService; + + @Inject + public TransportIndexDocumentIntoSystemIndexAction( + final TransportService transportService, + final ActionFilters actionFilters, + final Client client, + final ThreadPool threadPool, + final PluginContextSwitcher contextSwitcher, + final IdentityService identityService + ) { + super(IndexDocumentIntoSystemIndexAction.NAME, transportService, actionFilters, IndexDocumentIntoSystemIndexRequest::new); + this.client = client; + this.threadPool = threadPool; + this.contextSwitcher = contextSwitcher; + this.identityService = identityService; + } + + @Override + protected void doExecute( + Task task, + IndexDocumentIntoSystemIndexRequest request, + ActionListener actionListener + ) { + String indexName = request.getIndexName(); + String runAs = request.getRunAs(); + Subject userSubject = identityService.getCurrentSubject(); + System.out.println("User Subject: " + userSubject); + try { + contextSwitcher.runAs(() -> { + client.admin().indices().create(new CreateIndexRequest(indexName), ActionListener.wrap(r -> { + if ("user".equalsIgnoreCase(runAs)) { + userSubject.runAs(() -> { + client.index( + new IndexRequest(indexName).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .source("{\"content\":1}", XContentType.JSON), + ActionListener.wrap(r2 -> { + User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + actionListener.onResponse(new IndexDocumentIntoSystemIndexResponse(true, user.getName())); + }, actionListener::onFailure) + ); + return null; + }); + } else { + client.index( + new IndexRequest(indexName).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .source("{\"content\":1}", XContentType.JSON), + ActionListener.wrap(r2 -> { + User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + System.out.println("Test User: " + user); + actionListener.onResponse(new IndexDocumentIntoSystemIndexResponse(true, user.getName())); + }, actionListener::onFailure) + ); + } + }, actionListener::onFailure)); + return null; + }); + } catch (Exception ex) { + throw new RuntimeException("Unexpected error: " + ex.getMessage()); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/TransportRunClusterHealthAction.java b/src/integrationTest/java/org/opensearch/security/plugin/TransportRunClusterHealthAction.java new file mode 100644 index 0000000000..4be06933a2 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/plugin/TransportRunClusterHealthAction.java @@ -0,0 +1,84 @@ +/* + * 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.plugin; + +import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.client.Client; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.identity.IdentityService; +import org.opensearch.identity.Subject; +import org.opensearch.security.identity.PluginContextSwitcher; +import org.opensearch.tasks.Task; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +public class TransportRunClusterHealthAction extends HandledTransportAction { + + private final Client client; + private final ThreadPool threadPool; + private final PluginContextSwitcher contextSwitcher; + private final IdentityService identityService; + + @Inject + public TransportRunClusterHealthAction( + final TransportService transportService, + final ActionFilters actionFilters, + final Client client, + final ThreadPool threadPool, + final PluginContextSwitcher contextSwitcher, + final IdentityService identityService + ) { + super(RunClusterHealthAction.NAME, transportService, actionFilters, RunClusterHealthRequest::new); + this.client = client; + this.threadPool = threadPool; + this.contextSwitcher = contextSwitcher; + this.identityService = identityService; + } + + @Override + protected void doExecute(Task task, RunClusterHealthRequest request, ActionListener actionListener) { + String runAs = request.getRunAs(); + if ("user".equalsIgnoreCase(runAs)) { + Subject user = identityService.getCurrentSubject(); + try { + user.runAs(() -> { + ActionListener chr = ActionListener.wrap( + r -> { actionListener.onResponse(new RunClusterHealthResponse(true)); }, + actionListener::onFailure + ); + client.admin().cluster().health(new ClusterHealthRequest(), chr); + return null; + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } else if ("plugin".equalsIgnoreCase(runAs)) { + contextSwitcher.runAs(() -> { + ActionListener chr = ActionListener.wrap( + r -> { actionListener.onResponse(new RunClusterHealthResponse(true)); }, + actionListener::onFailure + ); + client.admin().cluster().health(new ClusterHealthRequest(), chr); + return null; + }); + } else { + ActionListener chr = ActionListener.wrap( + r -> { actionListener.onResponse(new RunClusterHealthResponse(true)); }, + actionListener::onFailure + ); + client.admin().cluster().health(new ClusterHealthRequest(), chr); + } + } +} diff --git a/src/main/java/org/opensearch/security/auth/SecurityUser.java b/src/main/java/org/opensearch/security/auth/SecurityUser.java new file mode 100644 index 0000000000..8ce4bfb3a1 --- /dev/null +++ b/src/main/java/org/opensearch/security/auth/SecurityUser.java @@ -0,0 +1,51 @@ +/* + * 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.auth; + +import java.security.Principal; +import java.util.concurrent.Callable; + +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.identity.NamedPrincipal; +import org.opensearch.identity.UserSubject; +import org.opensearch.identity.tokens.AuthToken; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.ThreadPool; + +public class SecurityUser implements UserSubject { + private final NamedPrincipal userPrincipal; + private final ThreadPool threadPool; + private final User user; + + SecurityUser(ThreadPool threadPool, User user) { + this.threadPool = threadPool; + this.user = user; + this.userPrincipal = new NamedPrincipal(user.getName()); + } + + @Override + public void authenticate(AuthToken authToken) { + // not implemented + } + + @Override + public Principal getPrincipal() { + return userPrincipal; + } + + @Override + public T runAs(Callable callable) throws Exception { + try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) { + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, user); + return callable.call(); + } + } +} diff --git a/src/main/java/org/opensearch/security/identity/ContextProvidingPluginSubject.java b/src/main/java/org/opensearch/security/identity/ContextProvidingPluginSubject.java new file mode 100644 index 0000000000..ab6dddceba --- /dev/null +++ b/src/main/java/org/opensearch/security/identity/ContextProvidingPluginSubject.java @@ -0,0 +1,51 @@ +/* + * 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.identity; + +import java.security.Principal; +import java.util.concurrent.Callable; + +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.identity.NamedPrincipal; +import org.opensearch.identity.PluginSubject; +import org.opensearch.plugins.Plugin; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.ThreadPool; + +public class ContextProvidingPluginSubject implements PluginSubject { + private final ThreadPool threadPool; + private final NamedPrincipal pluginPrincipal; + private final User pluginUser; + + public ContextProvidingPluginSubject(ThreadPool threadPool, Settings settings, Plugin plugin) { + super(); + this.threadPool = threadPool; + String principal = "plugin:" + plugin.getClass().getCanonicalName(); + this.pluginPrincipal = new NamedPrincipal(principal); + // Convention for plugin username. Prefixed with 'plugin:'. ':' is forbidden from usernames, so this + // guarantees that a user with this username cannot be created by other means. + this.pluginUser = new User(principal); + } + + @Override + public Principal getPrincipal() { + return pluginPrincipal; + } + + @Override + public T runAs(Callable callable) throws Exception { + try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) { + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, pluginUser); + return callable.call(); + } + } +} diff --git a/src/main/java/org/opensearch/security/identity/PluginContextSwitcher.java b/src/main/java/org/opensearch/security/identity/PluginContextSwitcher.java new file mode 100644 index 0000000000..a16671fda4 --- /dev/null +++ b/src/main/java/org/opensearch/security/identity/PluginContextSwitcher.java @@ -0,0 +1,35 @@ +/* + * 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. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ +package org.opensearch.security.identity; + +import java.util.Objects; +import java.util.concurrent.Callable; + +import org.opensearch.identity.PluginSubject; + +public class PluginContextSwitcher { + private PluginSubject pluginSubject; + + public PluginContextSwitcher() {} + + public void initialize(PluginSubject pluginSubject) { + this.pluginSubject = pluginSubject; + } + + public T runAs(Callable callable) { + Objects.requireNonNull(pluginSubject); + try { + return pluginSubject.runAs(callable); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/org/opensearch/security/auth/SecurityUserTests.java b/src/test/java/org/opensearch/security/auth/SecurityUserTests.java new file mode 100644 index 0000000000..4ae15ef303 --- /dev/null +++ b/src/test/java/org/opensearch/security/auth/SecurityUserTests.java @@ -0,0 +1,54 @@ +/* + * 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. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.auth; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import org.opensearch.security.user.User; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.security.support.ConfigConstants.OPENDISTRO_SECURITY_USER; +import static org.junit.Assert.assertNull; + +public class SecurityUserTests { + + public static boolean terminate(ThreadPool threadPool) { + return ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS); + } + + @Test + public void testSecurityUserSubjectRunAs() throws Exception { + final ThreadPool threadPool = new TestThreadPool(getClass().getName()); + + User user = new User("testUser"); + + SecurityUser subject = new SecurityUser(threadPool, user); + + assertThat(subject.getPrincipal().getName(), equalTo(user.getName())); + + assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); + + subject.runAs(() -> { + assertThat(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER), equalTo(user)); + return null; + }); + + assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); + + terminate(threadPool); + } +} diff --git a/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java b/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java new file mode 100644 index 0000000000..48851c48b3 --- /dev/null +++ b/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java @@ -0,0 +1,91 @@ +/* + * 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. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.identity; + +import org.junit.Test; + +import org.opensearch.common.settings.Settings; +import org.opensearch.plugins.IdentityAwarePlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.security.auth.SecurityUserTests; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.security.support.ConfigConstants.OPENDISTRO_SECURITY_USER; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; + +public class ContextProvidingPluginSubjectTests { + static class TestIdentityAwarePlugin extends Plugin implements IdentityAwarePlugin { + + } + + @Test + public void testSecurityUserSubjectRunAs() throws Exception { + final ThreadPool threadPool = new TestThreadPool(getClass().getName()); + + final Plugin testPlugin = new TestIdentityAwarePlugin(); + + final User pluginUser = new User("plugin:" + testPlugin.getClass().getCanonicalName()); + + ContextProvidingPluginSubject subject = new ContextProvidingPluginSubject(threadPool, Settings.EMPTY, testPlugin); + + assertThat(subject.getPrincipal().getName(), equalTo(testPlugin.getClass().getCanonicalName())); + + assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); + + subject.runAs(() -> { + assertThat(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER), equalTo(pluginUser)); + return null; + }); + + assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); + + SecurityUserTests.terminate(threadPool); + } + + @Test + public void testPluginContextSwitcherRunAs() throws Exception { + final ThreadPool threadPool = new TestThreadPool(getClass().getName()); + + final Plugin testPlugin = new TestIdentityAwarePlugin(); + + final PluginContextSwitcher contextSwitcher = new PluginContextSwitcher(); + + final User pluginUser = new User("plugin:" + testPlugin.getClass().getCanonicalName()); + + ContextProvidingPluginSubject subject = new ContextProvidingPluginSubject(threadPool, Settings.EMPTY, testPlugin); + + contextSwitcher.initialize(subject); + + assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); + + subject.runAs(() -> { + assertThat(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER), equalTo(pluginUser)); + return null; + }); + + assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); + + SecurityUserTests.terminate(threadPool); + } + + @Test + public void testPluginContextSwitcherUninitializedRunAs() throws Exception { + final PluginContextSwitcher contextSwitcher = new PluginContextSwitcher(); + + assertThrows(NullPointerException.class, () -> contextSwitcher.runAs(() -> null)); + } +} From de50e87a87cc33fac65b65903b9d4767949eef4f Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 11 Nov 2024 15:55:19 -0500 Subject: [PATCH 02/14] Complete rebase Signed-off-by: Craig Perkins --- .../opensearch/security/SystemIndexTests.java | 103 +++++++++++++++++- .../http/ExampleSystemIndexPlugin.java | 27 ----- ...ortIndexDocumentIntoSystemIndexAction.java | 2 - .../privileges/ActionPrivilegesTest.java | 3 +- .../test/framework/cluster/LocalCluster.java | 10 ++ .../security/OpenSearchSecurityPlugin.java | 15 ++- .../security/auth/BackendRegistry.java | 11 +- .../security/filter/SecurityFilter.java | 5 + .../security/privileges/ActionPrivileges.java | 60 +++++++++- .../privileges/PrivilegesEvaluator.java | 18 ++- .../SystemIndexAccessEvaluator.java | 25 +++++ .../security/support/ConfigConstants.java | 3 + .../org/opensearch/security/user/User.java | 11 +- 13 files changed, 248 insertions(+), 45 deletions(-) delete mode 100644 src/integrationTest/java/org/opensearch/security/http/ExampleSystemIndexPlugin.java diff --git a/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java b/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java index ae068255da..83eac441a0 100644 --- a/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java +++ b/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java @@ -19,7 +19,8 @@ import org.junit.runner.RunWith; import org.opensearch.core.rest.RestStatus; -import org.opensearch.security.http.ExampleSystemIndexPlugin; +import org.opensearch.security.plugin.SystemIndexPlugin1; +import org.opensearch.security.plugin.SystemIndexPlugin2; import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; import org.opensearch.test.framework.cluster.ClusterManager; import org.opensearch.test.framework.cluster.LocalCluster; @@ -27,7 +28,10 @@ import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.security.plugin.SystemIndexPlugin1.SYSTEM_INDEX_1; +import static org.opensearch.security.plugin.SystemIndexPlugin2.SYSTEM_INDEX_2; import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ROLES_ENABLED; import static org.opensearch.security.support.ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY; import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; @@ -44,7 +48,7 @@ public class SystemIndexTests { .anonymousAuth(false) .authc(AUTHC_DOMAIN) .users(USER_ADMIN) - .plugin(ExampleSystemIndexPlugin.class) + .plugin(SystemIndexPlugin1.class, SystemIndexPlugin2.class) .nodeSettings( Map.of( SECURITY_RESTAPI_ROLES_ENABLED, @@ -89,6 +93,101 @@ public void adminShouldNotBeAbleToDeleteSecurityIndex() { } } + @Test + public void testPluginShouldBeAbleToIndexDocumentIntoItsSystemIndex() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_1); + + assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + assertThat(response.getBody(), containsString(SystemIndexPlugin1.class.getCanonicalName())); + } + } + + @Test + public void testPluginShouldNotBeAbleToIndexDocumentIntoSystemIndexRegisteredByOtherPlugin() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_2); + + assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); + assertThat( + response.getBody(), + containsString( + "no permissions for [indices:admin/create] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1" + ) + ); + } + } + + @Test + public void testPluginShouldBeAbleToCreateSystemIndexButUserShouldNotBeAbleToIndex() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_1 + "?runAs=user"); + + assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); + assertThat(response.getBody(), containsString("no permissions for [indices:data/write/index] and User [name=admin")); + } + } + + @Test + public void testPluginShouldNotBeAbleToRunClusterActions() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.get("try-cluster-health/plugin"); + + assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); + assertThat( + response.getBody(), + containsString( + "no permissions for [cluster:monitor/health] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1" + ) + ); + } + } + + @Test + public void testAdminUserShouldBeAbleToRunClusterActions() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.get("try-cluster-health/user"); + + assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + } + } + + @Test + public void testAuthenticatedUserShouldBeAbleToRunClusterActions() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.get("try-cluster-health/default"); + + assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + } + } + + @Test + public void testPluginShouldBeAbleToBulkIndexDocumentIntoItsSystemIndex() { + 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())); + } + } + + @Test + public void testPluginShouldNotBeAbleToBulkIndexDocumentIntoMixOfSystemIndexWhereAtLeastOneDoesNotBelongToPlugin() { + try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) { + client.put(".system-index1"); + client.put(".system-index2"); + } + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.put("try-create-and-bulk-mixed-index"); + + assertThat( + response.getBody(), + containsString( + "no permissions for [indices:data/write/bulk[s]] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1" + ) + ); + } + } + @Test public void regularUserShouldGetNoResultsWhenSearchingSystemIndex() { // Create system index and index a dummy document as the super admin user, data returned to super admin diff --git a/src/integrationTest/java/org/opensearch/security/http/ExampleSystemIndexPlugin.java b/src/integrationTest/java/org/opensearch/security/http/ExampleSystemIndexPlugin.java deleted file mode 100644 index b4877aae14..0000000000 --- a/src/integrationTest/java/org/opensearch/security/http/ExampleSystemIndexPlugin.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.http; - -import java.util.Collection; -import java.util.Collections; - -import org.opensearch.common.settings.Settings; -import org.opensearch.indices.SystemIndexDescriptor; -import org.opensearch.plugins.Plugin; -import org.opensearch.plugins.SystemIndexPlugin; - -public class ExampleSystemIndexPlugin extends Plugin implements SystemIndexPlugin { - - @Override - public Collection getSystemIndexDescriptors(Settings settings) { - final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(".system-index1", "System index 1"); - return Collections.singletonList(systemIndexDescriptor); - } -} diff --git a/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java index 19ec06ca7d..c549da7809 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java +++ b/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java @@ -62,7 +62,6 @@ protected void doExecute( String indexName = request.getIndexName(); String runAs = request.getRunAs(); Subject userSubject = identityService.getCurrentSubject(); - System.out.println("User Subject: " + userSubject); try { contextSwitcher.runAs(() -> { client.admin().indices().create(new CreateIndexRequest(indexName), ActionListener.wrap(r -> { @@ -84,7 +83,6 @@ protected void doExecute( .source("{\"content\":1}", XContentType.JSON), ActionListener.wrap(r2 -> { User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - System.out.println("Test User: " + user); actionListener.onResponse(new IndexDocumentIntoSystemIndexResponse(true, user.getName())); }, actionListener::onFailure) ); diff --git a/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java b/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java index 7807dae748..221c710cb3 100644 --- a/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java +++ b/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java @@ -455,7 +455,8 @@ public IndicesAndAliases(IndexSpec indexSpec, ActionSpec actionSpec, Statefulnes settings, WellKnownActions.CLUSTER_ACTIONS, WellKnownActions.INDEX_ACTIONS, - WellKnownActions.INDEX_ACTIONS + WellKnownActions.INDEX_ACTIONS, + Map.of() ); if (statefulness == Statefulness.STATEFUL || statefulness == Statefulness.STATEFUL_LIMITED) { diff --git a/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java b/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java index 5ae8c0b125..cfdffb331a 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java +++ b/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java @@ -413,6 +413,16 @@ public Builder nodeSpecificSettings(int nodeNumber, Map settings return this; } + /** + * Adds additional plugins to the cluster + */ + @SafeVarargs + public final Builder plugin(Class... plugins) { + this.plugins.addAll(List.of(plugins)); + + return this; + } + /** * Adds additional plugins to the cluster */ diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 57ffc4df6f..6c8eb8c0a2 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -43,6 +43,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -69,6 +70,7 @@ import org.opensearch.SpecialPermission; import org.opensearch.Version; import org.opensearch.action.ActionRequest; +import org.opensearch.action.bulk.BulkAction; import org.opensearch.action.search.PitService; import org.opensearch.action.search.SearchScrollAction; import org.opensearch.action.support.ActionFilter; @@ -110,7 +112,6 @@ import org.opensearch.http.netty4.ssl.SecureNetty4HttpServerTransport; import org.opensearch.identity.PluginSubject; import org.opensearch.identity.Subject; -import org.opensearch.identity.noop.NoopSubject; import org.opensearch.index.IndexModule; import org.opensearch.index.cache.query.QueryCache; import org.opensearch.indices.IndicesService; @@ -164,7 +165,7 @@ import org.opensearch.security.hasher.PasswordHasherFactory; import org.opensearch.security.http.NonSslHttpServerTransport; import org.opensearch.security.http.XFFResolver; -import org.opensearch.security.identity.NoopPluginSubject; +import org.opensearch.security.identity.ContextProvidingPluginSubject; import org.opensearch.security.identity.SecurityTokenManager; import org.opensearch.security.privileges.ActionPrivileges; import org.opensearch.security.privileges.PrivilegesEvaluationException; @@ -221,6 +222,7 @@ import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.ENDPOINTS_WITH_PERMISSIONS; import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.SECURITY_CONFIG_UPDATE; import static org.opensearch.security.setting.DeprecatedSettings.checkForDeprecatedSetting; +import static org.opensearch.security.support.ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER; import static org.opensearch.security.support.ConfigConstants.SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX; import static org.opensearch.security.support.ConfigConstants.SECURITY_ALLOW_DEFAULT_INIT_USE_CLUSTER_STATE; import static org.opensearch.security.support.ConfigConstants.SECURITY_UNSUPPORTED_RESTAPI_ALLOW_SECURITYCONFIG_MODIFICATION; @@ -2115,8 +2117,7 @@ public Collection getSystemIndexDescriptors(Settings sett @Override public Subject getCurrentSubject() { - // Not supported - return new NoopSubject(); + return (Subject) threadPool.getThreadContext().getPersistent(OPENDISTRO_SECURITY_AUTHENTICATED_USER); } @Override @@ -2126,7 +2127,11 @@ public SecurityTokenManager getTokenManager() { @Override public PluginSubject getPluginSubject(Plugin plugin) { - return new NoopPluginSubject(threadPool); + Set clusterActions = new HashSet<>(); + clusterActions.add(BulkAction.NAME); + PluginSubject subject = new ContextProvidingPluginSubject(threadPool, settings, plugin); + sf.updatePluginToClusterAction(subject.getPrincipal().getName(), clusterActions); + return subject; } @Override diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index 0b00bcf943..5be7e9622a 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -55,6 +55,7 @@ import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.core.rest.RestStatus; +import org.opensearch.identity.UserSubject; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.auth.blocking.ClientBlockRegistry; import org.opensearch.security.auth.internal.NoOpAuthenticationBackend; @@ -223,7 +224,10 @@ public boolean authenticate(final SecurityRequestChannel request) { if (adminDns.isAdminDN(sslPrincipal)) { // PKI authenticated REST call - threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, new User(sslPrincipal)); + User superuser = new User(sslPrincipal); + UserSubject subject = new SecurityUser(threadPool, superuser); + threadPool.getThreadContext().putPersistent(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER, subject); + threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, superuser); auditLog.logSucceededLogin(sslPrincipal, true, null, request); return true; } @@ -389,6 +393,8 @@ public boolean authenticate(final SecurityRequestChannel request) { final User impersonatedUser = impersonate(request, authenticatedUser); threadPool.getThreadContext() .putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, impersonatedUser == null ? authenticatedUser : impersonatedUser); + UserSubject subject = new SecurityUser(threadPool, impersonatedUser == null ? authenticatedUser : impersonatedUser); + threadPool.getThreadContext().putPersistent(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER, subject); auditLog.logSucceededLogin( (impersonatedUser == null ? authenticatedUser : impersonatedUser).getName(), false, @@ -421,7 +427,10 @@ public boolean authenticate(final SecurityRequestChannel request) { User anonymousUser = new User(User.ANONYMOUS.getName(), new HashSet(User.ANONYMOUS.getRoles()), null); anonymousUser.setRequestedTenant(tenant); + UserSubject subject = new SecurityUser(threadPool, anonymousUser); + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, anonymousUser); + threadPool.getThreadContext().putPersistent(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER, subject); auditLog.logSucceededLogin(anonymousUser.getName(), false, null, request); if (isDebugEnabled) { log.debug("Anonymous User is authenticated"); diff --git a/src/main/java/org/opensearch/security/filter/SecurityFilter.java b/src/main/java/org/opensearch/security/filter/SecurityFilter.java index 3323c9e38a..efda9a809f 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityFilter.java @@ -320,6 +320,7 @@ private void ap if (Origin.LOCAL.toString().equals(threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN)) && (interClusterRequest || HeaderHelper.isDirectRequest(threadContext)) && (injectedRoles == null) + && (user == null) && !enforcePrivilegesEvaluation) { chain.proceed(task, action, request, listener); @@ -528,6 +529,10 @@ private boolean checkImmutableIndices(Object request, ActionListener listener) { return false; } + public void updatePluginToClusterAction(String pluginIdentifier, Set clusterActions) { + evalp.updatePluginToClusterActions(pluginIdentifier, clusterActions); + } + private boolean isRequestIndexImmutable(Object request) { final IndexResolverReplacer.Resolved resolved = indexResolverReplacer.resolveRequest(request); if (resolved.isLocalAll()) { diff --git a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java index 87ac32d090..67ba95e587 100644 --- a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java +++ b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java @@ -90,9 +90,10 @@ public ActionPrivileges( Settings settings, ImmutableSet wellKnownClusterActions, ImmutableSet wellKnownIndexActions, - ImmutableSet explicitlyRequiredIndexActions + ImmutableSet explicitlyRequiredIndexActions, + Map> pluginToClusterActions ) { - this.cluster = new ClusterPrivileges(roles, actionGroups, wellKnownClusterActions); + this.cluster = new ClusterPrivileges(roles, actionGroups, wellKnownClusterActions, pluginToClusterActions); this.index = new IndexPrivileges(roles, actionGroups, wellKnownIndexActions, explicitlyRequiredIndexActions); this.roles = roles; this.actionGroups = actionGroups; @@ -115,7 +116,27 @@ public ActionPrivileges( settings, WellKnownActions.CLUSTER_ACTIONS, WellKnownActions.INDEX_ACTIONS, - WellKnownActions.EXPLICITLY_REQUIRED_INDEX_ACTIONS + WellKnownActions.EXPLICITLY_REQUIRED_INDEX_ACTIONS, + Map.of() + ); + } + + public ActionPrivileges( + SecurityDynamicConfiguration roles, + FlattenedActionGroups actionGroups, + Supplier> indexMetadataSupplier, + Settings settings, + Map> pluginToClusterActions + ) { + this( + roles, + actionGroups, + indexMetadataSupplier, + settings, + WellKnownActions.CLUSTER_ACTIONS, + WellKnownActions.INDEX_ACTIONS, + WellKnownActions.EXPLICITLY_REQUIRED_INDEX_ACTIONS, + pluginToClusterActions ); } @@ -297,6 +318,8 @@ static class ClusterPrivileges { */ private final ImmutableMap rolesToActionMatcher; + private final ImmutableMap usersToActionMatcher; + private final ImmutableSet wellKnownClusterActions; /** @@ -310,7 +333,8 @@ static class ClusterPrivileges { ClusterPrivileges( SecurityDynamicConfiguration roles, FlattenedActionGroups actionGroups, - ImmutableSet wellKnownClusterActions + ImmutableSet wellKnownClusterActions, + Map> pluginToClusterActions ) { DeduplicatingCompactSubSetBuilder roleSetBuilder = new DeduplicatingCompactSubSetBuilder<>( roles.getCEntries().keySet() @@ -318,6 +342,7 @@ static class ClusterPrivileges { Map> actionToRoles = new HashMap<>(); ImmutableSet.Builder rolesWithWildcardPermissions = ImmutableSet.builder(); ImmutableMap.Builder rolesToActionMatcher = ImmutableMap.builder(); + ImmutableMap.Builder usersToActionMatcher = ImmutableMap.builder(); for (Map.Entry entry : roles.getCEntries().entrySet()) { try { @@ -367,6 +392,14 @@ static class ClusterPrivileges { } } + if (pluginToClusterActions != null) { + for (String pluginIdentifier : pluginToClusterActions.keySet()) { + Set clusterActions = pluginToClusterActions.get(pluginIdentifier); + WildcardMatcher matcher = WildcardMatcher.from(clusterActions); + usersToActionMatcher.put(pluginIdentifier, matcher); + } + } + DeduplicatingCompactSubSetBuilder.Completed completedRoleSetBuilder = roleSetBuilder.build(); this.actionToRoles = actionToRoles.entrySet() @@ -374,6 +407,7 @@ static class ClusterPrivileges { .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, entry -> entry.getValue().build(completedRoleSetBuilder))); this.rolesWithWildcardPermissions = rolesWithWildcardPermissions.build(); this.rolesToActionMatcher = rolesToActionMatcher.build(); + this.usersToActionMatcher = usersToActionMatcher.build(); this.wellKnownClusterActions = wellKnownClusterActions; } @@ -407,6 +441,14 @@ PrivilegesEvaluatorResponse providesPrivilege(PrivilegesEvaluationContext contex } } + // 4: If plugin is performing the action, check if plugin has permission + if (context.getUser().isPluginUser()) { + WildcardMatcher matcher = this.usersToActionMatcher.get(context.getUser().getName()); + if (matcher != null && matcher.test(action)) { + return PrivilegesEvaluatorResponse.ok(); + } + } + return PrivilegesEvaluatorResponse.insufficient(action); } @@ -476,6 +518,16 @@ PrivilegesEvaluatorResponse providesAnyPrivilege(PrivilegesEvaluationContext con } } + // 4: If plugin is performing the action, check if plugin has permission + if (context.getUser().isPluginUser()) { + WildcardMatcher matcher = this.usersToActionMatcher.get(context.getUser().getName()); + for (String action : actions) { + if (matcher != null && matcher.test(action)) { + return PrivilegesEvaluatorResponse.ok(); + } + } + } + if (actions.size() == 1) { return PrivilegesEvaluatorResponse.insufficient(actions.iterator().next()); } else { diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 36666972ec..8f30d81dba 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -142,6 +143,7 @@ public class PrivilegesEvaluator { private final boolean checkSnapshotRestoreWritePrivileges; private final ClusterInfoHolder clusterInfoHolder; + private final ConfigurationRepository configurationRepository; private ConfigModel configModel; private final IndexResolverReplacer irr; private final SnapshotRestoreEvaluator snapshotRestoreEvaluator; @@ -152,6 +154,7 @@ public class PrivilegesEvaluator { private DynamicConfigModel dcm; private final NamedXContentRegistry namedXContentRegistry; private final Settings settings; + private final Map> pluginToClusterActions; private final AtomicReference actionPrivileges = new AtomicReference<>(); public PrivilegesEvaluator( @@ -175,6 +178,7 @@ public PrivilegesEvaluator( this.threadContext = threadContext; this.privilegesInterceptor = privilegesInterceptor; + this.pluginToClusterActions = new HashMap<>(); this.clusterStateSupplier = clusterStateSupplier; this.settings = settings; @@ -191,6 +195,7 @@ public PrivilegesEvaluator( termsAggregationEvaluator = new TermsAggregationEvaluator(); pitPrivilegesEvaluator = new PitPrivilegesEvaluator(); this.namedXContentRegistry = namedXContentRegistry; + this.configurationRepository = configurationRepository; if (configurationRepository != null) { configurationRepository.subscribeOnChange(configMap -> { @@ -231,7 +236,8 @@ void updateConfiguration( DynamicConfigFactory.addStatics(rolesConfiguration.clone()), flattenedActionGroups, () -> clusterStateSupplier.get().metadata().getIndicesLookup(), - settings + settings, + pluginToClusterActions ); Metadata metadata = clusterStateSupplier.get().metadata(); actionPrivileges.updateStatefulIndexPrivileges(metadata.getIndicesLookup(), metadata.version()); @@ -394,7 +400,11 @@ public PrivilegesEvaluatorResponse evaluate(PrivilegesEvaluationContext context) // Security index access if (systemIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse, context, actionPrivileges, user) .isComplete()) { - return presponse; + if (!presponse.isAllowed()) { + return PrivilegesEvaluatorResponse.insufficient(action0); + } else { + return presponse; + } } // Protected index access @@ -838,4 +848,8 @@ private List toString(List aliases) { return Collections.unmodifiableList(ret); } + + public void updatePluginToClusterActions(String pluginIdentifier, Set clusterActions) { + pluginToClusterActions.put(pluginIdentifier, clusterActions); + } } diff --git a/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java index 99828f7b17..e8995c637e 100644 --- a/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java @@ -302,6 +302,31 @@ private void evaluateSystemIndicesAccess( } } + if (user.isPluginUser()) { + Set matchingSystemIndices = SystemIndexRegistry.matchesPluginSystemIndexPattern( + user.getName().replace("plugin:", ""), + requestedResolved.getAllIndices() + ); + if (requestedResolved.getAllIndices().equals(matchingSystemIndices)) { + // plugin is authorized to perform any actions on its own registered system indices + presponse.allowed = true; + presponse.markComplete(); + } else { + if (log.isInfoEnabled()) { + log.info( + "Plugin {} can only perform {} on it's own registered System Indices. System indices from request that match plugin's registered system indices: {}", + user.getName(), + action, + matchingSystemIndices + ); + } + presponse.allowed = false; + presponse.getMissingPrivileges(); + presponse.markComplete(); + } + return; + } + if (isActionAllowed(action)) { if (requestedResolved.isLocalAll()) { if (filterSecurityIndex) { diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index f35afc6489..321fc261dc 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -115,6 +115,9 @@ public class ConfigConstants { public static final String OPENDISTRO_SECURITY_USER = OPENDISTRO_SECURITY_CONFIG_PREFIX + "user"; public static final String OPENDISTRO_SECURITY_USER_HEADER = OPENDISTRO_SECURITY_CONFIG_PREFIX + "user_header"; + // persistent header. This header is set once and cannot be stashed + public static final String OPENDISTRO_SECURITY_AUTHENTICATED_USER = OPENDISTRO_SECURITY_CONFIG_PREFIX + "authenticated_user"; + public static final String OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT = OPENDISTRO_SECURITY_CONFIG_PREFIX + "user_info"; public static final String OPENDISTRO_SECURITY_INJECTED_USER = "injected_user"; diff --git a/src/main/java/org/opensearch/security/user/User.java b/src/main/java/org/opensearch/security/user/User.java index 6abba3d734..190729623d 100644 --- a/src/main/java/org/opensearch/security/user/User.java +++ b/src/main/java/org/opensearch/security/user/User.java @@ -290,10 +290,19 @@ public final Set getSecurityRoles() { /** * Check the custom attributes associated with this user * - * @return true if it has a service account attributes. otherwise false + * @return true if it has a service account attributes, otherwise false */ public boolean isServiceAccount() { Map userAttributesMap = this.getCustomAttributesMap(); return userAttributesMap != null && "true".equals(userAttributesMap.get("attr.internal.service")); } + + /** + * Check the custom attributes associated with this user + * + * @return true if it has a plugin account attributes, otherwise false + */ + public boolean isPluginUser() { + return name != null && name.startsWith("plugin:"); + } } From 9a997b2c72d01f7c9bfabe9a2904a56baf7b4e00 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 11 Nov 2024 16:10:49 -0500 Subject: [PATCH 03/14] Fix tests Signed-off-by: Craig Perkins --- .../identity/ContextProvidingPluginSubjectTests.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java b/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java index 48851c48b3..369a661298 100644 --- a/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java +++ b/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java @@ -38,11 +38,13 @@ public void testSecurityUserSubjectRunAs() throws Exception { final Plugin testPlugin = new TestIdentityAwarePlugin(); - final User pluginUser = new User("plugin:" + testPlugin.getClass().getCanonicalName()); + final String pluginPrincipal = "plugin:" + testPlugin.getClass().getCanonicalName(); + + final User pluginUser = new User(pluginPrincipal); ContextProvidingPluginSubject subject = new ContextProvidingPluginSubject(threadPool, Settings.EMPTY, testPlugin); - assertThat(subject.getPrincipal().getName(), equalTo(testPlugin.getClass().getCanonicalName())); + assertThat(subject.getPrincipal().getName(), equalTo(pluginPrincipal)); assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); @@ -64,7 +66,9 @@ public void testPluginContextSwitcherRunAs() throws Exception { final PluginContextSwitcher contextSwitcher = new PluginContextSwitcher(); - final User pluginUser = new User("plugin:" + testPlugin.getClass().getCanonicalName()); + final String pluginPrincipal = "plugin:" + testPlugin.getClass().getCanonicalName(); + + final User pluginUser = new User(pluginPrincipal); ContextProvidingPluginSubject subject = new ContextProvidingPluginSubject(threadPool, Settings.EMPTY, testPlugin); From 372c65719296dccdf14e81bb48cc64822e80be16 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 11 Nov 2024 16:52:13 -0500 Subject: [PATCH 04/14] Leave action out of error message for now Signed-off-by: Craig Perkins --- .../org/opensearch/security/SystemIndexTests.java | 14 ++++---------- .../security/privileges/PrivilegesEvaluator.java | 6 +----- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java b/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java index 83eac441a0..da8715b673 100644 --- a/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java +++ b/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java @@ -111,9 +111,7 @@ public void testPluginShouldNotBeAbleToIndexDocumentIntoSystemIndexRegisteredByO assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); assertThat( response.getBody(), - containsString( - "no permissions for [indices:admin/create] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1" - ) + containsString("no permissions for [] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1") ); } } @@ -124,7 +122,7 @@ public void testPluginShouldBeAbleToCreateSystemIndexButUserShouldNotBeAbleToInd HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_1 + "?runAs=user"); assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); - assertThat(response.getBody(), containsString("no permissions for [indices:data/write/index] and User [name=admin")); + assertThat(response.getBody(), containsString("no permissions for [] and User [name=admin")); } } @@ -136,9 +134,7 @@ public void testPluginShouldNotBeAbleToRunClusterActions() { assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); assertThat( response.getBody(), - containsString( - "no permissions for [cluster:monitor/health] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1" - ) + containsString("no permissions for [] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1") ); } } @@ -181,9 +177,7 @@ public void testPluginShouldNotBeAbleToBulkIndexDocumentIntoMixOfSystemIndexWher assertThat( response.getBody(), - containsString( - "no permissions for [indices:data/write/bulk[s]] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1" - ) + containsString("no permissions for [] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1") ); } } diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 8f30d81dba..158a5d0a48 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -400,11 +400,7 @@ public PrivilegesEvaluatorResponse evaluate(PrivilegesEvaluationContext context) // Security index access if (systemIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse, context, actionPrivileges, user) .isComplete()) { - if (!presponse.isAllowed()) { - return PrivilegesEvaluatorResponse.insufficient(action0); - } else { - return presponse; - } + return presponse; } // Protected index access From c25e1ab5bf22ccd1c74d1083946632400d5f3bae Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 14 Nov 2024 16:18:34 -0500 Subject: [PATCH 05/14] Remove calls to isPluginUser Signed-off-by: Craig Perkins --- .../org/opensearch/security/privileges/ActionPrivileges.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java index 67ba95e587..8cceb99773 100644 --- a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java +++ b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java @@ -442,7 +442,7 @@ PrivilegesEvaluatorResponse providesPrivilege(PrivilegesEvaluationContext contex } // 4: If plugin is performing the action, check if plugin has permission - if (context.getUser().isPluginUser()) { + if (this.usersToActionMatcher.containsKey(context.getUser().getName())) { WildcardMatcher matcher = this.usersToActionMatcher.get(context.getUser().getName()); if (matcher != null && matcher.test(action)) { return PrivilegesEvaluatorResponse.ok(); @@ -519,7 +519,7 @@ PrivilegesEvaluatorResponse providesAnyPrivilege(PrivilegesEvaluationContext con } // 4: If plugin is performing the action, check if plugin has permission - if (context.getUser().isPluginUser()) { + if (this.usersToActionMatcher.containsKey(context.getUser().getName())) { WildcardMatcher matcher = this.usersToActionMatcher.get(context.getUser().getName()); for (String action : actions) { if (matcher != null && matcher.test(action)) { From 941c131bfa19143d04ad9116d805da7a2b6d96fa Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 19 Nov 2024 13:31:19 -0500 Subject: [PATCH 06/14] Pluralize Signed-off-by: Craig Perkins --- .../java/org/opensearch/security/OpenSearchSecurityPlugin.java | 2 +- .../java/org/opensearch/security/filter/SecurityFilter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 6c8eb8c0a2..21551d731a 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -2130,7 +2130,7 @@ public PluginSubject getPluginSubject(Plugin plugin) { Set clusterActions = new HashSet<>(); clusterActions.add(BulkAction.NAME); PluginSubject subject = new ContextProvidingPluginSubject(threadPool, settings, plugin); - sf.updatePluginToClusterAction(subject.getPrincipal().getName(), clusterActions); + sf.updatePluginToClusterActions(subject.getPrincipal().getName(), clusterActions); return subject; } diff --git a/src/main/java/org/opensearch/security/filter/SecurityFilter.java b/src/main/java/org/opensearch/security/filter/SecurityFilter.java index efda9a809f..5bdf275306 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityFilter.java @@ -529,7 +529,7 @@ private boolean checkImmutableIndices(Object request, ActionListener listener) { return false; } - public void updatePluginToClusterAction(String pluginIdentifier, Set clusterActions) { + public void updatePluginToClusterActions(String pluginIdentifier, Set clusterActions) { evalp.updatePluginToClusterActions(pluginIdentifier, clusterActions); } From 3e353c94f79b548f95e0b3eb75be665adcc7c1ee Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 11 Dec 2024 15:12:19 -0500 Subject: [PATCH 07/14] Address review feedback Signed-off-by: Craig Perkins --- .../org/opensearch/security/ThreadPoolTests.java | 2 -- ...BulkIndexDocumentIntoMixOfSystemIndexAction.java | 5 ++--- .../opensearch/security/auth/BackendRegistry.java | 13 ++++--------- .../opensearch/security/filter/SecurityFilter.java | 4 +--- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/integrationTest/java/org/opensearch/security/ThreadPoolTests.java b/src/integrationTest/java/org/opensearch/security/ThreadPoolTests.java index 6fb1a2c91b..10aaab0f73 100644 --- a/src/integrationTest/java/org/opensearch/security/ThreadPoolTests.java +++ b/src/integrationTest/java/org/opensearch/security/ThreadPoolTests.java @@ -21,7 +21,6 @@ import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.core.rest.RestStatus; import org.opensearch.core.xcontent.XContentBuilder; -import org.opensearch.security.http.ExampleSystemIndexPlugin; import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; import org.opensearch.test.framework.cluster.ClusterManager; import org.opensearch.test.framework.cluster.LocalCluster; @@ -47,7 +46,6 @@ public class ThreadPoolTests { .anonymousAuth(false) .authc(AUTHC_DOMAIN) .users(USER_ADMIN) - .plugin(ExampleSystemIndexPlugin.class) .nodeSettings(Map.of(SECURITY_RESTAPI_ROLES_ENABLED, List.of("user_" + USER_ADMIN.getName() + "__" + ALL_ACCESS.getName()))) .build(); diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java index 0d1dc4fe01..34ec19749f 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java +++ b/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java @@ -18,7 +18,6 @@ import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.action.ActionListener; import org.opensearch.core.rest.RestStatus; import org.opensearch.core.xcontent.ToXContent; @@ -61,8 +60,8 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client public void accept(RestChannel channel) throws Exception { contextSwitcher.runAs(() -> { BulkRequestBuilder builder = client.prepareBulk(); - builder.add(new IndexRequest(SYSTEM_INDEX_1).source("{\"content\":1}", XContentType.JSON)); - builder.add(new IndexRequest(SYSTEM_INDEX_2).source("{\"content\":1}", XContentType.JSON)); + builder.add(new IndexRequest(SYSTEM_INDEX_1).source("content", 1)); + builder.add(new IndexRequest(SYSTEM_INDEX_2).source("content", 1)); builder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); BulkRequest bulkRequest = builder.request(); client.bulk(bulkRequest, ActionListener.wrap(r -> { diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index 5be7e9622a..16a7c76a0d 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -391,16 +391,11 @@ public boolean authenticate(final SecurityRequestChannel request) { if (authenticated) { final User impersonatedUser = impersonate(request, authenticatedUser); - threadPool.getThreadContext() - .putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, impersonatedUser == null ? authenticatedUser : impersonatedUser); - UserSubject subject = new SecurityUser(threadPool, impersonatedUser == null ? authenticatedUser : impersonatedUser); + final User effectiveUser = impersonatedUser == null ? authenticatedUser : impersonatedUser; + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, effectiveUser); + UserSubject subject = new SecurityUser(threadPool, effectiveUser); threadPool.getThreadContext().putPersistent(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER, subject); - auditLog.logSucceededLogin( - (impersonatedUser == null ? authenticatedUser : impersonatedUser).getName(), - false, - authenticatedUser.getName(), - request - ); + auditLog.logSucceededLogin(effectiveUser.getName(), false, authenticatedUser.getName(), request); } else { if (isDebugEnabled) { log.debug("User still not authenticated after checking {} auth domains", restAuthDomains.size()); diff --git a/src/main/java/org/opensearch/security/filter/SecurityFilter.java b/src/main/java/org/opensearch/security/filter/SecurityFilter.java index 5bdf275306..183e3743a3 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityFilter.java @@ -319,9 +319,7 @@ private void ap if (Origin.LOCAL.toString().equals(threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN)) && (interClusterRequest || HeaderHelper.isDirectRequest(threadContext)) - && (injectedRoles == null) - && (user == null) - && !enforcePrivilegesEvaluation) { + && (user == null)) { chain.proceed(task, action, request, listener); return; From ce80b7c50bd3224b15b93ae92da2207dc87220f5 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 11 Dec 2024 15:20:58 -0500 Subject: [PATCH 08/14] Check if pluginUser Signed-off-by: Craig Perkins --- .../org/opensearch/security/privileges/ActionPrivileges.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java index 8cceb99773..eb560ed901 100644 --- a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java +++ b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java @@ -442,7 +442,7 @@ PrivilegesEvaluatorResponse providesPrivilege(PrivilegesEvaluationContext contex } // 4: If plugin is performing the action, check if plugin has permission - if (this.usersToActionMatcher.containsKey(context.getUser().getName())) { + if (context.getUser().isPluginUser() && this.usersToActionMatcher.containsKey(context.getUser().getName())) { WildcardMatcher matcher = this.usersToActionMatcher.get(context.getUser().getName()); if (matcher != null && matcher.test(action)) { return PrivilegesEvaluatorResponse.ok(); From 154556c091afd5eff1569ce787ae4c0ccdeba96e Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 13 Dec 2024 15:50:49 -0500 Subject: [PATCH 09/14] Remove overloaded method Signed-off-by: Craig Perkins --- .../opensearch/test/framework/cluster/LocalCluster.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java b/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java index cfdffb331a..30e826829a 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java +++ b/src/integrationTest/java/org/opensearch/test/framework/cluster/LocalCluster.java @@ -423,15 +423,6 @@ public final Builder plugin(Class... plugins) { return this; } - /** - * Adds additional plugins to the cluster - */ - public Builder plugin(Class plugin) { - this.plugins.add(plugin); - - return this; - } - public Builder authFailureListeners(AuthFailureListeners listener) { testSecurityConfig.authFailureListeners(listener); return this; From 1f7417c08c8a9d8c8fe3a2ed1f68081741a6913f Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 30 Dec 2024 11:52:26 -0500 Subject: [PATCH 10/14] Respond to PR feedback Signed-off-by: Craig Perkins --- .../{ => systemindex}/SystemIndexTests.java | 22 +++++++----- .../IndexDocumentIntoSystemIndexAction.java | 2 +- .../IndexDocumentIntoSystemIndexRequest.java | 2 +- .../IndexDocumentIntoSystemIndexResponse.java | 2 +- .../sampleplugin}/PluginContextSwitcher.java | 4 +-- ...dexDocumentIntoMixOfSystemIndexAction.java | 7 ++-- ...ulkIndexDocumentIntoSystemIndexAction.java | 3 +- ...estIndexDocumentIntoSystemIndexAction.java | 2 +- .../RestRunClusterHealthAction.java | 3 +- .../sampleplugin}/RunClusterHealthAction.java | 2 +- .../RunClusterHealthRequest.java | 2 +- .../RunClusterHealthResponse.java | 2 +- .../sampleplugin}/SystemIndexPlugin1.java | 3 +- .../sampleplugin}/SystemIndexPlugin2.java | 2 +- ...ortIndexDocumentIntoSystemIndexAction.java | 3 +- .../TransportRunClusterHealthAction.java | 3 +- .../security/auth/BackendRegistry.java | 2 +- .../SystemIndexAccessEvaluator.java | 2 +- .../ContextProvidingPluginSubjectTests.java | 36 ------------------- 19 files changed, 34 insertions(+), 70 deletions(-) rename src/integrationTest/java/org/opensearch/security/{ => systemindex}/SystemIndexTests.java (89%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/IndexDocumentIntoSystemIndexAction.java (92%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/IndexDocumentIntoSystemIndexRequest.java (95%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/IndexDocumentIntoSystemIndexResponse.java (96%) rename src/{main/java/org/opensearch/security/identity => integrationTest/java/org/opensearch/security/systemindex/sampleplugin}/PluginContextSwitcher.java (93%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java (91%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/RestBulkIndexDocumentIntoSystemIndexAction.java (96%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/RestIndexDocumentIntoSystemIndexAction.java (96%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/RestRunClusterHealthAction.java (93%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/RunClusterHealthAction.java (91%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/RunClusterHealthRequest.java (94%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/RunClusterHealthResponse.java (95%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/SystemIndexPlugin1.java (97%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/SystemIndexPlugin2.java (97%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/TransportIndexDocumentIntoSystemIndexAction.java (97%) rename src/integrationTest/java/org/opensearch/security/{plugin => systemindex/sampleplugin}/TransportRunClusterHealthAction.java (97%) diff --git a/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java b/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java similarity index 89% rename from src/integrationTest/java/org/opensearch/security/SystemIndexTests.java rename to src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java index da8715b673..1bbc4f10ec 100644 --- a/src/integrationTest/java/org/opensearch/security/SystemIndexTests.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java @@ -7,7 +7,7 @@ * compatible open source license. * */ -package org.opensearch.security; +package org.opensearch.security.systemindex; import java.util.List; import java.util.Map; @@ -19,8 +19,8 @@ import org.junit.runner.RunWith; import org.opensearch.core.rest.RestStatus; -import org.opensearch.security.plugin.SystemIndexPlugin1; -import org.opensearch.security.plugin.SystemIndexPlugin2; +import org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1; +import org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin2; import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; import org.opensearch.test.framework.cluster.ClusterManager; import org.opensearch.test.framework.cluster.LocalCluster; @@ -30,10 +30,10 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.opensearch.security.plugin.SystemIndexPlugin1.SYSTEM_INDEX_1; -import static org.opensearch.security.plugin.SystemIndexPlugin2.SYSTEM_INDEX_2; import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ROLES_ENABLED; import static org.opensearch.security.support.ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY; +import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1.SYSTEM_INDEX_1; +import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin2.SYSTEM_INDEX_2; import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; import static org.opensearch.test.framework.TestSecurityConfig.User.USER_ADMIN; @@ -111,7 +111,9 @@ public void testPluginShouldNotBeAbleToIndexDocumentIntoSystemIndexRegisteredByO assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); assertThat( response.getBody(), - containsString("no permissions for [] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1") + containsString( + "no permissions for [] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1" + ) ); } } @@ -134,7 +136,9 @@ public void testPluginShouldNotBeAbleToRunClusterActions() { assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); assertThat( response.getBody(), - containsString("no permissions for [] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1") + containsString( + "no permissions for [cluster:monitor/health] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1" + ) ); } } @@ -177,7 +181,9 @@ public void testPluginShouldNotBeAbleToBulkIndexDocumentIntoMixOfSystemIndexWher assertThat( response.getBody(), - containsString("no permissions for [] and User [name=plugin:org.opensearch.security.plugin.SystemIndexPlugin1") + containsString( + "no permissions for [] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1" + ) ); } } diff --git a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexAction.java similarity index 92% rename from src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexAction.java index 9a60de201c..a78c57e1d1 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import org.opensearch.action.ActionType; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexRequest.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexRequest.java similarity index 95% rename from src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexRequest.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexRequest.java index d1b644ad11..ae7c345457 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexRequest.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexRequest.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.io.IOException; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexResponse.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexResponse.java similarity index 96% rename from src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexResponse.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexResponse.java index 9779cca252..e154742fe2 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/IndexDocumentIntoSystemIndexResponse.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/IndexDocumentIntoSystemIndexResponse.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; // CS-SUPPRESS-SINGLE: RegexpSingleline It is not possible to use phrase "cluster manager" instead of master here import java.io.IOException; diff --git a/src/main/java/org/opensearch/security/identity/PluginContextSwitcher.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/PluginContextSwitcher.java similarity index 93% rename from src/main/java/org/opensearch/security/identity/PluginContextSwitcher.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/PluginContextSwitcher.java index a16671fda4..4ef420efdb 100644 --- a/src/main/java/org/opensearch/security/identity/PluginContextSwitcher.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/PluginContextSwitcher.java @@ -8,7 +8,7 @@ * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ -package org.opensearch.security.identity; +package org.opensearch.security.systemindex.sampleplugin; import java.util.Objects; import java.util.concurrent.Callable; @@ -21,11 +21,11 @@ public class PluginContextSwitcher { public PluginContextSwitcher() {} public void initialize(PluginSubject pluginSubject) { + Objects.requireNonNull(pluginSubject); this.pluginSubject = pluginSubject; } public T runAs(Callable callable) { - Objects.requireNonNull(pluginSubject); try { return pluginSubject.runAs(callable); } catch (Exception e) { diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java similarity index 91% rename from src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java index 34ec19749f..e9b569a263 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestBulkIndexDocumentIntoMixOfSystemIndexAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.util.List; @@ -25,12 +25,11 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.security.identity.PluginContextSwitcher; import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.PUT; -import static org.opensearch.security.plugin.SystemIndexPlugin1.SYSTEM_INDEX_1; -import static org.opensearch.security.plugin.SystemIndexPlugin2.SYSTEM_INDEX_2; +import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1.SYSTEM_INDEX_1; +import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin2.SYSTEM_INDEX_2; public class RestBulkIndexDocumentIntoMixOfSystemIndexAction extends BaseRestHandler { diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestBulkIndexDocumentIntoSystemIndexAction.java similarity index 96% rename from src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoSystemIndexAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestBulkIndexDocumentIntoSystemIndexAction.java index 56fa3ef2a9..8b37e54164 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RestBulkIndexDocumentIntoSystemIndexAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestBulkIndexDocumentIntoSystemIndexAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.util.List; @@ -27,7 +27,6 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.security.identity.PluginContextSwitcher; import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.PUT; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestIndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestIndexDocumentIntoSystemIndexAction.java similarity index 96% rename from src/integrationTest/java/org/opensearch/security/plugin/RestIndexDocumentIntoSystemIndexAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestIndexDocumentIntoSystemIndexAction.java index e668e8bccc..9092c97988 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RestIndexDocumentIntoSystemIndexAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestIndexDocumentIntoSystemIndexAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.util.List; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RestRunClusterHealthAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestRunClusterHealthAction.java similarity index 93% rename from src/integrationTest/java/org/opensearch/security/plugin/RestRunClusterHealthAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestRunClusterHealthAction.java index 755f3278f0..f4674b4377 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RestRunClusterHealthAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RestRunClusterHealthAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.util.List; @@ -17,7 +17,6 @@ import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; -import org.opensearch.security.identity.PluginContextSwitcher; import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.GET; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthAction.java similarity index 91% rename from src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthAction.java index 4234879bb8..dca6b8d2b7 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import org.opensearch.action.ActionType; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthRequest.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthRequest.java similarity index 94% rename from src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthRequest.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthRequest.java index 8ae08bd6ff..099264313e 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthRequest.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthRequest.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.io.IOException; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthResponse.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthResponse.java similarity index 95% rename from src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthResponse.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthResponse.java index 7a855744dc..a500755e22 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/RunClusterHealthResponse.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/RunClusterHealthResponse.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; // CS-SUPPRESS-SINGLE: RegexpSingleline It is not possible to use phrase "cluster manager" instead of master here import java.io.IOException; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin1.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin1.java similarity index 97% rename from src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin1.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin1.java index 3f1112c4b6..9805619965 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin1.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin1.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.util.Arrays; import java.util.Collection; @@ -39,7 +39,6 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; import org.opensearch.script.ScriptService; -import org.opensearch.security.identity.PluginContextSwitcher; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin2.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin2.java similarity index 97% rename from src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin2.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin2.java index 8fcf23e3db..f91cbd3226 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/SystemIndexPlugin2.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/SystemIndexPlugin2.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import java.util.Collection; import java.util.Collections; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/TransportIndexDocumentIntoSystemIndexAction.java similarity index 97% rename from src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/TransportIndexDocumentIntoSystemIndexAction.java index c549da7809..6138cfbb54 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/TransportIndexDocumentIntoSystemIndexAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/TransportIndexDocumentIntoSystemIndexAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.index.IndexRequest; @@ -21,7 +21,6 @@ import org.opensearch.core.action.ActionListener; import org.opensearch.identity.IdentityService; import org.opensearch.identity.Subject; -import org.opensearch.security.identity.PluginContextSwitcher; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.User; import org.opensearch.tasks.Task; diff --git a/src/integrationTest/java/org/opensearch/security/plugin/TransportRunClusterHealthAction.java b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/TransportRunClusterHealthAction.java similarity index 97% rename from src/integrationTest/java/org/opensearch/security/plugin/TransportRunClusterHealthAction.java rename to src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/TransportRunClusterHealthAction.java index 4be06933a2..310262c947 100644 --- a/src/integrationTest/java/org/opensearch/security/plugin/TransportRunClusterHealthAction.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/sampleplugin/TransportRunClusterHealthAction.java @@ -8,7 +8,7 @@ * */ -package org.opensearch.security.plugin; +package org.opensearch.security.systemindex.sampleplugin; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -19,7 +19,6 @@ import org.opensearch.core.action.ActionListener; import org.opensearch.identity.IdentityService; import org.opensearch.identity.Subject; -import org.opensearch.security.identity.PluginContextSwitcher; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index 16a7c76a0d..07885d6465 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -226,7 +226,7 @@ public boolean authenticate(final SecurityRequestChannel request) { // PKI authenticated REST call User superuser = new User(sslPrincipal); UserSubject subject = new SecurityUser(threadPool, superuser); - threadPool.getThreadContext().putPersistent(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER, subject); + threadContext.putPersistent(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER, subject); threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, superuser); auditLog.logSucceededLogin(sslPrincipal, true, null, request); return true; diff --git a/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java index e8995c637e..7337cf511b 100644 --- a/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SystemIndexAccessEvaluator.java @@ -302,7 +302,7 @@ private void evaluateSystemIndicesAccess( } } - if (user.isPluginUser()) { + if (user.isPluginUser() && !requestedResolved.isLocalAll()) { Set matchingSystemIndices = SystemIndexRegistry.matchesPluginSystemIndexPattern( user.getName().replace("plugin:", ""), requestedResolved.getAllIndices() diff --git a/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java b/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java index 369a661298..bb5360e8f2 100644 --- a/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java +++ b/src/test/java/org/opensearch/security/identity/ContextProvidingPluginSubjectTests.java @@ -25,7 +25,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.opensearch.security.support.ConfigConstants.OPENDISTRO_SECURITY_USER; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; public class ContextProvidingPluginSubjectTests { static class TestIdentityAwarePlugin extends Plugin implements IdentityAwarePlugin { @@ -57,39 +56,4 @@ public void testSecurityUserSubjectRunAs() throws Exception { SecurityUserTests.terminate(threadPool); } - - @Test - public void testPluginContextSwitcherRunAs() throws Exception { - final ThreadPool threadPool = new TestThreadPool(getClass().getName()); - - final Plugin testPlugin = new TestIdentityAwarePlugin(); - - final PluginContextSwitcher contextSwitcher = new PluginContextSwitcher(); - - final String pluginPrincipal = "plugin:" + testPlugin.getClass().getCanonicalName(); - - final User pluginUser = new User(pluginPrincipal); - - ContextProvidingPluginSubject subject = new ContextProvidingPluginSubject(threadPool, Settings.EMPTY, testPlugin); - - contextSwitcher.initialize(subject); - - assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); - - subject.runAs(() -> { - assertThat(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER), equalTo(pluginUser)); - return null; - }); - - assertNull(threadPool.getThreadContext().getTransient(OPENDISTRO_SECURITY_USER)); - - SecurityUserTests.terminate(threadPool); - } - - @Test - public void testPluginContextSwitcherUninitializedRunAs() throws Exception { - final PluginContextSwitcher contextSwitcher = new PluginContextSwitcher(); - - assertThrows(NullPointerException.class, () -> contextSwitcher.runAs(() -> null)); - } } From cb06896ba64e27e2613e61a489d14a6a5e2a586f Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 30 Dec 2024 15:19:13 -0500 Subject: [PATCH 11/14] Add unit tests Signed-off-by: Craig Perkins --- .../privileges/ActionPrivilegesTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java b/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java index 221c710cb3..1f60cf92d5 100644 --- a/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java +++ b/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java @@ -126,6 +126,28 @@ public void wildcard() throws Exception { ); } + @Test + public void wildcardByUsername() throws Exception { + SecurityDynamicConfiguration roles = SecurityDynamicConfiguration.empty(CType.ROLES); + + ActionPrivileges subject = new ActionPrivileges( + roles, + FlattenedActionGroups.EMPTY, + null, + Settings.EMPTY, + Map.of("plugin:org.opensearch.sample.SamplePlugin", Set.of("*")) + ); + + assertThat( + subject.hasClusterPrivilege(ctxByUsername("plugin:org.opensearch.sample.SamplePlugin"), "cluster:whatever"), + isAllowed() + ); + assertThat( + subject.hasClusterPrivilege(ctx("plugin:org.opensearch.other.OtherPlugin"), "cluster:whatever"), + isForbidden(missingPrivileges("cluster:whatever")) + ); + } + @Test public void explicit_wellKnown() throws Exception { SecurityDynamicConfiguration roles = SecurityDynamicConfiguration.fromYaml("non_explicit_role:\n" + // @@ -1031,4 +1053,19 @@ static PrivilegesEvaluationContext ctx(String... roles) { null ); } + + static PrivilegesEvaluationContext ctxByUsername(String username) { + User user = new User(username); + user.addAttributes(ImmutableMap.of("attrs.dept_no", "a11")); + return new PrivilegesEvaluationContext( + user, + ImmutableSet.of(), + null, + null, + null, + null, + new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY)), + null + ); + } } From 40c89ecef361a513f923795189006018038cfdfe Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 30 Dec 2024 16:00:39 -0500 Subject: [PATCH 12/14] Create RestMatchers Signed-off-by: Craig Perkins --- .../systemindex/SystemIndexTests.java | 16 ++--- .../test/framework/matcher/RestMatchers.java | 59 +++++++++++++++++++ 2 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java diff --git a/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java b/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java index 1bbc4f10ec..3435e6dbdc 100644 --- a/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java +++ b/src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java @@ -26,6 +26,7 @@ import org.opensearch.test.framework.cluster.LocalCluster; import org.opensearch.test.framework.cluster.TestRestClient; import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; +import org.opensearch.test.framework.matcher.RestMatchers; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -108,10 +109,10 @@ public void testPluginShouldNotBeAbleToIndexDocumentIntoSystemIndexRegisteredByO try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_2); - assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); assertThat( - response.getBody(), - containsString( + response, + RestMatchers.isForbidden( + "/error/root_cause/0/reason", "no permissions for [] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1" ) ); @@ -123,8 +124,7 @@ public void testPluginShouldBeAbleToCreateSystemIndexButUserShouldNotBeAbleToInd try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_1 + "?runAs=user"); - assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); - assertThat(response.getBody(), containsString("no permissions for [] and User [name=admin")); + assertThat(response, RestMatchers.isForbidden("/error/root_cause/0/reason", "no permissions for [] and User [name=admin")); } } @@ -133,10 +133,10 @@ public void testPluginShouldNotBeAbleToRunClusterActions() { try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { HttpResponse response = client.get("try-cluster-health/plugin"); - assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); assertThat( - response.getBody(), - containsString( + response, + RestMatchers.isForbidden( + "/error/root_cause/0/reason", "no permissions for [cluster:monitor/health] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1" ) ); diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java new file mode 100644 index 0000000000..05986213ad --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java @@ -0,0 +1,59 @@ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.DiagnosingMatcher; + +import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; + +public class RestMatchers { + + private RestMatchers() {} + + public static DiagnosingMatcher isForbidden(String jsonPointer, String patternString) { + return new DiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("Response has status 403 Forbidden with a JSON response that has the value ") + .appendValue(patternString) + .appendText(" at ") + .appendValue(jsonPointer); + } + + @Override + protected boolean matches(Object item, Description mismatchDescription) { + if (!(item instanceof HttpResponse)) { + mismatchDescription.appendValue(item).appendText(" is not a HttpResponse"); + return false; + } + + HttpResponse response = (HttpResponse) item; + + if (response.getStatusCode() != 403) { + mismatchDescription.appendText("Status is not 403 Forbidden: ").appendText("\n").appendValue(item); + return false; + } + + try { + String value = response.getTextFromJsonBody(jsonPointer); + + if (value == null) { + mismatchDescription.appendText("Could not find value at " + jsonPointer).appendText("\n").appendValue(item); + return false; + } + + if (value.contains(patternString)) { + return true; + } else { + mismatchDescription.appendText("Value at " + jsonPointer + " does not match pattern: " + patternString + "\n") + .appendValue(item); + return false; + } + } catch (Exception e) { + mismatchDescription.appendText("Parsing request body failed with " + e).appendText("\n").appendValue(item); + return false; + } + } + }; + } +} From cf1a4d90be3f9e164e65fa4c2427d3af605921ff Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 30 Dec 2024 16:23:53 -0500 Subject: [PATCH 13/14] Add license header Signed-off-by: Craig Perkins --- .../opensearch/test/framework/matcher/RestMatchers.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java index 05986213ad..b885074688 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/RestMatchers.java @@ -1,3 +1,12 @@ +/* + * 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.test.framework.matcher; import org.hamcrest.Description; From 6537f14eaf97385d1b23e5b25a4334ce32e8a5ff Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 31 Dec 2024 15:18:11 -0500 Subject: [PATCH 14/14] Check for injected roles Signed-off-by: Craig Perkins --- .../org/opensearch/security/filter/SecurityFilter.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/org/opensearch/security/filter/SecurityFilter.java b/src/main/java/org/opensearch/security/filter/SecurityFilter.java index 183e3743a3..6b23fb6b53 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityFilter.java @@ -193,15 +193,9 @@ private void ap attachSourceFieldContext(request); } final Set injectedRoles = rolesInjector.injectUserAndRoles(request, action, task, threadContext); - boolean enforcePrivilegesEvaluation = false; User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); if (user == null && (user = userInjector.getInjectedUser()) != null) { threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, user); - // since there is no support for TransportClient auth/auth in 2.0 anymore, usually we - // can skip any checks on transport in case of trusted requests. - // However, if another plugin injected a user in the ThreadContext, we still need - // to perform privileges checks. - enforcePrivilegesEvaluation = true; } final boolean userIsAdmin = isUserAdmin(user, adminDns); final boolean interClusterRequest = HeaderHelper.isInterClusterRequest(threadContext); @@ -319,6 +313,7 @@ private void ap if (Origin.LOCAL.toString().equals(threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN)) && (interClusterRequest || HeaderHelper.isDirectRequest(threadContext)) + && (injectedRoles == null) && (user == null)) { chain.proceed(task, action, request, listener);