From 9b4492bfb4999f21780557d216e6d821215c71c2 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 7 Aug 2024 15:42:20 -0400 Subject: [PATCH 01/20] WIP adding security resource sharing SPI and sample-extension-plugin Signed-off-by: Craig Perkins --- build.gradle | 1 + sample-extension-plugin/build.gradle | 188 ++++++++++++++ .../SampleExtensionPlugin.java | 92 +++++++ .../SampleExtensionRestHandler.java | 60 +++++ ...arch.security.spi.ResourceSharingExtension | 6 + .../sampleextension/ODFERestTestCase.java | 206 +++++++++++++++ .../SampleExtensionPluginIT.java | 38 +++ .../SecureRestClientBuilder.java | 236 ++++++++++++++++++ .../security/sampleextension/TrustStore.java | 71 ++++++ .../test/resources/security/esnode-key.pem | 28 +++ .../src/test/resources/security/esnode.pem | 25 ++ .../src/test/resources/security/kirk-key.pem | 28 +++ .../src/test/resources/security/kirk.pem | 27 ++ .../src/test/resources/security/root-ca.pem | 28 +++ .../src/test/resources/security/sample.pem | 25 ++ .../src/test/resources/security/test-kirk.jks | Bin 0 -> 3766 bytes settings.gradle | 6 + spi/build.gradle | 150 +++++++++++ .../spi/ResourceSharingExtension.java | 24 ++ 19 files changed, 1239 insertions(+) create mode 100644 sample-extension-plugin/build.gradle create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java create mode 100644 sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension create mode 100644 sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java create mode 100644 sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java create mode 100644 sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SecureRestClientBuilder.java create mode 100644 sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/TrustStore.java create mode 100644 sample-extension-plugin/src/test/resources/security/esnode-key.pem create mode 100644 sample-extension-plugin/src/test/resources/security/esnode.pem create mode 100644 sample-extension-plugin/src/test/resources/security/kirk-key.pem create mode 100644 sample-extension-plugin/src/test/resources/security/kirk.pem create mode 100644 sample-extension-plugin/src/test/resources/security/root-ca.pem create mode 100644 sample-extension-plugin/src/test/resources/security/sample.pem create mode 100644 sample-extension-plugin/src/test/resources/security/test-kirk.jks create mode 100644 spi/build.gradle create mode 100644 spi/src/main/java/org/opensearch/security/spi/ResourceSharingExtension.java diff --git a/build.gradle b/build.gradle index fa48dd6bbd..375370c6f9 100644 --- a/build.gradle +++ b/build.gradle @@ -569,6 +569,7 @@ tasks.integrationTest.finalizedBy(jacocoTestReport) // report is always generate check.dependsOn integrationTest dependencies { + implementation project(path: ":${rootProject.name}-spi", configuration: 'shadow') implementation "org.opensearch.plugin:transport-netty4-client:${opensearch_version}" implementation "org.opensearch.client:opensearch-rest-high-level-client:${opensearch_version}" implementation "org.apache.httpcomponents.client5:httpclient5-cache:${versions.httpclient5}" diff --git a/sample-extension-plugin/build.gradle b/sample-extension-plugin/build.gradle new file mode 100644 index 0000000000..7420afb315 --- /dev/null +++ b/sample-extension-plugin/build.gradle @@ -0,0 +1,188 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'opensearch.opensearchplugin' +apply plugin: 'opensearch.testclusters' +apply plugin: 'opensearch.java-rest-test' + +import org.opensearch.gradle.test.RestIntegTestTask +import org.opensearch.gradle.testclusters.StandaloneRestIntegTestTask +import org.apache.tools.ant.taskdefs.condition.Os + +import java.util.concurrent.Callable + + +opensearchplugin { + name 'opensearch-security-sample-extension' + description 'Sample plugin that extends OpenSearch Security Resource Sharing plugin' + classname 'org.opensearch.security.sampleextension.SampleExtensionPlugin' + // extendedPlugins = ['opensearch-security'] +} + +ext { + projectSubstitutions = [:] + licenseFile = rootProject.file('LICENSE.txt') + noticeFile = rootProject.file('NOTICE.txt') +} + +repositories { + mavenLocal() + mavenCentral() + maven { url "https://aws.oss.sonatype.org/content/repositories/snapshots" } +} + +dependencies { + implementation project(path: ":${rootProject.name}-spi", configuration: 'shadow') +} + +def es_tmp_dir = rootProject.file('build/private/es_tmp').absoluteFile +es_tmp_dir.mkdirs() + +File repo = file("$buildDir/testclusters/repo") +def _numNodes = findProperty('numNodes') as Integer ?: 1 + +licenseHeaders.enabled = true +validateNebulaPom.enabled = false +testingConventions.enabled = false +loggerUsageCheck.enabled = false + +javaRestTest.dependsOn(rootProject.assemble) +javaRestTest { + systemProperty 'tests.security.manager', 'false' +} +testClusters.javaRestTest { + testDistribution = 'INTEG_TEST' +} + +task integTest(type: RestIntegTestTask) { + description = "Run tests against a cluster" + testClassesDirs = sourceSets.test.output.classesDirs + classpath = sourceSets.test.runtimeClasspath +} +tasks.named("check").configure { dependsOn(integTest) } + +integTest { + if (project.hasProperty('excludeTests')) { + project.properties['excludeTests']?.replaceAll('\\s', '')?.split('[,;]')?.each { + exclude "${it}" + } + } + systemProperty 'tests.security.manager', 'false' + systemProperty 'java.io.tmpdir', es_tmp_dir.absolutePath + + systemProperty "https", System.getProperty("https") + systemProperty "user", System.getProperty("user") + systemProperty "password", System.getProperty("password") + // Tell the test JVM if the cluster JVM is running under a debugger so that tests can use longer timeouts for + // requests. The 'doFirst' delays reading the debug setting on the cluster till execution time. + doFirst { + // Tell the test JVM if the cluster JVM is running under a debugger so that tests can + // use longer timeouts for requests. + def isDebuggingCluster = getDebug() || System.getProperty("test.debug") != null + systemProperty 'cluster.debug', isDebuggingCluster + // Set number of nodes system property to be used in tests + systemProperty 'cluster.number_of_nodes', "${_numNodes}" + // There seems to be an issue when running multi node run or integ tasks with unicast_hosts + // not being written, the waitForAllConditions ensures it's written + getClusters().forEach { cluster -> + cluster.waitForAllConditions() + } + } + + // The -Dcluster.debug option makes the cluster debuggable; this makes the tests debuggable + if (System.getProperty("test.debug") != null) { + jvmArgs '-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=8000' + } + if (System.getProperty("tests.rest.bwcsuite") == null) { + filter { + excludeTestsMatching "org.opensearch.security.sampleextension.bwc.*IT" + } + } +} +project.getTasks().getByName('bundlePlugin').dependsOn(rootProject.tasks.getByName('build')) +Zip bundle = (Zip) project.getTasks().getByName("bundlePlugin"); +Zip rootBundle = (Zip) rootProject.getTasks().getByName("bundlePlugin"); +integTest.dependsOn(bundle) +integTest.getClusters().forEach{c -> { + c.plugin(rootProject.getObjects().fileProperty().value(rootBundle.getArchiveFile())) + c.plugin(project.getObjects().fileProperty().value(bundle.getArchiveFile())) +}} + +testClusters.integTest { + testDistribution = 'INTEG_TEST' + + // Cluster shrink exception thrown if we try to set numberOfNodes to 1, so only apply if > 1 + if (_numNodes > 1) numberOfNodes = _numNodes + // When running integration tests it doesn't forward the --debug-jvm to the cluster anymore + // i.e. we have to use a custom property to flag when we want to debug OpenSearch JVM + // since we also support multi node integration tests we increase debugPort per node + if (System.getProperty("cluster.debug") != null) { + def debugPort = 5005 + nodes.forEach { node -> + node.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=*:${debugPort}") + debugPort += 1 + } + } + setting 'path.repo', repo.absolutePath +} + +afterEvaluate { + testClusters.integTest.nodes.each { node -> + def plugins = node.plugins + def firstPlugin = plugins.get(0) + if (firstPlugin.provider == project.bundlePlugin.archiveFile) { + plugins.remove(0) + plugins.add(firstPlugin) + } + + node.extraConfigFile("kirk.pem", file("src/test/resources/security/kirk.pem")) + node.extraConfigFile("kirk-key.pem", file("src/test/resources/security/kirk-key.pem")) + node.extraConfigFile("esnode.pem", file("src/test/resources/security/esnode.pem")) + node.extraConfigFile("esnode-key.pem", file("src/test/resources/security/esnode-key.pem")) + node.extraConfigFile("root-ca.pem", file("src/test/resources/security/root-ca.pem")) + node.setting("plugins.security.ssl.transport.pemcert_filepath", "esnode.pem") + node.setting("plugins.security.ssl.transport.pemkey_filepath", "esnode-key.pem") + node.setting("plugins.security.ssl.transport.pemtrustedcas_filepath", "root-ca.pem") + node.setting("plugins.security.ssl.transport.enforce_hostname_verification", "false") + node.setting("plugins.security.ssl.http.enabled", "true") + node.setting("plugins.security.ssl.http.pemcert_filepath", "esnode.pem") + node.setting("plugins.security.ssl.http.pemkey_filepath", "esnode-key.pem") + node.setting("plugins.security.ssl.http.pemtrustedcas_filepath", "root-ca.pem") + node.setting("plugins.security.allow_unsafe_democertificates", "true") + node.setting("plugins.security.allow_default_init_securityindex", "true") + node.setting("plugins.security.authcz.admin_dn", "\n - CN=kirk,OU=client,O=client,L=test,C=de") + node.setting("plugins.security.audit.type", "internal_opensearch") + node.setting("plugins.security.enable_snapshot_restore_privilege", "true") + node.setting("plugins.security.check_snapshot_restore_write_privileges", "true") + node.setting("plugins.security.restapi.roles_enabled", "[\"all_access\", \"security_rest_api_access\"]") + } +} + +run { + doFirst { + // There seems to be an issue when running multi node run or integ tasks with unicast_hosts + // not being written, the waitForAllConditions ensures it's written + getClusters().forEach { cluster -> + cluster.waitForAllConditions() + } + } + useCluster testClusters.integTest +} + +// As of ES 7.7 the sample-extension-plugin is being added to the list of plugins for the testCluster during build before +// the security plugin is causing build failures. +// The security zip is added explicitly above but the sample-extension-plugin is added implicitly at some time during evaluation. +// Will need to do a deep dive to find out exactly what task adds the sample-extension-plugin and add security there but a temporary hack is to +// reorder the plugins list after evaluation but prior to task execution when the plugins are installed. +afterEvaluate { + testClusters.javaRestTest.nodes.each { node -> + def nodePlugins = node.plugins + def firstPlugin = nodePlugins.get(0) + if (firstPlugin.provider == project.bundlePlugin.archiveFile) { + nodePlugins.remove(0) + nodePlugins.add(firstPlugin) + } + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java new file mode 100644 index 0000000000..3fd552bad9 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java @@ -0,0 +1,92 @@ +/* + * 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.sampleextension; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +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.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.env.Environment; +import org.opensearch.env.NodeEnvironment; +import org.opensearch.plugins.ActionPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestHandler; +import org.opensearch.script.ScriptService; +import org.opensearch.security.spi.ResourceSharingExtension; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.watcher.ResourceWatcherService; + +/** + * Sample Security Resource Sharing extension plugin. + * + * It use ".sample_extension_resources" index to manage its resources, and exposes a REST API + * endpoint using {@link SampleExtensionRestHandler}. + * + */ +public class SampleExtensionPlugin extends Plugin implements ActionPlugin, ResourceSharingExtension { + private static final Logger log = LogManager.getLogger(SampleExtensionPlugin.class); + + static final String RESOURCE_INDEX_NAME = ".sample_extension_resources"; + + @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 + ) { + return Collections.emptyList(); + } + + @Override + public String getResourceType() { + return "sample_resource"; + } + + @Override + public String getResourceIndex() { + return RESOURCE_INDEX_NAME; + } + + @Override + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { + return Collections.singletonList(new SampleExtensionRestHandler()); + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java new file mode 100644 index 0000000000..f2906c6eca --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java @@ -0,0 +1,60 @@ +/* + * 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.sampleextension; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.opensearch.client.node.NodeClient; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestRequest; + +/** + * A sample rest handler that supports schedule and deschedule job operation + * + * Users need to provide "id", "index", "job_name", and "interval" parameter to schedule + * a job. e.g. + * {@code + * POST /_plugins/scheduler_sample/watch?id=dashboards-job-id&job_name=watch dashboards index&index=.opensearch_dashboards_1&interval=1 + * } + * + * creates a job with id "dashboards-job-id" and job name "watch dashboards index", + * which logs ".opensearch_dashboards_1" index's shards info every 1 minute + * + * Users can remove that job by calling + * {@code DELETE /_plugins/scheduler_sample/watch?id=dashboards-job-id} + */ +public class SampleExtensionRestHandler extends BaseRestHandler { + public static final String LIST_RESOURCE_URI = "/_plugins/resource_sample/resource"; + + @Override + public String getName() { + return "Sample Security Resource Sharing extension handler"; + } + + @Override + public List routes() { + return Collections.unmodifiableList(Arrays.asList(new Route(RestRequest.Method.GET, LIST_RESOURCE_URI))); + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + if (request.method().equals(RestRequest.Method.GET)) { + return restChannel -> { restChannel.sendResponse(new BytesRestResponse(RestStatus.OK, "List Resources called")); }; + } else { + return restChannel -> { + restChannel.sendResponse(new BytesRestResponse(RestStatus.METHOD_NOT_ALLOWED, request.method() + " is not allowed.")); + }; + } + } +} diff --git a/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension b/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension new file mode 100644 index 0000000000..e32f06ee56 --- /dev/null +++ b/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension @@ -0,0 +1,6 @@ +# +# Copyright OpenSearch Contributors +# SPDX-License-Identifier: Apache-2.0 +# + +org.opensearch.security.sampleextension.SampleExtensionPlugin \ No newline at end of file diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java new file mode 100644 index 0000000000..0dda33b230 --- /dev/null +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java @@ -0,0 +1,206 @@ +/* + * 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.sampleextension; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import javax.net.ssl.SSLEngine; + +import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; +import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; +import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; +import org.apache.hc.core5.function.Factory; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.message.BasicHeader; +import org.apache.hc.core5.http.nio.ssl.TlsStrategy; +import org.apache.hc.core5.reactor.ssl.TlsDetails; +import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.apache.hc.core5.util.Timeout; +import org.junit.After; + +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestClientBuilder; +import org.opensearch.client.WarningFailureException; +import org.opensearch.common.io.PathUtils; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.test.rest.OpenSearchRestTestCase; + +public abstract class ODFERestTestCase extends OpenSearchRestTestCase { + + protected boolean isHttps() { + boolean isHttps = Optional.ofNullable(System.getProperty("https")).map("true"::equalsIgnoreCase).orElse(false); + if (isHttps) { + // currently only external cluster is supported for security enabled testing + if (!Optional.ofNullable(System.getProperty("tests.rest.cluster")).isPresent()) { + throw new RuntimeException("cluster url should be provided for security enabled testing"); + } + } + + return isHttps; + } + + @Override + protected String getProtocol() { + return isHttps() ? "https" : "http"; + } + + @Override + protected Settings restAdminSettings() { + return Settings.builder() + .put("http.port", 9200) + .put("plugins.security.ssl.http.enabled", isHttps()) + .put("plugins.security.ssl.http.pemcert_filepath", "sample.pem") + .put("plugins.security.ssl.http.keystore_filepath", "test-kirk.jks") + .put("plugins.security.ssl.http.keystore_password", "changeit") + .build(); + // return Settings.builder().put("strictDeprecationMode", false).put("http.port", 9200).build(); + } + + @Override + protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException { + boolean strictDeprecationMode = settings.getAsBoolean("strictDeprecationMode", true); + RestClientBuilder builder = RestClient.builder(hosts); + if (isHttps()) { + String keystore = settings.get("plugins.security.ssl.http.keystore_filepath"); + if (Objects.nonNull(keystore)) { + URI uri = null; + try { + uri = this.getClass().getClassLoader().getResource("security/sample.pem").toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + Path configPath = PathUtils.get(uri).getParent().toAbsolutePath(); + return new SecureRestClientBuilder(settings, configPath, hosts).build(); + } else { + configureHttpsClient(builder, settings); + builder.setStrictDeprecationMode(strictDeprecationMode); + return builder.build(); + } + } else { + configureClient(builder, settings); + builder.setStrictDeprecationMode(strictDeprecationMode); + return builder.build(); + } + + } + + @SuppressWarnings("unchecked") + @After + protected void wipeAllODFEIndices() throws IOException { + Response response = adminClient().performRequest(new Request("GET", "/_cat/indices?format=json&expand_wildcards=all")); + MediaType mediaType = MediaType.fromMediaType(response.getEntity().getContentType()); + try ( + XContentParser parser = mediaType.xContent() + .createParser( + NamedXContentRegistry.EMPTY, + DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + response.getEntity().getContent() + ) + ) { + XContentParser.Token token = parser.nextToken(); + List> parserList = null; + if (token == XContentParser.Token.START_ARRAY) { + parserList = parser.listOrderedMap().stream().map(obj -> (Map) obj).collect(Collectors.toList()); + } else { + parserList = Collections.singletonList(parser.mapOrdered()); + } + + for (Map index : parserList) { + String indexName = (String) index.get("index"); + if (indexName != null && !".opendistro_security".equals(indexName)) { + try { + adminClient().performRequest(new Request("DELETE", "/" + indexName)); + } catch (WarningFailureException ignore) {} + } + } + } + } + + protected static void configureHttpsClient(RestClientBuilder builder, Settings settings) throws IOException { + Map headers = new HashMap<>(ThreadContext.buildDefaultHeaders(settings)); + String userName = Optional.ofNullable(System.getProperty("user")).orElseThrow(() -> new RuntimeException("user name is missing")); + String password = Optional.ofNullable(System.getProperty("password")) + .orElseThrow(() -> new RuntimeException("password is missing")); + headers.put( + "Authorization", + "Basic " + Base64.getEncoder().encodeToString((userName + ":" + password).getBytes(StandardCharsets.UTF_8)) + ); + Header[] defaultHeaders = new Header[headers.size()]; + int i = 0; + for (Map.Entry entry : headers.entrySet()) { + defaultHeaders[i++] = new BasicHeader(entry.getKey(), entry.getValue()); + } + builder.setDefaultHeaders(defaultHeaders); + builder.setHttpClientConfigCallback(httpClientBuilder -> { + try { + final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create() + .setSslContext(SSLContextBuilder.create().loadTrustMaterial(null, (chains, authType) -> true).build()) + // disable the certificate since our testing cluster just uses the default security configuration + .setHostnameVerifier(NoopHostnameVerifier.INSTANCE) + // See please https://issues.apache.org/jira/browse/HTTPCLIENT-2219 + .setTlsDetailsFactory(new Factory() { + @Override + public TlsDetails create(final SSLEngine sslEngine) { + return new TlsDetails(sslEngine.getSession(), sslEngine.getApplicationProtocol()); + } + }) + .build(); + + final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create() + .setTlsStrategy(tlsStrategy) + .build(); + + return httpClientBuilder.setConnectionManager(connectionManager); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + final String socketTimeoutString = settings.get(CLIENT_SOCKET_TIMEOUT); + final TimeValue socketTimeout = TimeValue.parseTimeValue( + socketTimeoutString == null ? "60s" : socketTimeoutString, + CLIENT_SOCKET_TIMEOUT + ); + builder.setRequestConfigCallback( + conf -> conf.setResponseTimeout(Timeout.ofMilliseconds(Math.toIntExact(socketTimeout.getMillis()))) + ); + if (settings.hasValue(CLIENT_PATH_PREFIX)) { + builder.setPathPrefix(settings.get(CLIENT_PATH_PREFIX)); + } + } + + /** + * wipeAllIndices won't work since it cannot delete security index. Use wipeAllODFEIndices instead. + */ + @Override + protected boolean preserveIndicesUponCompletion() { + return true; + } +} diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java new file mode 100644 index 0000000000..ad73e5f695 --- /dev/null +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java @@ -0,0 +1,38 @@ +/* + * 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.sampleextension; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.junit.Assert; + +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.NamedXContentRegistry; + +public class SampleExtensionPluginIT extends ODFERestTestCase { + + @SuppressWarnings("unchecked") + public void testPluginsAreInstalled() throws IOException { + Request request = new Request("GET", "/_cat/plugins?s=component&h=name,component,version,description&format=json"); + Response response = client().performRequest(request); + List pluginsList = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + response.getEntity().getContent() + ).list(); + Assert.assertTrue( + pluginsList.stream().map(o -> (Map) o).anyMatch(plugin -> plugin.get("component").equals("opensearch-security")) + ); + } +} diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SecureRestClientBuilder.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SecureRestClientBuilder.java new file mode 100644 index 0000000000..6a0a6d33e1 --- /dev/null +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SecureRestClientBuilder.java @@ -0,0 +1,236 @@ +/* + * 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.sampleextension; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.Arrays; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + +import org.apache.hc.client5.http.auth.AuthScope; +import org.apache.hc.client5.http.auth.CredentialsProvider; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; +import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; +import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; +import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; +import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder; +import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy; +import org.apache.hc.core5.function.Factory; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.nio.ssl.TlsStrategy; +import org.apache.hc.core5.reactor.ssl.TlsDetails; +import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.apache.hc.core5.util.Timeout; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.OpenSearchException; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestClientBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; + +/** + * Provides builder to create low-level and high-level REST client to make calls to OpenSearch. + * + * Sample usage: + * SecureRestClientBuilder builder = new SecureRestClientBuilder(settings).build() + * RestClient restClient = builder.build(); + * + * Other usage: + * RestClient restClient = new SecureRestClientBuilder("localhost", 9200, false) + * .setUserPassword("admin", "myStrongPassword123") + * .setTrustCerts(trustStorePath) + * .build(); + * + * + * If https is enabled, creates RestClientBuilder using self-signed certificates or passed pem + * as trusted. + * + * If https is not enabled, creates a http based client. + */ +public class SecureRestClientBuilder { + + private final boolean httpSSLEnabled; + private final String user; + private final String passwd; + private final ArrayList hosts = new ArrayList<>(); + + private final Path configPath; + private final Settings settings; + + private int defaultConnectTimeOutMSecs = 5000; + private int defaultSoTimeoutMSecs = 10000; + private int defaultConnRequestTimeoutMSecs = 3 * 60 * 1000; /* 3 mins */ + private int defaultMaxConnPerRoute = RestClientBuilder.DEFAULT_MAX_CONN_PER_ROUTE; + private int defaultMaxConnTotal = RestClientBuilder.DEFAULT_MAX_CONN_TOTAL; + + private static final Logger log = LogManager.getLogger(SecureRestClientBuilder.class); + + public SecureRestClientBuilder(Settings settings, Path configPath, HttpHost[] httpHosts) { + this.httpSSLEnabled = settings.getAsBoolean("plugins.security.ssl.http.enabled", false); + this.settings = settings; + this.configPath = configPath; + this.user = null; + this.passwd = null; + hosts.addAll(Arrays.asList(httpHosts)); + } + + /** + * Creates a low-level Rest client. + * @return + * @throws IOException + */ + public RestClient build() throws IOException { + return createRestClientBuilder().build(); + } + + private RestClientBuilder createRestClientBuilder() throws IOException { + RestClientBuilder builder = RestClient.builder(hosts.toArray(new HttpHost[hosts.size()])); + + builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() { + @Override + public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) { + return requestConfigBuilder.setConnectTimeout(Timeout.ofMilliseconds(defaultConnectTimeOutMSecs)) + .setResponseTimeout(Timeout.ofMilliseconds(defaultSoTimeoutMSecs)) + .setConnectionRequestTimeout(Timeout.ofMilliseconds(defaultConnRequestTimeoutMSecs)); + } + }); + + final SSLContext sslContext; + try { + sslContext = createSSLContext(); + } catch (GeneralSecurityException | IOException ex) { + throw new IOException(ex); + } + final CredentialsProvider credentialsProvider = createCredsProvider(); + builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { + if (sslContext != null) { + TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create() + .setSslContext(sslContext) + // See please https://issues.apache.org/jira/browse/HTTPCLIENT-2219 + .setTlsDetailsFactory(new Factory() { + @Override + public TlsDetails create(final SSLEngine sslEngine) { + return new TlsDetails(sslEngine.getSession(), sslEngine.getApplicationProtocol()); + } + }) + .build(); + PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create() + .setTlsStrategy(tlsStrategy) + .setMaxConnPerRoute(defaultMaxConnPerRoute) + .setMaxConnTotal(defaultMaxConnTotal) + .build(); + httpClientBuilder.setConnectionManager(connectionManager); + } + if (credentialsProvider != null) { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + return httpClientBuilder; + } + }); + return builder; + } + + private SSLContext createSSLContext() throws IOException, GeneralSecurityException { + SSLContextBuilder builder = new SSLContextBuilder(); + if (httpSSLEnabled) { + // Handle trust store + String pemFile = getTrustPem(); + if (Strings.isNullOrEmpty(pemFile)) { + builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); + } else { + String pem = resolve(pemFile, configPath); + KeyStore trustStore = new TrustStore(pem).create(); + builder.loadTrustMaterial(trustStore, null); + } + + // Handle key store. + KeyStore keyStore = getKeyStore(); + if (keyStore != null) { + builder.loadKeyMaterial(keyStore, getKeystorePasswd().toCharArray()); + } + + } + return builder.build(); + } + + private CredentialsProvider createCredsProvider() { + if (Strings.isNullOrEmpty(user) || Strings.isNullOrEmpty(passwd)) return null; + + final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(null, -1), new UsernamePasswordCredentials(user, passwd.toCharArray())); + return credentialsProvider; + } + + private String resolve(final String originalFile, final Path configPath) { + String path = null; + if (originalFile != null && originalFile.length() > 0) { + path = configPath.resolve(originalFile).toAbsolutePath().toString(); + log.debug("Resolved {} to {} against {}", originalFile, path, configPath.toAbsolutePath().toString()); + } + + if (path == null || path.length() == 0) { + throw new OpenSearchException("Empty file path for " + originalFile); + } + + if (Files.isDirectory(Paths.get(path), LinkOption.NOFOLLOW_LINKS)) { + throw new OpenSearchException("Is a directory: " + path + " Expected a file for " + originalFile); + } + + if (!Files.isReadable(Paths.get(path))) { + throw new OpenSearchException( + "Unable to read " + + path + + " (" + + Paths.get(path) + + "). Please make sure this files exists and is readable regarding to permissions. Property: " + + originalFile + ); + } + if ("".equals(path)) { + path = null; + } + return path; + } + + private String getTrustPem() { + return settings.get("plugins.security.ssl.http.pemcert_filepath", null); + } + + private String getKeystorePasswd() { + return settings.get("plugins.security.ssl.http.keystore_password", null); + } + + private KeyStore getKeyStore() throws IOException, GeneralSecurityException { + KeyStore keyStore = KeyStore.getInstance("jks"); + String keyStoreFile = settings.get("plugins.security.ssl.http.keystore_filepath", null); + String passwd = settings.get("plugins.security.ssl.http.keystore_password", null); + if (Strings.isNullOrEmpty(keyStoreFile) || Strings.isNullOrEmpty(passwd)) { + return null; + } + String keyStorePath = resolve(keyStoreFile, configPath); + try (InputStream is = Files.newInputStream(Paths.get(keyStorePath))) { + keyStore.load(is, passwd.toCharArray()); + } + return keyStore; + } +} diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/TrustStore.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/TrustStore.java new file mode 100644 index 0000000000..2bee6291d1 --- /dev/null +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/TrustStore.java @@ -0,0 +1,71 @@ +/* + * 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.sampleextension; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; + +/** + * Helper class to read raw pem files to keystore. + */ +public class TrustStore { + + private final String effectiveKeyAlias = "al"; + private final String storeType = "JKS"; + private final String certType = "X.509"; + private final String cert; + + public TrustStore(final String file) { + cert = file; + } + + public KeyStore create() throws IOException, GeneralSecurityException { + X509Certificate[] trustCerts = loadCertificatesFromFile(cert); + return toTrustStore(effectiveKeyAlias, trustCerts); + } + + private X509Certificate[] loadCertificatesFromFile(String file) throws IOException, GeneralSecurityException { + if (file == null) { + return null; + } + CertificateFactory fact = CertificateFactory.getInstance(certType); + try (FileInputStream is = new FileInputStream(file)) { + Collection certs = fact.generateCertificates(is); + X509Certificate[] x509Certs = new X509Certificate[certs.size()]; + int i = 0; + for (Certificate cert : certs) { + x509Certs[i++] = (X509Certificate) cert; + } + return x509Certs; + } + } + + private KeyStore toTrustStore(final String trustCertificatesAliasPrefix, final X509Certificate[] trustCertificates) throws IOException, + GeneralSecurityException { + if (trustCertificates == null) { + return null; + } + KeyStore ks = KeyStore.getInstance(storeType); + ks.load(null); + + if (trustCertificates != null) { + for (int i = 0; i < trustCertificates.length; i++) { + X509Certificate x509Certificate = trustCertificates[i]; + ks.setCertificateEntry(trustCertificatesAliasPrefix + "_" + i, x509Certificate); + } + } + return ks; + } +} diff --git a/sample-extension-plugin/src/test/resources/security/esnode-key.pem b/sample-extension-plugin/src/test/resources/security/esnode-key.pem new file mode 100644 index 0000000000..e90562be43 --- /dev/null +++ b/sample-extension-plugin/src/test/resources/security/esnode-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCm93kXteDQHMAv +bUPNPW5pyRHKDD42XGWSgq0k1D29C/UdyL21HLzTJa49ZU2ldIkSKs9JqbkHdyK0 +o8MO6L8dotLoYbxDWbJFW8bp1w6tDTU0HGkn47XVu3EwbfrTENg3jFu+Oem6a/50 +1SzITzJWtS0cn2dIFOBimTVpT/4Zv5qrXA6Cp4biOmoTYWhi/qQl8d0IaADiqoZ1 +MvZbZ6x76qTrRAbg+UWkpTEXoH1xTc8ndibR7+HP6OTqCKvo1NhE8uP4pY+fWd6b +6l+KLo3IKpfTbAIJXIO+M67FLtWKtttDao94B069skzKk6FPgW/OZh6PRCD0oxOa +vV+ld2SjAgMBAAECggEAQK1+uAOZeaSZggW2jQut+MaN4JHLi61RH2cFgU3COLgo +FIiNjFn8f2KKU3gpkt1It8PjlmprpYut4wHI7r6UQfuv7ZrmncRiPWHm9PB82+ZQ +5MXYqj4YUxoQJ62Cyz4sM6BobZDrjG6HHGTzuwiKvHHkbsEE9jQ4E5m7yfbVvM0O +zvwrSOM1tkZihKSTpR0j2+taji914tjBssbn12TMZQL5ItGnhR3luY8mEwT9MNkZ +xg0VcREoAH+pu9FE0vPUgLVzhJ3be7qZTTSRqv08bmW+y1plu80GbppePcgYhEow +dlW4l6XPJaHVSn1lSFHE6QAx6sqiAnBz0NoTPIaLyQKBgQDZqDOlhCRciMRicSXn +7yid9rhEmdMkySJHTVFOidFWwlBcp0fGxxn8UNSBcXdSy7GLlUtH41W9PWl8tp9U +hQiiXORxOJ7ZcB80uNKXF01hpPj2DpFPWyHFxpDkWiTAYpZl68rOlYujxZUjJIej +VvcykBC2BlEOG9uZv2kxcqLyJwKBgQDEYULTxaTuLIa17wU3nAhaainKB3vHxw9B +Ksy5p3ND43UNEKkQm7K/WENx0q47TA1mKD9i+BhaLod98mu0YZ+BCUNgWKcBHK8c +uXpauvM/pLhFLXZ2jvEJVpFY3J79FSRK8bwE9RgKfVKMMgEk4zOyZowS8WScOqiy +hnQn1vKTJQKBgElhYuAnl9a2qXcC7KOwRsJS3rcKIVxijzL4xzOyVShp5IwIPbOv +hnxBiBOH/JGmaNpFYBcBdvORE9JfA4KMQ2fx53agfzWRjoPI1/7mdUk5RFI4gRb/ +A3jZRBoopgFSe6ArCbnyQxzYzToG48/Wzwp19ZxYrtUR4UyJct6f5n27AoGBAJDh +KIpQQDOvCdtjcbfrF4aM2DPCfaGPzENJriwxy6oEPzDaX8Bu/dqI5Ykt43i/zQrX +GpyLaHvv4+oZVTiI5UIvcVO9U8hQPyiz9f7F+fu0LHZs6f7hyhYXlbe3XFxeop3f +5dTKdWgXuTTRF2L9dABkA2deS9mutRKwezWBMQk5AoGBALPtX0FrT1zIosibmlud +tu49A/0KZu4PBjrFMYTSEWGNJez3Fb2VsJwylVl6HivwbP61FhlYfyksCzQQFU71 ++x7Nmybp7PmpEBECr3deoZKQ/acNHn0iwb0It+YqV5+TquQebqgwK6WCLsMuiYKT +bg/ch9Rhxbq22yrVgWHh6epp +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/sample-extension-plugin/src/test/resources/security/esnode.pem b/sample-extension-plugin/src/test/resources/security/esnode.pem new file mode 100644 index 0000000000..44101f0b37 --- /dev/null +++ b/sample-extension-plugin/src/test/resources/security/esnode.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIUaYSlET3nzsotWTrWueVPPh10yLYwDQYJKoZIhvcNAQEL +BQAwgY8xEzARBgoJkiaJk/IsZAEZFgNjb20xFzAVBgoJkiaJk/IsZAEZFgdleGFt +cGxlMRkwFwYDVQQKDBBFeGFtcGxlIENvbSBJbmMuMSEwHwYDVQQLDBhFeGFtcGxl +IENvbSBJbmMuIFJvb3QgQ0ExITAfBgNVBAMMGEV4YW1wbGUgQ29tIEluYy4gUm9v +dCBDQTAeFw0yNDAyMjAxNzAzMjVaFw0zNDAyMTcxNzAzMjVaMFcxCzAJBgNVBAYT +AmRlMQ0wCwYDVQQHDAR0ZXN0MQ0wCwYDVQQKDARub2RlMQ0wCwYDVQQLDARub2Rl +MRswGQYDVQQDDBJub2RlLTAuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCm93kXteDQHMAvbUPNPW5pyRHKDD42XGWSgq0k1D29C/Ud +yL21HLzTJa49ZU2ldIkSKs9JqbkHdyK0o8MO6L8dotLoYbxDWbJFW8bp1w6tDTU0 +HGkn47XVu3EwbfrTENg3jFu+Oem6a/501SzITzJWtS0cn2dIFOBimTVpT/4Zv5qr +XA6Cp4biOmoTYWhi/qQl8d0IaADiqoZ1MvZbZ6x76qTrRAbg+UWkpTEXoH1xTc8n +dibR7+HP6OTqCKvo1NhE8uP4pY+fWd6b6l+KLo3IKpfTbAIJXIO+M67FLtWKtttD +ao94B069skzKk6FPgW/OZh6PRCD0oxOavV+ld2SjAgMBAAGjgcYwgcMwRwYDVR0R +BEAwPogFKgMEBQWCEm5vZGUtMC5leGFtcGxlLmNvbYIJbG9jYWxob3N0hxAAAAAA +AAAAAAAAAAAAAAABhwR/AAABMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEF +BQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU0/qDQaY10jIo +wCjLUpz/HfQXyt8wHwYDVR0jBBgwFoAUF4ffoFrrZhKn1dD4uhJFPLcrAJwwDQYJ +KoZIhvcNAQELBQADggEBAGbij5WyF0dKhQodQfTiFDb73ygU6IyeJkFSnxF67gDz +pQJZKFvXuVBa3cGP5e7Qp3TK50N+blXGH0xXeIV9lXeYUk4hVfBlp9LclZGX8tGi +7Xa2enMvIt5q/Yg3Hh755ZxnDYxCoGkNOXUmnMusKstE0YzvZ5Gv6fcRKFBUgZLh +hUBqIEAYly1EqH/y45APiRt3Nor1yF6zEI4TnL0yNrHw6LyQkUNCHIGMJLfnJQ9L +camMGIXOx60kXNMTigF9oXXwixWAnDM9y3QT8QXA7hej/4zkbO+vIeV/7lGUdkyg +PAi92EvyxmsliEMyMR0VINl8emyobvfwa7oMeWMR+hg= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/sample-extension-plugin/src/test/resources/security/kirk-key.pem b/sample-extension-plugin/src/test/resources/security/kirk-key.pem new file mode 100644 index 0000000000..1949c26139 --- /dev/null +++ b/sample-extension-plugin/src/test/resources/security/kirk-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCVXDgEJQorgfXp +gpY0TgF55bD2xuzxN5Dc9rDfgWxrsOvOloMpd7k6FR71bKWjJi1KptSmM/cDElky +AWYKSfYWGiGxsQ+EQW+6kwCfEOHXQldn+0+JcWqP+osSPjtJfwRvRN5kRqP69MPo +7U0N2kdqenqMWjmG1chDGLRSOEGU5HIBiDxsZtOcvMaJ8b1eaW0lvS+6gFQ80AvB +GBkDDCOHHLtDXBylrZk2CQP8AzxNicIZ4B8G3CG3OHA8+nBtEtxZoIihrrkqlMt+ +b/5N8u8zB0Encew0kdrc4R/2wS//ahr6U+9Siq8T7WsUtGwKj3BJClg6OyDJRhlu +y2gFnxoPAgMBAAECggEAP5TOycDkx+megAWVoHV2fmgvgZXkBrlzQwUG/VZQi7V4 +ZGzBMBVltdqI38wc5MtbK3TCgHANnnKgor9iq02Z4wXDwytPIiti/ycV9CDRKvv0 +TnD2hllQFjN/IUh5n4thHWbRTxmdM7cfcNgX3aZGkYbLBVVhOMtn4VwyYu/Mxy8j +xClZT2xKOHkxqwmWPmdDTbAeZIbSv7RkIGfrKuQyUGUaWhrPslvYzFkYZ0umaDgQ +OAthZew5Bz3OfUGOMPLH61SVPuJZh9zN1hTWOvT65WFWfsPd2yStI+WD/5PU1Doo +1RyeHJO7s3ug8JPbtNJmaJwHe9nXBb/HXFdqb976yQKBgQDNYhpu+MYSYupaYqjs +9YFmHQNKpNZqgZ4ceRFZ6cMJoqpI5dpEMqToFH7tpor72Lturct2U9nc2WR0HeEs +/6tiptyMPTFEiMFb1opQlXF2ae7LeJllntDGN0Q6vxKnQV+7VMcXA0Y8F7tvGDy3 +qJu5lfvB1mNM2I6y/eMxjBuQhwKBgQC6K41DXMFro0UnoO879pOQYMydCErJRmjG +/tZSy3Wj4KA/QJsDSViwGfvdPuHZRaG9WtxdL6kn0w1exM9Rb0bBKl36lvi7o7xv +M+Lw9eyXMkww8/F5d7YYH77gIhGo+RITkKI3+5BxeBaUnrGvmHrpmpgRXWmINqr0 +0jsnN3u0OQKBgCf45vIgItSjQb8zonLz2SpZjTFy4XQ7I92gxnq8X0Q5z3B+o7tQ +K/4rNwTju/sGFHyXAJlX+nfcK4vZ4OBUJjP+C8CTjEotX4yTNbo3S6zjMyGQqDI5 +9aIOUY4pb+TzeUFJX7If5gR+DfGyQubvvtcg1K3GHu9u2l8FwLj87sRzAoGAflQF +RHuRiG+/AngTPnZAhc0Zq0kwLkpH2Rid6IrFZhGLy8AUL/O6aa0IGoaMDLpSWUJp +nBY2S57MSM11/MVslrEgGmYNnI4r1K25xlaqV6K6ztEJv6n69327MS4NG8L/gCU5 +3pEm38hkUi8pVYU7in7rx4TCkrq94OkzWJYurAkCgYATQCL/rJLQAlJIGulp8s6h +mQGwy8vIqMjAdHGLrCS35sVYBXG13knS52LJHvbVee39AbD5/LlWvjJGlQMzCLrw +F7oILW5kXxhb8S73GWcuMbuQMFVHFONbZAZgn+C9FW4l7XyRdkrbR1MRZ2km8YMs +/AHmo368d4PSNRMMzLHw8Q== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/sample-extension-plugin/src/test/resources/security/kirk.pem b/sample-extension-plugin/src/test/resources/security/kirk.pem new file mode 100644 index 0000000000..36b7e19a75 --- /dev/null +++ b/sample-extension-plugin/src/test/resources/security/kirk.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEmDCCA4CgAwIBAgIUaYSlET3nzsotWTrWueVPPh10yLcwDQYJKoZIhvcNAQEL +BQAwgY8xEzARBgoJkiaJk/IsZAEZFgNjb20xFzAVBgoJkiaJk/IsZAEZFgdleGFt +cGxlMRkwFwYDVQQKDBBFeGFtcGxlIENvbSBJbmMuMSEwHwYDVQQLDBhFeGFtcGxl +IENvbSBJbmMuIFJvb3QgQ0ExITAfBgNVBAMMGEV4YW1wbGUgQ29tIEluYy4gUm9v +dCBDQTAeFw0yNDAyMjAxNzA0MjRaFw0zNDAyMTcxNzA0MjRaME0xCzAJBgNVBAYT +AmRlMQ0wCwYDVQQHDAR0ZXN0MQ8wDQYDVQQKDAZjbGllbnQxDzANBgNVBAsMBmNs +aWVudDENMAsGA1UEAwwEa2lyazCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAJVcOAQlCiuB9emCljROAXnlsPbG7PE3kNz2sN+BbGuw686Wgyl3uToVHvVs +paMmLUqm1KYz9wMSWTIBZgpJ9hYaIbGxD4RBb7qTAJ8Q4ddCV2f7T4lxao/6ixI+ +O0l/BG9E3mRGo/r0w+jtTQ3aR2p6eoxaOYbVyEMYtFI4QZTkcgGIPGxm05y8xonx +vV5pbSW9L7qAVDzQC8EYGQMMI4ccu0NcHKWtmTYJA/wDPE2JwhngHwbcIbc4cDz6 +cG0S3FmgiKGuuSqUy35v/k3y7zMHQSdx7DSR2tzhH/bBL/9qGvpT71KKrxPtaxS0 +bAqPcEkKWDo7IMlGGW7LaAWfGg8CAwEAAaOCASswggEnMAwGA1UdEwEB/wQCMAAw +DgYDVR0PAQH/BAQDAgXgMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1UdDgQW +BBSjMS8tgguX/V7KSGLoGg7K6XMzIDCBzwYDVR0jBIHHMIHEgBQXh9+gWutmEqfV +0Pi6EkU8tysAnKGBlaSBkjCBjzETMBEGCgmSJomT8ixkARkWA2NvbTEXMBUGCgmS +JomT8ixkARkWB2V4YW1wbGUxGTAXBgNVBAoMEEV4YW1wbGUgQ29tIEluYy4xITAf +BgNVBAsMGEV4YW1wbGUgQ29tIEluYy4gUm9vdCBDQTEhMB8GA1UEAwwYRXhhbXBs +ZSBDb20gSW5jLiBSb290IENBghQNZAmZZn3EFOxBR4630XlhI+mo4jANBgkqhkiG +9w0BAQsFAAOCAQEACEUPPE66/Ot3vZqRGpjDjPHAdtOq+ebaglQhvYcnDw8LOZm8 +Gbh9M88CiO6UxC8ipQLTPh2yyeWArkpJzJK/Pi1eoF1XLiAa0sQ/RaJfQWPm9dvl +1ZQeK5vfD4147b3iBobwEV+CR04SKow0YeEEzAJvzr8YdKI6jqr+2GjjVqzxvRBy +KRVHWCFiR7bZhHGLq3br8hSu0hwjb3oGa1ZI8dui6ujyZt6nm6BoEkau3G/6+zq9 +E6vX3+8Fj4HKCAL6i0SwfGmEpTNp5WUhqibK/fMhhmMT4Mx6MxkT+OFnIjdUU0S/ +e3kgnG8qjficUr38CyEli1U0M7koIXUZI7r+LQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/sample-extension-plugin/src/test/resources/security/root-ca.pem b/sample-extension-plugin/src/test/resources/security/root-ca.pem new file mode 100644 index 0000000000..d33f5f7216 --- /dev/null +++ b/sample-extension-plugin/src/test/resources/security/root-ca.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExjCCA66gAwIBAgIUDWQJmWZ9xBTsQUeOt9F5YSPpqOIwDQYJKoZIhvcNAQEL +BQAwgY8xEzARBgoJkiaJk/IsZAEZFgNjb20xFzAVBgoJkiaJk/IsZAEZFgdleGFt +cGxlMRkwFwYDVQQKDBBFeGFtcGxlIENvbSBJbmMuMSEwHwYDVQQLDBhFeGFtcGxl +IENvbSBJbmMuIFJvb3QgQ0ExITAfBgNVBAMMGEV4YW1wbGUgQ29tIEluYy4gUm9v +dCBDQTAeFw0yNDAyMjAxNzAwMzZaFw0zNDAyMTcxNzAwMzZaMIGPMRMwEQYKCZIm +iZPyLGQBGRYDY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTEZMBcGA1UECgwQ +RXhhbXBsZSBDb20gSW5jLjEhMB8GA1UECwwYRXhhbXBsZSBDb20gSW5jLiBSb290 +IENBMSEwHwYDVQQDDBhFeGFtcGxlIENvbSBJbmMuIFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEPyN7J9VGPyJcQmCBl5TGwfSzvVdWwoQU +j9aEsdfFJ6pBCDQSsj8Lv4RqL0dZra7h7SpZLLX/YZcnjikrYC+rP5OwsI9xEE/4 +U98CsTBPhIMgqFK6SzNE5494BsAk4cL72dOOc8tX19oDS/PvBULbNkthQ0aAF1dg +vbrHvu7hq7LisB5ZRGHVE1k/AbCs2PaaKkn2jCw/b+U0Ml9qPuuEgz2mAqJDGYoA +WSR4YXrOcrmPuRqbws464YZbJW898/0Pn/U300ed+4YHiNYLLJp51AMkR4YEw969 +VRPbWIvLrd0PQBooC/eLrL6rvud/GpYhdQEUx8qcNCKd4bz3OaQ5AgMBAAGjggEW +MIIBEjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQU +F4ffoFrrZhKn1dD4uhJFPLcrAJwwgc8GA1UdIwSBxzCBxIAUF4ffoFrrZhKn1dD4 +uhJFPLcrAJyhgZWkgZIwgY8xEzARBgoJkiaJk/IsZAEZFgNjb20xFzAVBgoJkiaJ +k/IsZAEZFgdleGFtcGxlMRkwFwYDVQQKDBBFeGFtcGxlIENvbSBJbmMuMSEwHwYD +VQQLDBhFeGFtcGxlIENvbSBJbmMuIFJvb3QgQ0ExITAfBgNVBAMMGEV4YW1wbGUg +Q29tIEluYy4gUm9vdCBDQYIUDWQJmWZ9xBTsQUeOt9F5YSPpqOIwDQYJKoZIhvcN +AQELBQADggEBAL3Q3AHUhMiLUy6OlLSt8wX9I2oNGDKbBu0atpUNDztk/0s3YLQC +YuXgN4KrIcMXQIuAXCx407c+pIlT/T1FNn+VQXwi56PYzxQKtlpoKUL3oPQE1d0V +6EoiNk+6UodvyZqpdQu7fXVentRMk1QX7D9otmiiNuX+GSxJhJC2Lyzw65O9EUgG +1yVJon6RkUGtqBqKIuLksKwEr//ELnjmXit4LQKSnqKr0FTCB7seIrKJNyb35Qnq +qy9a/Unhokrmdda1tr6MbqU8l7HmxLuSd/Ky+L0eDNtYv6YfMewtjg0TtAnFyQov +rdXmeq1dy9HLo3Ds4AFz3Gx9076TxcRS/iI= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/sample-extension-plugin/src/test/resources/security/sample.pem b/sample-extension-plugin/src/test/resources/security/sample.pem new file mode 100644 index 0000000000..44101f0b37 --- /dev/null +++ b/sample-extension-plugin/src/test/resources/security/sample.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIUaYSlET3nzsotWTrWueVPPh10yLYwDQYJKoZIhvcNAQEL +BQAwgY8xEzARBgoJkiaJk/IsZAEZFgNjb20xFzAVBgoJkiaJk/IsZAEZFgdleGFt +cGxlMRkwFwYDVQQKDBBFeGFtcGxlIENvbSBJbmMuMSEwHwYDVQQLDBhFeGFtcGxl +IENvbSBJbmMuIFJvb3QgQ0ExITAfBgNVBAMMGEV4YW1wbGUgQ29tIEluYy4gUm9v +dCBDQTAeFw0yNDAyMjAxNzAzMjVaFw0zNDAyMTcxNzAzMjVaMFcxCzAJBgNVBAYT +AmRlMQ0wCwYDVQQHDAR0ZXN0MQ0wCwYDVQQKDARub2RlMQ0wCwYDVQQLDARub2Rl +MRswGQYDVQQDDBJub2RlLTAuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCm93kXteDQHMAvbUPNPW5pyRHKDD42XGWSgq0k1D29C/Ud +yL21HLzTJa49ZU2ldIkSKs9JqbkHdyK0o8MO6L8dotLoYbxDWbJFW8bp1w6tDTU0 +HGkn47XVu3EwbfrTENg3jFu+Oem6a/501SzITzJWtS0cn2dIFOBimTVpT/4Zv5qr +XA6Cp4biOmoTYWhi/qQl8d0IaADiqoZ1MvZbZ6x76qTrRAbg+UWkpTEXoH1xTc8n +dibR7+HP6OTqCKvo1NhE8uP4pY+fWd6b6l+KLo3IKpfTbAIJXIO+M67FLtWKtttD +ao94B069skzKk6FPgW/OZh6PRCD0oxOavV+ld2SjAgMBAAGjgcYwgcMwRwYDVR0R +BEAwPogFKgMEBQWCEm5vZGUtMC5leGFtcGxlLmNvbYIJbG9jYWxob3N0hxAAAAAA +AAAAAAAAAAAAAAABhwR/AAABMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEF +BQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU0/qDQaY10jIo +wCjLUpz/HfQXyt8wHwYDVR0jBBgwFoAUF4ffoFrrZhKn1dD4uhJFPLcrAJwwDQYJ +KoZIhvcNAQELBQADggEBAGbij5WyF0dKhQodQfTiFDb73ygU6IyeJkFSnxF67gDz +pQJZKFvXuVBa3cGP5e7Qp3TK50N+blXGH0xXeIV9lXeYUk4hVfBlp9LclZGX8tGi +7Xa2enMvIt5q/Yg3Hh755ZxnDYxCoGkNOXUmnMusKstE0YzvZ5Gv6fcRKFBUgZLh +hUBqIEAYly1EqH/y45APiRt3Nor1yF6zEI4TnL0yNrHw6LyQkUNCHIGMJLfnJQ9L +camMGIXOx60kXNMTigF9oXXwixWAnDM9y3QT8QXA7hej/4zkbO+vIeV/7lGUdkyg +PAi92EvyxmsliEMyMR0VINl8emyobvfwa7oMeWMR+hg= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/sample-extension-plugin/src/test/resources/security/test-kirk.jks b/sample-extension-plugin/src/test/resources/security/test-kirk.jks new file mode 100644 index 0000000000000000000000000000000000000000..6c8c5ef77e20980f8c78295b159256b805da6a28 GIT binary patch literal 3766 zcmd^=c{r47AIImJ%`(PV###wuU&o%k$xbMgr4m`Pk2Tv-j4?=zEwY?!X|aVw)I`=A zPAY52Rt6yODkPjhAQ%WsfbL*f;mp!-018Nf*#Q6sf)b!}Nv;s_8gzOC@mTmi+D9F}jyYkhL=#Xk3eYM2csmxKA&W!xAdE{tZ2mEGS z;L%QU`DHcrbdbw$3GsKUvmfQu0Z^?sH7B)!W)eLbG*fXB^G$&6CbCnj4~ z*J>Rkut6vL1EvT!JqAq#X=O~#!JHQ#QVSPuOGlnLrXXB~{{FsGRq?o?I;>^GFEhMB zw;z!v1sXap8nq3zz&+prKs-DRPm*XsS4BaP6Z{8tM~n@m|rxMA=p6*i(w=7 z*2&*Yg-uWU$5|W>>g5h)Fn{3B={`skAJ5_wXB5pDwyj{vG1_{{Y-`wB_i^B!5PA|= zrx=_>rprb&75BQ=J)SKPAJI;?(D#46)o+a?SsR^-&qJjXY2ER8S*1ZvU`t7~M6?NKULuzlAZ8C#X9>8j2;WDY z(TY-^!`&0%67`u|U_-Y(knWVcSlh-kwZQ6KG@S?L`W!iVl>Gyd(LnpMc@C!QeY{(E z)uAwF_CcqH#00}jer2dQk3}R|p^87XCxR8`n4c@g9rASTt9$8}SuGW!!+QQ&w&G!P zvv5Mft<&pzv^&XuuQAj&ieoa*3nI-hx}0`4kym=(cd>?v6yM3v43y@5@;yPeJ_N{@ z622W$@5Z4VqliMF3GAf_RcB;$HX^%cwTCgxg^4)5I0?*&oW|giBB@nUNBO+IX=iON zo~;L}HOwhyeqH4GHvAQ5i=|0c+_5*661aDyT_tr=I#+Zog%!9nRiuBb8m&SS4qp2fv7HJMG zwJFuqV*Hoq3`|Mayml;So|9W4Um6Lu8(k+(Hc2}p@&>?!7!7H~9*O%@BrKNAOa-~e z$e6#G)fJ+wNz5x9zU;#>&V}d z?!F1W_eNN;&LI9$!kWa0Zqa)0CVM4D=x(r>aXgW=XQ)PTRsJJ&MC?WjjoMwLRh`-I z8yD|^&(r#NU|pRpRF%wn&t%X`)8HQe%uxEKnXxIu9yui1s$eH0*YZ^Wvt25yOg6{5 zPefKstjqam-PRDz=&-BVb^xZe>{C{$cza!_sV&3M*l0ocMJVr!l~TlJi4JChDn9Nn zc&la1caY}0P&Ho=r;)l;mKBf$V<6A*R6XC}s98g%I7ZIAFI=e6SqQ4;oevw)nw0%^ zKq9#$;{3R0zJv}#mr7@}e+5-(`{C?^vEE#xb7uBY=X#_1v+@~@l?W@Zaq+Yo9bpu& zR<0us_T`(Q6qp1xYb)Rq;tJ|aTZ&y5xqx<_j-|>1$SEi@3!A|| z9YH<3ub_#ai=2WG_V9iQ!NU8mB|$4ZK3Gr>_s15;6W-XV-*##3TjwoMP&yb zq!L{!sQoUn<_ZWb)BbzloM2Zs1tb=+FBn*$!EQmp3Ml#oe;g0);^XP&_osni`NR1A z0SL>FG{F)8;h%d#4-g0eK+%&0UD-=ghUr~yDQ?!lNE5tKiJ_rjY{@`Q1vjbVAFU;|?Qs;w|1hFx_ z`*jR7rVAU>9*yRSpD1)#aOb!)@ak(5hk;guG$_9)=K8Ie^uOP<63|FjrX2UEcJw07 zD5c?bxHD${?)1+CMgPg@0|kH>4NzJZO*;#rl-xA_8*SHCS}ygKZP7*uHbRtmaTE%n zp7Vt7QIt|IIN?)fyS#8IxKHO$?TeY{DpQl5^kyAd$HH^Aa)SJC+I0!ULR znF7*z6R6~{CCW6M^qKuU!N`I`>YB3i6toA7f7#3%T&$5&wm0nY{&d9(g)LB$%g9dX zf>HfjVn9;)rG-^=)tiGDd<5M4wDHPl@yEGU_whSh78l$%S*WCqjvj^Xt?_VKp0T{pQGU!F;?_^4EMT$__$E zH0hMGQlo@W2p^_tPZsnirl@pGb<#0a^*g5ihYtSzKKx%Wg;i4h8B_c6Z+PPWM!I%g zOr-dLp|0@RV@@&InVrwRJfPT~ZY840gT$Jl4)HP^qcTUWE~1&}C2wS3Sv9pJWiRva zyK}a9ilnrYe7SB$bu~GF&GM`D1h@ukNsJY|Yt>|?q(4gzgSUuGwSIfsmlD)%J2V0@ zTU&-58&x%P)-#Oev2~&}bv^wwRbD$?Enu(jJiuwM3shGOZ{$juY+RGk#m^`!p7+vO zAjWFn1{dq`T?N^TggHmN3~VGf^5?a_)R-cj5yfk-?V<|S)%uKn{YGL)7(~eAhWA56 zj7ZS7amp#qQM;t>%6F)v{1S-Gq>88IPiL?2X9=q_r$vhc4{Pd3$WssBMbZaV2W zu&8||{U99-3!x+JudoA1KSAx^0qg$*YLr)FKtJ($lC@k)W?khPY!~B&3F~Xnxs_WH)b*(MC{~@>r={U4@A6+2p8il>0lojdT`r8~C>rA6;jw^lZK9gk<_y!v za(Rbclc{1;TFBtT`lr|YO0}|UXzh>FLsx6RQUq8=?V4{NR#=oxL2}kHb-ZAfuN + project.shadow.component(publication) + artifact sourcesJar + artifact javadocJar + + pom { + name = "OpenSearch Security Resource Sharing SPI" + packaging = "jar" + url = "https://github.com/opensearch-project/security" + description = "OpenSearch Security" + scm { + connection = "scm:git@github.com:opensearch-project/security.git" + developerConnection = "scm:git@github.com:opensearch-project/security.git" + url = "git@github.com:opensearch-project/security.git" + } + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "OpenSearch" + url = "https://github.com/opensearch-project/security" + } + } + } + } + } + + // TODO - enabled debug logging for the time being, remove this eventually + gradle.startParameter.setShowStacktrace(ShowStacktrace.ALWAYS) + gradle.startParameter.setLogLevel(LogLevel.DEBUG) +} diff --git a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingExtension.java b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingExtension.java new file mode 100644 index 0000000000..cddc9bfc7e --- /dev/null +++ b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingExtension.java @@ -0,0 +1,24 @@ +/* + * 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.spi; + +/** + * SPI of security. + */ +public interface ResourceSharingExtension { + /** + * @return resource type string. + */ + String getResourceType(); + + /** + * @return resource index name. + */ + String getResourceIndex(); +} From 3fd702d311c5abc6b7de6b4e13f3ef7f94dc74ef Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 7 Aug 2024 15:48:01 -0400 Subject: [PATCH 02/20] Also assert sample extension Signed-off-by: Craig Perkins --- .../security/sampleextension/SampleExtensionPluginIT.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java index ad73e5f695..fc4e32ed0d 100644 --- a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java @@ -34,5 +34,10 @@ public void testPluginsAreInstalled() throws IOException { Assert.assertTrue( pluginsList.stream().map(o -> (Map) o).anyMatch(plugin -> plugin.get("component").equals("opensearch-security")) ); + Assert.assertTrue( + pluginsList.stream() + .map(o -> (Map) o) + .anyMatch(plugin -> plugin.get("component").equals("opensearch-security-sample-extension")) + ); } } From 69f6105eb1b88fe3a86ef9e373921d7c69cf1b87 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 7 Aug 2024 16:34:24 -0400 Subject: [PATCH 03/20] WIP on add create resource handler Signed-off-by: Craig Perkins --- .../SampleExtensionPlugin.java | 15 ++++- .../SampleExtensionRestHandler.java | 4 +- .../actions/CreateSampleResourceAction.java | 29 ++++++++++ .../actions/CreateSampleResourceRequest.java | 50 +++++++++++++++++ .../actions/CreateSampleResourceResponse.java | 55 +++++++++++++++++++ .../CreateSampleResourceRestAction.java | 45 +++++++++++++++ .../CreateSampleResourceTransportAction.java | 37 +++++++++++++ 7 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceAction.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceResponse.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRestAction.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java index 3fd552bad9..117c98c68f 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java @@ -16,6 +16,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.action.ActionRequest; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNodes; @@ -24,6 +25,7 @@ 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; @@ -34,6 +36,9 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; import org.opensearch.script.ScriptService; +import org.opensearch.security.sampleextension.actions.CreateSampleResourceAction; +import org.opensearch.security.sampleextension.actions.CreateSampleResourceRestAction; +import org.opensearch.security.sampleextension.actions.CreateSampleResourceTransportAction; import org.opensearch.security.spi.ResourceSharingExtension; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; @@ -50,6 +55,8 @@ public class SampleExtensionPlugin extends Plugin implements ActionPlugin, Resou static final String RESOURCE_INDEX_NAME = ".sample_extension_resources"; + private Client client; + @Override public Collection createComponents( Client client, @@ -64,6 +71,7 @@ public Collection createComponents( IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier ) { + this.client = client; return Collections.emptyList(); } @@ -87,6 +95,11 @@ public List getRestHandlers( IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster ) { - return Collections.singletonList(new SampleExtensionRestHandler()); + return List.of(new SampleExtensionRestHandler(), new CreateSampleResourceRestAction()); + } + + @Override + public List> getActions() { + return List.of(new ActionHandler<>(CreateSampleResourceAction.INSTANCE, CreateSampleResourceTransportAction.class)); } } diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java index f2906c6eca..ee53f5af21 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java @@ -35,7 +35,7 @@ * {@code DELETE /_plugins/scheduler_sample/watch?id=dashboards-job-id} */ public class SampleExtensionRestHandler extends BaseRestHandler { - public static final String LIST_RESOURCE_URI = "/_plugins/resource_sample/resource"; + public static final String RESOURCE_URI = "/_plugins/resource_sample/resource"; @Override public String getName() { @@ -44,7 +44,7 @@ public String getName() { @Override public List routes() { - return Collections.unmodifiableList(Arrays.asList(new Route(RestRequest.Method.GET, LIST_RESOURCE_URI))); + return Collections.unmodifiableList(Arrays.asList(new Route(RestRequest.Method.GET, RESOURCE_URI))); } @Override diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceAction.java new file mode 100644 index 0000000000..1a706e981c --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceAction.java @@ -0,0 +1,29 @@ +/* + * 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.sampleextension.actions; + +import org.opensearch.action.ActionType; + +/** + * Action to create a sample resource + */ +public class CreateSampleResourceAction extends ActionType { + /** + * Create sample resource action instance + */ + public static final CreateSampleResourceAction INSTANCE = new CreateSampleResourceAction(); + /** + * Create sample resource action name + */ + public static final String NAME = "cluster:admin/sampleresource/create"; + + private CreateSampleResourceAction() { + super(NAME, CreateSampleResourceResponse::new); + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java new file mode 100644 index 0000000000..d44b59dcc7 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java @@ -0,0 +1,50 @@ +/* + * 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.sampleextension.actions; + +import java.io.IOException; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +/** + * Request object for CreateSampleResource transport action + */ +public class CreateSampleResourceRequest extends ActionRequest { + + private final String name; + + /** + * Default constructor + */ + public CreateSampleResourceRequest(String name) { + this.name = name; + } + + /** + * Constructor with stream input + * @param in the stream input + * @throws IOException IOException + */ + public CreateSampleResourceRequest(final StreamInput in) throws IOException { + this.name = in.readString(); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeString(name); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceResponse.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceResponse.java new file mode 100644 index 0000000000..b2b018d849 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceResponse.java @@ -0,0 +1,55 @@ +/* + * 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.sampleextension.actions; + +import java.io.IOException; + +import org.opensearch.core.action.ActionResponse; +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; + +/** + * Response to a CreateSampleResourceRequest + */ +public class CreateSampleResourceResponse extends ActionResponse implements ToXContentObject { + private final String message; + + /** + * Default constructor + * + * @param message The message + */ + public CreateSampleResourceResponse(String message) { + this.message = message; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(message); + } + + /** + * Constructor with StreamInput + * + * @param in the stream input + */ + public CreateSampleResourceResponse(final StreamInput in) throws IOException { + message = in.readString(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("message", message); + builder.endObject(); + return builder; + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRestAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRestAction.java new file mode 100644 index 0000000000..24641d82e1 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRestAction.java @@ -0,0 +1,45 @@ +/* + * 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.sampleextension.actions; + +import java.util.List; + +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.POST; + +public class CreateSampleResourceRestAction extends BaseRestHandler { + + public CreateSampleResourceRestAction() {} + + @Override + public List routes() { + return singletonList(new Route(POST, "/_plugins/resource_sharing_example/resource")); + } + + @Override + public String getName() { + return "create_sample_resource"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { + String name = request.param("name"); + final CreateSampleResourceRequest createSampleResourceRequest = new CreateSampleResourceRequest(name); + return channel -> client.executeLocally( + CreateSampleResourceAction.INSTANCE, + createSampleResourceRequest, + new RestToXContentListener<>(channel) + ); + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java new file mode 100644 index 0000000000..a5bb6b9641 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java @@ -0,0 +1,37 @@ +/* + * 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.sampleextension.actions; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +/** + * Transport action for GetExecutionContext. + * + * Returns the canonical class name of the plugin that is currently executing the transport action. + */ +public class CreateSampleResourceTransportAction extends HandledTransportAction { + private final TransportService transportService; + + @Inject + public CreateSampleResourceTransportAction(TransportService transportService, ActionFilters actionFilters) { + super(CreateSampleResourceAction.NAME, transportService, actionFilters, CreateSampleResourceRequest::new); + this.transportService = transportService; + } + + @Override + protected void doExecute(Task task, CreateSampleResourceRequest request, ActionListener listener) { + System.out.println("HERE"); + listener.onResponse(new CreateSampleResourceResponse("Hello, world!")); + } +} From c850f1d76a3baca07ab8c1d460809132f79ec466 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 8 Aug 2024 14:27:56 -0400 Subject: [PATCH 04/20] WIP on Create Resource Transport action Signed-off-by: Craig Perkins --- .../CreateSampleResourceTransportAction.java | 1 - .../security/sampleextension/ODFERestTestCase.java | 1 + .../sampleextension/SampleExtensionPluginIT.java | 13 +++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java index a5bb6b9641..d8d0ec856b 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java @@ -31,7 +31,6 @@ public CreateSampleResourceTransportAction(TransportService transportService, Ac @Override protected void doExecute(Task task, CreateSampleResourceRequest request, ActionListener listener) { - System.out.println("HERE"); listener.onResponse(new CreateSampleResourceResponse("Hello, world!")); } } diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java index 0dda33b230..39a3b97810 100644 --- a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/ODFERestTestCase.java @@ -152,6 +152,7 @@ protected static void configureHttpsClient(RestClientBuilder builder, Settings s "Authorization", "Basic " + Base64.getEncoder().encodeToString((userName + ":" + password).getBytes(StandardCharsets.UTF_8)) ); + headers.put("Content-Type", "application/json"); Header[] defaultHeaders = new Header[headers.size()]; int i = 0; for (Map.Entry entry : headers.entrySet()) { diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java index fc4e32ed0d..9182006773 100644 --- a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; +import org.apache.hc.core5.http.io.entity.StringEntity; import org.junit.Assert; import org.opensearch.client.Request; @@ -40,4 +41,16 @@ public void testPluginsAreInstalled() throws IOException { .anyMatch(plugin -> plugin.get("component").equals("opensearch-security-sample-extension")) ); } + + public void testCreateSampleResource() throws IOException { + Request request = new Request("POST", "/_plugins/resource_sharing_example/resource"); + request.setEntity(new StringEntity("{\"name\":\"Craig\"}")); + Response response = client().performRequest(request); + Map createResourceResponse = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + response.getEntity().getContent() + ).mapStrings(); + System.out.println("createResourceResponse: " + createResourceResponse); + } } From 334504b8f7a78e4eb6aab2aba06273b47ad6c086 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 8 Aug 2024 15:47:21 -0400 Subject: [PATCH 05/20] Create index Signed-off-by: Craig Perkins --- .../CreateSampleResourceTransportAction.java | 19 +++++++++++++++++-- .../SampleExtensionPluginIT.java | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java index d8d0ec856b..c6f14948bd 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java @@ -8,8 +8,11 @@ package org.opensearch.security.sampleextension.actions; +import org.opensearch.action.admin.indices.create.CreateIndexRequest; +import org.opensearch.action.admin.indices.create.CreateIndexResponse; 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.tasks.Task; @@ -22,15 +25,27 @@ */ public class CreateSampleResourceTransportAction extends HandledTransportAction { private final TransportService transportService; + private final Client nodeClient; @Inject - public CreateSampleResourceTransportAction(TransportService transportService, ActionFilters actionFilters) { + public CreateSampleResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { super(CreateSampleResourceAction.NAME, transportService, actionFilters, CreateSampleResourceRequest::new); this.transportService = transportService; + this.nodeClient = nodeClient; } @Override protected void doExecute(Task task, CreateSampleResourceRequest request, ActionListener listener) { - listener.onResponse(new CreateSampleResourceResponse("Hello, world!")); + CreateIndexRequest cir = new CreateIndexRequest(".resource-sharing"); + ActionListener cirListener = ActionListener.wrap(response -> { + if (response.isAcknowledged()) { + System.out.println("Created .resource-sharing"); + } else { + System.out.println("Created .resource-sharing call not acknowledged."); + } + listener.onResponse(new CreateSampleResourceResponse("Created .resource-sharing")); + }, listener::onFailure); + System.out.println("Calling create index for .resource-sharing"); + nodeClient.admin().indices().create(cir, cirListener); } } diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java index 9182006773..4e40cc4157 100644 --- a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java @@ -16,6 +16,7 @@ import org.junit.Assert; import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; import org.opensearch.client.Response; import org.opensearch.common.xcontent.LoggingDeprecationHandler; import org.opensearch.common.xcontent.json.JsonXContent; @@ -45,6 +46,9 @@ public void testPluginsAreInstalled() throws IOException { public void testCreateSampleResource() throws IOException { Request request = new Request("POST", "/_plugins/resource_sharing_example/resource"); request.setEntity(new StringEntity("{\"name\":\"Craig\"}")); + RequestOptions.Builder requestOptions = RequestOptions.DEFAULT.toBuilder(); + requestOptions.setWarningsHandler((warnings) -> false); + request.setOptions(requestOptions); Response response = client().performRequest(request); Map createResourceResponse = JsonXContent.jsonXContent.createParser( NamedXContentRegistry.EMPTY, From cf528004a99546c258da71c8f20e785464bed60f Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 8 Aug 2024 16:59:39 -0400 Subject: [PATCH 06/20] Example of usage of why threadContext.stashContext is needed Signed-off-by: Craig Perkins --- .../SampleExtensionPlugin.java | 12 +++++-- .../CreateSampleResourceTransportAction.java | 36 +++++++++++++------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java index 117c98c68f..382a8b70f0 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java @@ -30,8 +30,10 @@ 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.ActionPlugin; 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; @@ -50,10 +52,10 @@ * endpoint using {@link SampleExtensionRestHandler}. * */ -public class SampleExtensionPlugin extends Plugin implements ActionPlugin, ResourceSharingExtension { +public class SampleExtensionPlugin extends Plugin implements ActionPlugin, SystemIndexPlugin, ResourceSharingExtension { private static final Logger log = LogManager.getLogger(SampleExtensionPlugin.class); - static final String RESOURCE_INDEX_NAME = ".sample_extension_resources"; + public static final String RESOURCE_INDEX_NAME = ".sample_extension_resources"; private Client client; @@ -102,4 +104,10 @@ public List getRestHandlers( public List> getActions() { return List.of(new ActionHandler<>(CreateSampleResourceAction.INSTANCE, CreateSampleResourceTransportAction.class)); } + + @Override + public Collection getSystemIndexDescriptors(Settings settings) { + final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(RESOURCE_INDEX_NAME, "Example index with resources"); + return Collections.singletonList(systemIndexDescriptor); + } } diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java index c6f14948bd..28dfa30318 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java @@ -10,14 +10,19 @@ import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.Client; import org.opensearch.common.inject.Inject; +import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; + /** * Transport action for GetExecutionContext. * @@ -36,16 +41,25 @@ public CreateSampleResourceTransportAction(TransportService transportService, Ac @Override protected void doExecute(Task task, CreateSampleResourceRequest request, ActionListener listener) { - CreateIndexRequest cir = new CreateIndexRequest(".resource-sharing"); - ActionListener cirListener = ActionListener.wrap(response -> { - if (response.isAcknowledged()) { - System.out.println("Created .resource-sharing"); - } else { - System.out.println("Created .resource-sharing call not acknowledged."); - } - listener.onResponse(new CreateSampleResourceResponse("Created .resource-sharing")); - }, listener::onFailure); - System.out.println("Calling create index for .resource-sharing"); - nodeClient.admin().indices().create(cir, cirListener); + try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { + CreateIndexRequest cir = new CreateIndexRequest(RESOURCE_INDEX_NAME); + ActionListener cirListener = ActionListener.wrap(response -> { + if (response.isAcknowledged()) { + System.out.println("Created " + RESOURCE_INDEX_NAME); + } else { + System.out.println("Created " + RESOURCE_INDEX_NAME + " call not acknowledged."); + } + DeleteIndexRequest dir = new DeleteIndexRequest(RESOURCE_INDEX_NAME); + ActionListener dirListener = ActionListener.wrap(deletedResponse -> { + listener.onResponse(new CreateSampleResourceResponse("Created and Deleted " + RESOURCE_INDEX_NAME)); + }, listener::onFailure); + + System.out.println("Calling delete index for " + RESOURCE_INDEX_NAME); + nodeClient.admin().indices().delete(dir, dirListener); + // listener.onResponse(new CreateSampleResourceResponse("Created " + RESOURCE_INDEX_NAME)); + }, listener::onFailure); + System.out.println("Calling create index for " + RESOURCE_INDEX_NAME); + nodeClient.admin().indices().create(cir, cirListener); + } } } From 36455cc024400f2faaf5646e5170c28ad0618c93 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 13 Aug 2024 17:09:50 -0400 Subject: [PATCH 07/20] Implement create resource Signed-off-by: Craig Perkins --- .../actions/CreateSampleResourceRequest.java | 4 ++ .../CreateSampleResourceTransportAction.java | 40 +++++++++++-------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java index d44b59dcc7..a771680750 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java @@ -47,4 +47,8 @@ public void writeTo(final StreamOutput out) throws IOException { public ActionRequestValidationException validate() { return null; } + + public String getName() { + return this.name; + } } diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java index 28dfa30318..220b9ceb82 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java @@ -8,12 +8,14 @@ package org.opensearch.security.sampleextension.actions; +import java.io.IOException; + import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; -import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexResponse; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; -import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.Client; import org.opensearch.common.inject.Inject; import org.opensearch.common.util.concurrent.ThreadContext; @@ -21,6 +23,7 @@ import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; /** @@ -43,23 +46,28 @@ public CreateSampleResourceTransportAction(TransportService transportService, Ac protected void doExecute(Task task, CreateSampleResourceRequest request, ActionListener listener) { try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { CreateIndexRequest cir = new CreateIndexRequest(RESOURCE_INDEX_NAME); - ActionListener cirListener = ActionListener.wrap(response -> { - if (response.isAcknowledged()) { - System.out.println("Created " + RESOURCE_INDEX_NAME); - } else { - System.out.println("Created " + RESOURCE_INDEX_NAME + " call not acknowledged."); + ActionListener cirListener = ActionListener.wrap( + response -> { createResource(request, listener); }, + (failResponse) -> { + /* Index already exists, ignore and continue */ + createResource(request, listener); } - DeleteIndexRequest dir = new DeleteIndexRequest(RESOURCE_INDEX_NAME); - ActionListener dirListener = ActionListener.wrap(deletedResponse -> { - listener.onResponse(new CreateSampleResourceResponse("Created and Deleted " + RESOURCE_INDEX_NAME)); - }, listener::onFailure); + ); + nodeClient.admin().indices().create(cir, cirListener); + } + } - System.out.println("Calling delete index for " + RESOURCE_INDEX_NAME); - nodeClient.admin().indices().delete(dir, dirListener); - // listener.onResponse(new CreateSampleResourceResponse("Created " + RESOURCE_INDEX_NAME)); + private void createResource(CreateSampleResourceRequest request, ActionListener listener) { + try { + IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME) + .setSource(jsonBuilder().startObject().field("name", request.getName()).endObject()) + .request(); + ActionListener irListener = ActionListener.wrap(idxResponse -> { + listener.onResponse(new CreateSampleResourceResponse("Created resource: " + idxResponse.toString())); }, listener::onFailure); - System.out.println("Calling create index for " + RESOURCE_INDEX_NAME); - nodeClient.admin().indices().create(cir, cirListener); + nodeClient.index(ir, irListener); + } catch (IOException e) { + throw new RuntimeException(e); } } } From 04232ffb5e730aa3725f701c2609900e8c5af24d Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 14 Aug 2024 16:02:38 -0400 Subject: [PATCH 08/20] Create SampleResource Signed-off-by: Craig Perkins --- .../SampleExtensionPlugin.java | 13 +----- .../CreateSampleResourceTransportAction.java | 6 ++- .../resource/SampleResource.java | 40 +++++++++++++++++++ ...arch.security.spi.ResourceSharingExtension | 2 +- 4 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java index 382a8b70f0..a81ff808e0 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java @@ -41,7 +41,6 @@ import org.opensearch.security.sampleextension.actions.CreateSampleResourceAction; import org.opensearch.security.sampleextension.actions.CreateSampleResourceRestAction; import org.opensearch.security.sampleextension.actions.CreateSampleResourceTransportAction; -import org.opensearch.security.spi.ResourceSharingExtension; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; @@ -52,7 +51,7 @@ * endpoint using {@link SampleExtensionRestHandler}. * */ -public class SampleExtensionPlugin extends Plugin implements ActionPlugin, SystemIndexPlugin, ResourceSharingExtension { +public class SampleExtensionPlugin extends Plugin implements ActionPlugin, SystemIndexPlugin { private static final Logger log = LogManager.getLogger(SampleExtensionPlugin.class); public static final String RESOURCE_INDEX_NAME = ".sample_extension_resources"; @@ -77,16 +76,6 @@ public Collection createComponents( return Collections.emptyList(); } - @Override - public String getResourceType() { - return "sample_resource"; - } - - @Override - public String getResourceIndex() { - return RESOURCE_INDEX_NAME; - } - @Override public List getRestHandlers( Settings settings, diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java index 220b9ceb82..b5bf29260a 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java @@ -20,6 +20,8 @@ import org.opensearch.common.inject.Inject; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.security.sampleextension.resource.SampleResource; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -58,10 +60,12 @@ protected void doExecute(Task task, CreateSampleResourceRequest request, ActionL } private void createResource(CreateSampleResourceRequest request, ActionListener listener) { + SampleResource sample = new SampleResource(request.getName()); try { IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME) - .setSource(jsonBuilder().startObject().field("name", request.getName()).endObject()) + .setSource(sample.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) .request(); + ActionListener irListener = ActionListener.wrap(idxResponse -> { listener.onResponse(new CreateSampleResourceResponse("Created resource: " + idxResponse.toString())); }, listener::onFailure); diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java new file mode 100644 index 0000000000..ca1954bd12 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java @@ -0,0 +1,40 @@ +package org.opensearch.security.sampleextension.resource; + +import java.io.IOException; + +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.security.spi.ResourceSharingExtension; + +import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; + +public class SampleResource implements ResourceSharingExtension, Writeable, ToXContentFragment { + + private String name; + + public SampleResource(String name) { + this.name = name; + } + + @Override + public String getResourceType() { + return "sample_resource"; + } + + @Override + public String getResourceIndex() { + return RESOURCE_INDEX_NAME; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().field("name", name).endObject(); + } + + @Override + public void writeTo(StreamOutput streamOutput) throws IOException { + streamOutput.writeString(name); + } +} diff --git a/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension b/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension index e32f06ee56..904fb99e99 100644 --- a/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension +++ b/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension @@ -3,4 +3,4 @@ # SPDX-License-Identifier: Apache-2.0 # -org.opensearch.security.sampleextension.SampleExtensionPlugin \ No newline at end of file +org.opensearch.security.sampleextension.resource.SampleResource \ No newline at end of file From a0e0386d30ea29b81d20021b7fda11b2087c4a1a Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 14 Aug 2024 16:36:37 -0400 Subject: [PATCH 09/20] Add list resource Signed-off-by: Craig Perkins --- .../SampleExtensionPlugin.java | 16 +++-- .../SampleExtensionRestHandler.java | 60 ------------------- .../CreateSampleResourceAction.java | 2 +- .../CreateSampleResourceRequest.java | 2 +- .../CreateSampleResourceResponse.java | 2 +- .../CreateSampleResourceRestAction.java | 14 ++++- .../CreateSampleResourceTransportAction.java | 16 +++-- .../list/ListSampleResourceAction.java | 29 +++++++++ .../list/ListSampleResourceRequest.java | 39 ++++++++++++ .../list/ListSampleResourceResponse.java | 55 +++++++++++++++++ .../list/ListSampleResourceRestAction.java | 44 ++++++++++++++ .../ListSampleResourceTransportAction.java | 54 +++++++++++++++++ .../resource/SampleResource.java | 2 +- .../SampleExtensionPluginIT.java | 20 +++++-- 14 files changed, 275 insertions(+), 80 deletions(-) delete mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java rename sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/{ => create}/CreateSampleResourceAction.java (92%) rename sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/{ => create}/CreateSampleResourceRequest.java (95%) rename sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/{ => create}/CreateSampleResourceResponse.java (95%) rename sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/{ => create}/CreateSampleResourceRestAction.java (76%) rename sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/{ => create}/CreateSampleResourceTransportAction.java (86%) create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceAction.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRequest.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceResponse.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRestAction.java create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.java diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java index a81ff808e0..98918bda09 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java @@ -38,9 +38,12 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; import org.opensearch.script.ScriptService; -import org.opensearch.security.sampleextension.actions.CreateSampleResourceAction; -import org.opensearch.security.sampleextension.actions.CreateSampleResourceRestAction; -import org.opensearch.security.sampleextension.actions.CreateSampleResourceTransportAction; +import org.opensearch.security.sampleextension.actions.create.CreateSampleResourceAction; +import org.opensearch.security.sampleextension.actions.create.CreateSampleResourceRestAction; +import org.opensearch.security.sampleextension.actions.create.CreateSampleResourceTransportAction; +import org.opensearch.security.sampleextension.actions.list.ListSampleResourceAction; +import org.opensearch.security.sampleextension.actions.list.ListSampleResourceRestAction; +import org.opensearch.security.sampleextension.actions.list.ListSampleResourceTransportAction; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; @@ -86,12 +89,15 @@ public List getRestHandlers( IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster ) { - return List.of(new SampleExtensionRestHandler(), new CreateSampleResourceRestAction()); + return List.of(new CreateSampleResourceRestAction(), new ListSampleResourceRestAction()); } @Override public List> getActions() { - return List.of(new ActionHandler<>(CreateSampleResourceAction.INSTANCE, CreateSampleResourceTransportAction.class)); + return List.of( + new ActionHandler<>(CreateSampleResourceAction.INSTANCE, CreateSampleResourceTransportAction.class), + new ActionHandler<>(ListSampleResourceAction.INSTANCE, ListSampleResourceTransportAction.class) + ); } @Override diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java deleted file mode 100644 index ee53f5af21..0000000000 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionRestHandler.java +++ /dev/null @@ -1,60 +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.sampleextension; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.opensearch.client.node.NodeClient; -import org.opensearch.core.rest.RestStatus; -import org.opensearch.rest.BaseRestHandler; -import org.opensearch.rest.BytesRestResponse; -import org.opensearch.rest.RestRequest; - -/** - * A sample rest handler that supports schedule and deschedule job operation - * - * Users need to provide "id", "index", "job_name", and "interval" parameter to schedule - * a job. e.g. - * {@code - * POST /_plugins/scheduler_sample/watch?id=dashboards-job-id&job_name=watch dashboards index&index=.opensearch_dashboards_1&interval=1 - * } - * - * creates a job with id "dashboards-job-id" and job name "watch dashboards index", - * which logs ".opensearch_dashboards_1" index's shards info every 1 minute - * - * Users can remove that job by calling - * {@code DELETE /_plugins/scheduler_sample/watch?id=dashboards-job-id} - */ -public class SampleExtensionRestHandler extends BaseRestHandler { - public static final String RESOURCE_URI = "/_plugins/resource_sample/resource"; - - @Override - public String getName() { - return "Sample Security Resource Sharing extension handler"; - } - - @Override - public List routes() { - return Collections.unmodifiableList(Arrays.asList(new Route(RestRequest.Method.GET, RESOURCE_URI))); - } - - @Override - protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { - if (request.method().equals(RestRequest.Method.GET)) { - return restChannel -> { restChannel.sendResponse(new BytesRestResponse(RestStatus.OK, "List Resources called")); }; - } else { - return restChannel -> { - restChannel.sendResponse(new BytesRestResponse(RestStatus.METHOD_NOT_ALLOWED, request.method() + " is not allowed.")); - }; - } - } -} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceAction.java similarity index 92% rename from sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceAction.java rename to sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceAction.java index 1a706e981c..214bd4d660 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceAction.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sampleextension.actions; +package org.opensearch.security.sampleextension.actions.create; import org.opensearch.action.ActionType; diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java similarity index 95% rename from sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java rename to sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java index a771680750..0fbe375fed 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRequest.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sampleextension.actions; +package org.opensearch.security.sampleextension.actions.create; import java.io.IOException; diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceResponse.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceResponse.java similarity index 95% rename from sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceResponse.java rename to sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceResponse.java index b2b018d849..54cc5746f2 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceResponse.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceResponse.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sampleextension.actions; +package org.opensearch.security.sampleextension.actions.create; import java.io.IOException; diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRestAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java similarity index 76% rename from sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRestAction.java rename to sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java index 24641d82e1..d6618c413d 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceRestAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java @@ -6,11 +6,14 @@ * compatible open source license. */ -package org.opensearch.security.sampleextension.actions; +package org.opensearch.security.sampleextension.actions.create; +import java.io.IOException; import java.util.List; +import java.util.Map; import org.opensearch.client.node.NodeClient; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; @@ -33,8 +36,13 @@ public String getName() { } @Override - public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { - String name = request.param("name"); + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + Map source; + try (XContentParser parser = request.contentParser()) { + source = parser.map(); + } + + String name = (String) source.get("name"); final CreateSampleResourceRequest createSampleResourceRequest = new CreateSampleResourceRequest(name); return channel -> client.executeLocally( CreateSampleResourceAction.INSTANCE, diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java similarity index 86% rename from sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java rename to sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java index b5bf29260a..3e3aeaa15e 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java @@ -6,16 +6,20 @@ * compatible open source license. */ -package org.opensearch.security.sampleextension.actions; +package org.opensearch.security.sampleextension.actions.create; import java.io.IOException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; 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.util.concurrent.ThreadContext; @@ -29,11 +33,11 @@ import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; /** - * Transport action for GetExecutionContext. - * - * Returns the canonical class name of the plugin that is currently executing the transport action. + * Transport action for CreateSampleResource. */ public class CreateSampleResourceTransportAction extends HandledTransportAction { + private static final Logger log = LogManager.getLogger(CreateSampleResourceTransportAction.class); + private final TransportService transportService; private final Client nodeClient; @@ -60,12 +64,16 @@ protected void doExecute(Task task, CreateSampleResourceRequest request, ActionL } private void createResource(CreateSampleResourceRequest request, ActionListener listener) { + log.warn("Sample name: " + request.getName()); SampleResource sample = new SampleResource(request.getName()); try { IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .setSource(sample.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) .request(); + log.warn("Index Request: " + ir.toString()); + ActionListener irListener = ActionListener.wrap(idxResponse -> { listener.onResponse(new CreateSampleResourceResponse("Created resource: " + idxResponse.toString())); }, listener::onFailure); diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceAction.java new file mode 100644 index 0000000000..aac9ab10d9 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceAction.java @@ -0,0 +1,29 @@ +/* + * 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.sampleextension.actions.list; + +import org.opensearch.action.ActionType; + +/** + * Action to list sample resources + */ +public class ListSampleResourceAction extends ActionType { + /** + * List sample resource action instance + */ + public static final ListSampleResourceAction INSTANCE = new ListSampleResourceAction(); + /** + * List sample resource action name + */ + public static final String NAME = "cluster:admin/sampleresource/list"; + + private ListSampleResourceAction() { + super(NAME, ListSampleResourceResponse::new); + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRequest.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRequest.java new file mode 100644 index 0000000000..3a6acb5cfb --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRequest.java @@ -0,0 +1,39 @@ +/* + * 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.sampleextension.actions.list; + +import java.io.IOException; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +/** + * Request object for ListSampleResource transport action + */ +public class ListSampleResourceRequest extends ActionRequest { + + public ListSampleResourceRequest() {} + + /** + * Constructor with stream input + * @param in the stream input + * @throws IOException IOException + */ + public ListSampleResourceRequest(final StreamInput in) throws IOException {} + + @Override + public void writeTo(final StreamOutput out) throws IOException {} + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceResponse.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceResponse.java new file mode 100644 index 0000000000..eb1150a379 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceResponse.java @@ -0,0 +1,55 @@ +/* + * 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.sampleextension.actions.list; + +import java.io.IOException; + +import org.opensearch.core.action.ActionResponse; +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; + +/** + * Response to a ListSampleResourceRequest + */ +public class ListSampleResourceResponse extends ActionResponse implements ToXContentObject { + private final String message; + + /** + * Default constructor + * + * @param message The message + */ + public ListSampleResourceResponse(String message) { + this.message = message; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(message); + } + + /** + * Constructor with StreamInput + * + * @param in the stream input + */ + public ListSampleResourceResponse(final StreamInput in) throws IOException { + message = in.readString(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("message", message); + builder.endObject(); + return builder; + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRestAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRestAction.java new file mode 100644 index 0000000000..4bab2c9ce6 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceRestAction.java @@ -0,0 +1,44 @@ +/* + * 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.sampleextension.actions.list; + +import java.util.List; + +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.GET; + +public class ListSampleResourceRestAction extends BaseRestHandler { + + public ListSampleResourceRestAction() {} + + @Override + public List routes() { + return singletonList(new Route(GET, "/_plugins/resource_sharing_example/resource")); + } + + @Override + public String getName() { + return "list_sample_resources"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { + final ListSampleResourceRequest listSampleResourceRequest = new ListSampleResourceRequest(); + return channel -> client.executeLocally( + ListSampleResourceAction.INSTANCE, + listSampleResourceRequest, + new RestToXContentListener<>(channel) + ); + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.java new file mode 100644 index 0000000000..8f102eef14 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.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. + */ + +package org.opensearch.security.sampleextension.actions.list; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +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.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.index.query.MatchAllQueryBuilder; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; + +/** + * Transport action for ListSampleResource. + */ +public class ListSampleResourceTransportAction extends HandledTransportAction { + private final TransportService transportService; + private final Client nodeClient; + + @Inject + public ListSampleResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { + super(ListSampleResourceAction.NAME, transportService, actionFilters, ListSampleResourceRequest::new); + this.transportService = transportService; + this.nodeClient = nodeClient; + } + + @Override + protected void doExecute(Task task, ListSampleResourceRequest request, ActionListener listener) { + try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { + SearchRequest sr = new SearchRequest(RESOURCE_INDEX_NAME); + SearchSourceBuilder matchAllQuery = new SearchSourceBuilder(); + matchAllQuery.query(new MatchAllQueryBuilder()); + sr.source(matchAllQuery); + /* Index already exists, ignore and continue */ + ActionListener searchListener = ActionListener.wrap(response -> { + listener.onResponse(new ListSampleResourceResponse(response.toString())); + }, listener::onFailure); + nodeClient.search(sr, searchListener); + } + } +} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java index ca1954bd12..360d3357a2 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java @@ -12,7 +12,7 @@ public class SampleResource implements ResourceSharingExtension, Writeable, ToXContentFragment { - private String name; + private final String name; public SampleResource(String name) { this.name = name; diff --git a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java index 4e40cc4157..9b418eeb04 100644 --- a/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java +++ b/sample-extension-plugin/src/test/java/org/opensearch/security/sampleextension/SampleExtensionPluginIT.java @@ -44,17 +44,29 @@ public void testPluginsAreInstalled() throws IOException { } public void testCreateSampleResource() throws IOException { - Request request = new Request("POST", "/_plugins/resource_sharing_example/resource"); - request.setEntity(new StringEntity("{\"name\":\"Craig\"}")); + Request createRequest = new Request("POST", "/_plugins/resource_sharing_example/resource"); + createRequest.setEntity(new StringEntity("{\"name\":\"Craig\"}")); RequestOptions.Builder requestOptions = RequestOptions.DEFAULT.toBuilder(); requestOptions.setWarningsHandler((warnings) -> false); - request.setOptions(requestOptions); - Response response = client().performRequest(request); + createRequest.setOptions(requestOptions); + Response response = client().performRequest(createRequest); Map createResourceResponse = JsonXContent.jsonXContent.createParser( NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getEntity().getContent() ).mapStrings(); System.out.println("createResourceResponse: " + createResourceResponse); + + Request listRequest = new Request("GET", "/_plugins/resource_sharing_example/resource"); + RequestOptions.Builder listRequestOptions = RequestOptions.DEFAULT.toBuilder(); + requestOptions.setWarningsHandler((warnings) -> false); + listRequest.setOptions(listRequestOptions); + Response listResponse = client().performRequest(listRequest); + Map listResourceResponse = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + listResponse.getEntity().getContent() + ).mapStrings(); + System.out.println("listResourceResponse: " + listResourceResponse); } } From 2610ed04cff32411732053c8888f00d895046f46 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 14 Aug 2024 21:08:31 -0400 Subject: [PATCH 10/20] Create Abstract resource Signed-off-by: Craig Perkins --- .../create/CreateSampleResourceRequest.java | 16 +++++++++------- .../create/CreateSampleResourceRestAction.java | 4 +++- .../CreateSampleResourceTransportAction.java | 4 ++-- .../sampleextension/resource/Resource.java | 6 ++++++ .../sampleextension/resource/SampleResource.java | 9 ++++++--- 5 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java index 0fbe375fed..84363f4cad 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java @@ -14,19 +14,21 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.security.sampleextension.resource.Resource; +import org.opensearch.security.sampleextension.resource.SampleResource; /** * Request object for CreateSampleResource transport action */ public class CreateSampleResourceRequest extends ActionRequest { - private final String name; + private final Resource resource; /** * Default constructor */ - public CreateSampleResourceRequest(String name) { - this.name = name; + public CreateSampleResourceRequest(Resource resource) { + this.resource = resource; } /** @@ -35,12 +37,12 @@ public CreateSampleResourceRequest(String name) { * @throws IOException IOException */ public CreateSampleResourceRequest(final StreamInput in) throws IOException { - this.name = in.readString(); + this.resource = new SampleResource(in); } @Override public void writeTo(final StreamOutput out) throws IOException { - out.writeString(name); + resource.writeTo(out); } @Override @@ -48,7 +50,7 @@ public ActionRequestValidationException validate() { return null; } - public String getName() { - return this.name; + public Resource getResource() { + return this.resource; } } diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java index d6618c413d..28801f3e79 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java @@ -17,6 +17,7 @@ import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; +import org.opensearch.security.sampleextension.resource.SampleResource; import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.POST; @@ -43,7 +44,8 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client } String name = (String) source.get("name"); - final CreateSampleResourceRequest createSampleResourceRequest = new CreateSampleResourceRequest(name); + SampleResource resource = new SampleResource(name); + final CreateSampleResourceRequest createSampleResourceRequest = new CreateSampleResourceRequest(resource); return channel -> client.executeLocally( CreateSampleResourceAction.INSTANCE, createSampleResourceRequest, diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java index 3e3aeaa15e..ad72a290b1 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java @@ -64,8 +64,8 @@ protected void doExecute(Task task, CreateSampleResourceRequest request, ActionL } private void createResource(CreateSampleResourceRequest request, ActionListener listener) { - log.warn("Sample name: " + request.getName()); - SampleResource sample = new SampleResource(request.getName()); + log.warn("Sample name: " + request.getResource()); + SampleResource sample = (SampleResource) request.getResource(); try { IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java new file mode 100644 index 0000000000..ec8a2b48c4 --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java @@ -0,0 +1,6 @@ +package org.opensearch.security.sampleextension.resource; + +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; + +public abstract class Resource implements Writeable, ToXContentFragment {} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java index 360d3357a2..a709b7b894 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java @@ -2,15 +2,14 @@ import java.io.IOException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; -import org.opensearch.core.common.io.stream.Writeable; -import org.opensearch.core.xcontent.ToXContentFragment; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.security.spi.ResourceSharingExtension; import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; -public class SampleResource implements ResourceSharingExtension, Writeable, ToXContentFragment { +public class SampleResource extends Resource implements ResourceSharingExtension { private final String name; @@ -18,6 +17,10 @@ public SampleResource(String name) { this.name = name; } + public SampleResource(StreamInput in) throws IOException { + this.name = in.readString(); + } + @Override public String getResourceType() { return "sample_resource"; From 487e468f48162fcccbe979703745e6323f124260 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 14 Aug 2024 21:17:05 -0400 Subject: [PATCH 11/20] Remove cast Signed-off-by: Craig Perkins --- .../actions/create/CreateSampleResourceTransportAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java index ad72a290b1..6f7d99c516 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java @@ -25,7 +25,7 @@ import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; import org.opensearch.core.xcontent.ToXContent; -import org.opensearch.security.sampleextension.resource.SampleResource; +import org.opensearch.security.sampleextension.resource.Resource; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -65,7 +65,7 @@ protected void doExecute(Task task, CreateSampleResourceRequest request, ActionL private void createResource(CreateSampleResourceRequest request, ActionListener listener) { log.warn("Sample name: " + request.getResource()); - SampleResource sample = (SampleResource) request.getResource(); + Resource sample = request.getResource(); try { IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) From 3361e93cc22550ae304f3a4a583f4775f9793766 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 14 Aug 2024 23:15:59 -0400 Subject: [PATCH 12/20] Create abstract transport action Signed-off-by: Craig Perkins --- .../SampleExtensionPlugin.java | 1 - .../create/CreateSampleResourceAction.java | 5 +- .../create/CreateSampleResourceRequest.java | 2 +- .../CreateSampleResourceRestAction.java | 3 +- .../CreateSampleResourceTransportAction.java | 60 +----------- .../sampleextension/resource/Resource.java | 6 -- .../resource/SampleResource.java | 6 ++ .../org/opensearch/security/spi/Resource.java | 6 ++ .../spi/actions/CreateResourceRequest.java | 51 ++++++++++ .../spi/actions/CreateResourceResponse.java | 55 +++++++++++ .../CreateResourceTransportAction.java | 94 +++++++++++++++++++ 11 files changed, 222 insertions(+), 67 deletions(-) delete mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java create mode 100644 spi/src/main/java/org/opensearch/security/spi/Resource.java create mode 100644 spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceRequest.java create mode 100644 spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceResponse.java create mode 100644 spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java index 98918bda09..0c7162c0e9 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java @@ -51,7 +51,6 @@ * Sample Security Resource Sharing extension plugin. * * It use ".sample_extension_resources" index to manage its resources, and exposes a REST API - * endpoint using {@link SampleExtensionRestHandler}. * */ public class SampleExtensionPlugin extends Plugin implements ActionPlugin, SystemIndexPlugin { diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceAction.java index 214bd4d660..19c2685ec3 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceAction.java @@ -9,11 +9,12 @@ package org.opensearch.security.sampleextension.actions.create; import org.opensearch.action.ActionType; +import org.opensearch.security.spi.actions.CreateResourceResponse; /** * Action to create a sample resource */ -public class CreateSampleResourceAction extends ActionType { +public class CreateSampleResourceAction extends ActionType { /** * Create sample resource action instance */ @@ -24,6 +25,6 @@ public class CreateSampleResourceAction extends ActionType createSampleResourceRequest = new CreateResourceRequest<>(resource); return channel -> client.executeLocally( CreateSampleResourceAction.INSTANCE, createSampleResourceRequest, diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java index 6f7d99c516..f4b2a90fa0 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java @@ -8,78 +8,26 @@ package org.opensearch.security.sampleextension.actions.create; -import java.io.IOException; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.admin.indices.create.CreateIndexRequest; -import org.opensearch.action.admin.indices.create.CreateIndexResponse; -import org.opensearch.action.index.IndexRequest; -import org.opensearch.action.index.IndexResponse; 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.util.concurrent.ThreadContext; -import org.opensearch.core.action.ActionListener; -import org.opensearch.core.xcontent.ToXContent; -import org.opensearch.security.sampleextension.resource.Resource; -import org.opensearch.tasks.Task; +import org.opensearch.security.sampleextension.resource.SampleResource; +import org.opensearch.security.spi.actions.CreateResourceTransportAction; import org.opensearch.transport.TransportService; -import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; /** * Transport action for CreateSampleResource. */ -public class CreateSampleResourceTransportAction extends HandledTransportAction { +public class CreateSampleResourceTransportAction extends CreateResourceTransportAction { private static final Logger log = LogManager.getLogger(CreateSampleResourceTransportAction.class); - private final TransportService transportService; - private final Client nodeClient; - @Inject public CreateSampleResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { - super(CreateSampleResourceAction.NAME, transportService, actionFilters, CreateSampleResourceRequest::new); - this.transportService = transportService; - this.nodeClient = nodeClient; - } - - @Override - protected void doExecute(Task task, CreateSampleResourceRequest request, ActionListener listener) { - try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { - CreateIndexRequest cir = new CreateIndexRequest(RESOURCE_INDEX_NAME); - ActionListener cirListener = ActionListener.wrap( - response -> { createResource(request, listener); }, - (failResponse) -> { - /* Index already exists, ignore and continue */ - createResource(request, listener); - } - ); - nodeClient.admin().indices().create(cir, cirListener); - } - } - - private void createResource(CreateSampleResourceRequest request, ActionListener listener) { - log.warn("Sample name: " + request.getResource()); - Resource sample = request.getResource(); - try { - IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME) - .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) - .setSource(sample.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) - .request(); - - log.warn("Index Request: " + ir.toString()); - - ActionListener irListener = ActionListener.wrap(idxResponse -> { - listener.onResponse(new CreateSampleResourceResponse("Created resource: " + idxResponse.toString())); - }, listener::onFailure); - nodeClient.index(ir, irListener); - } catch (IOException e) { - throw new RuntimeException(e); - } + super(transportService, actionFilters, nodeClient, CreateSampleResourceAction.NAME, RESOURCE_INDEX_NAME, SampleResource::new); } } diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java deleted file mode 100644 index ec8a2b48c4..0000000000 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/Resource.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.opensearch.security.sampleextension.resource; - -import org.opensearch.core.common.io.stream.Writeable; -import org.opensearch.core.xcontent.ToXContentFragment; - -public abstract class Resource implements Writeable, ToXContentFragment {} diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java index a709b7b894..4b21953f83 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java @@ -5,6 +5,7 @@ import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.security.spi.Resource; import org.opensearch.security.spi.ResourceSharingExtension; import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; @@ -40,4 +41,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public void writeTo(StreamOutput streamOutput) throws IOException { streamOutput.writeString(name); } + + @Override + public String getWriteableName() { + return "sampled_resource"; + } } diff --git a/spi/src/main/java/org/opensearch/security/spi/Resource.java b/spi/src/main/java/org/opensearch/security/spi/Resource.java new file mode 100644 index 0000000000..39482b4232 --- /dev/null +++ b/spi/src/main/java/org/opensearch/security/spi/Resource.java @@ -0,0 +1,6 @@ +package org.opensearch.security.spi; + +import org.opensearch.core.common.io.stream.NamedWriteable; +import org.opensearch.core.xcontent.ToXContentFragment; + +public abstract class Resource implements NamedWriteable, ToXContentFragment {} diff --git a/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceRequest.java b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceRequest.java new file mode 100644 index 0000000000..e930cf1ecf --- /dev/null +++ b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceRequest.java @@ -0,0 +1,51 @@ +/* + * 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.spi.actions; + +import java.io.IOException; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.security.spi.Resource; + +/** + * Request object for CreateSampleResource transport action + */ +public class CreateResourceRequest extends ActionRequest { + + private final T resource; + + /** + * Default constructor + */ + public CreateResourceRequest(T resource) { + this.resource = resource; + } + + public CreateResourceRequest(StreamInput in, Writeable.Reader resourceReader) throws IOException { + this.resource = resourceReader.read(in); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + resource.writeTo(out); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + public Resource getResource() { + return this.resource; + } +} diff --git a/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceResponse.java b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceResponse.java new file mode 100644 index 0000000000..958ca9fbbf --- /dev/null +++ b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceResponse.java @@ -0,0 +1,55 @@ +/* + * 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.spi.actions; + +import java.io.IOException; + +import org.opensearch.core.action.ActionResponse; +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; + +/** + * Response to a CreateSampleResourceRequest + */ +public class CreateResourceResponse extends ActionResponse implements ToXContentObject { + private final String message; + + /** + * Default constructor + * + * @param message The message + */ + public CreateResourceResponse(String message) { + this.message = message; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(message); + } + + /** + * Constructor with StreamInput + * + * @param in the stream input + */ + public CreateResourceResponse(final StreamInput in) throws IOException { + message = in.readString(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("message", message); + builder.endObject(); + return builder; + } +} diff --git a/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java new file mode 100644 index 0000000000..5fbab4c4f9 --- /dev/null +++ b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java @@ -0,0 +1,94 @@ +/* + * 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.spi.actions; + +import java.io.IOException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.action.admin.indices.create.CreateIndexRequest; +import org.opensearch.action.admin.indices.create.CreateIndexResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexResponse; +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.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.security.spi.Resource; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; + +/** + * Transport action for CreateSampleResource. + */ +public class CreateResourceTransportAction extends HandledTransportAction< + CreateResourceRequest, + CreateResourceResponse> { + private static final Logger log = LogManager.getLogger(CreateResourceTransportAction.class); + + private final TransportService transportService; + private final Client nodeClient; + private final String resourceIndex; + + public CreateResourceTransportAction( + TransportService transportService, + ActionFilters actionFilters, + Client nodeClient, + String actionName, + String resourceIndex, + Writeable.Reader resourceReader + ) { + super(actionName, transportService, actionFilters, (in) -> new CreateResourceRequest(in, resourceReader)); + this.transportService = transportService; + this.nodeClient = nodeClient; + this.resourceIndex = resourceIndex; + } + + @Override + protected void doExecute(Task task, CreateResourceRequest request, ActionListener listener) { + try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { + CreateIndexRequest cir = new CreateIndexRequest(resourceIndex); + ActionListener cirListener = ActionListener.wrap( + response -> { createResource(request, listener); }, + (failResponse) -> { + /* Index already exists, ignore and continue */ + createResource(request, listener); + } + ); + nodeClient.admin().indices().create(cir, cirListener); + } + } + + private void createResource(CreateResourceRequest request, ActionListener listener) { + log.warn("Sample name: " + request.getResource()); + Resource sample = request.getResource(); + try { + IndexRequest ir = nodeClient.prepareIndex(resourceIndex) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .setSource(sample.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) + .request(); + + log.warn("Index Request: " + ir.toString()); + + ActionListener irListener = ActionListener.wrap(idxResponse -> { + listener.onResponse(new CreateResourceResponse("Created resource: " + idxResponse.toString())); + }, listener::onFailure); + nodeClient.index(ir, irListener); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} From e14532f8efc06b1526873e02efd000a995abca9e Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 15 Aug 2024 16:54:45 -0400 Subject: [PATCH 13/20] Create ResourceSharingUtils Signed-off-by: Craig Perkins --- .../resource/ResourceSharingUtils.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java new file mode 100644 index 0000000000..2b3f0744db --- /dev/null +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java @@ -0,0 +1,26 @@ +package org.opensearch.security.sampleextension.resource; + +import org.opensearch.OpenSearchSecurityException; +import org.opensearch.threadpool.ThreadPool; + +public class ResourceSharingUtils { + private static final ResourceSharingUtils INSTANCE = new ResourceSharingUtils(); + + public static final String RESOURCE_SHARING_INDEX = ".resource-sharing"; + + private ThreadPool threadPool; + + private ResourceSharingUtils() {} + + // Public method to provide access to the singleton instance + public static ResourceSharingUtils getInstance() { + return ResourceSharingUtils.INSTANCE; + } + + public void initialize(ThreadPool threadPool) { + if (threadPool != null) { + throw new OpenSearchSecurityException("ResourceSharingUtils can only be initialized once."); + } + this.threadPool = threadPool; + } +} From c0c8db1ee7f282b4b4c8011d5e5c0fdc2a3955c1 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 15 Aug 2024 17:14:14 -0400 Subject: [PATCH 14/20] Create .resource-sharing index Signed-off-by: Craig Perkins --- .../resource/ResourceSharingUtils.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java index 2b3f0744db..9ed643d4a9 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java @@ -1,9 +1,19 @@ package org.opensearch.security.sampleextension.resource; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import org.opensearch.OpenSearchSecurityException; +import org.opensearch.action.admin.indices.create.CreateIndexRequest; +import org.opensearch.action.admin.indices.create.CreateIndexResponse; +import org.opensearch.client.Client; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; import org.opensearch.threadpool.ThreadPool; public class ResourceSharingUtils { + private final static Logger log = LogManager.getLogger(ResourceSharingUtils.class); + private static final ResourceSharingUtils INSTANCE = new ResourceSharingUtils(); public static final String RESOURCE_SHARING_INDEX = ".resource-sharing"; @@ -17,10 +27,22 @@ public static ResourceSharingUtils getInstance() { return ResourceSharingUtils.INSTANCE; } - public void initialize(ThreadPool threadPool) { - if (threadPool != null) { + public void initialize(ThreadPool threadPool, Client client) { + if (this.threadPool != null) { throw new OpenSearchSecurityException("ResourceSharingUtils can only be initialized once."); } this.threadPool = threadPool; + + try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) { + CreateIndexRequest cir = new CreateIndexRequest(RESOURCE_SHARING_INDEX); + ActionListener cirListener = ActionListener.wrap( + response -> { log.info(RESOURCE_SHARING_INDEX + " created."); }, + (failResponse) -> { + /* Index already exists, ignore and continue */ + log.info(RESOURCE_SHARING_INDEX + " exists."); + } + ); + client.admin().indices().create(cir, cirListener); + } } } From efb6937ebce9480ae50db514e98fe62a4624a4cb Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 15 Aug 2024 18:00:15 -0400 Subject: [PATCH 15/20] Initialize ResourceSharingUtils Signed-off-by: Craig Perkins --- .../SampleExtensionPlugin.java | 2 ++ .../security/spi}/ResourceSharingUtils.java | 27 ++++++++++++++----- .../CreateResourceTransportAction.java | 2 ++ .../security/OpenSearchSecurityPlugin.java | 3 +++ 4 files changed, 27 insertions(+), 7 deletions(-) rename {sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource => spi/src/main/java/org/opensearch/security/spi}/ResourceSharingUtils.java (69%) diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java index 0c7162c0e9..a38cf18ab8 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/SampleExtensionPlugin.java @@ -44,6 +44,7 @@ import org.opensearch.security.sampleextension.actions.list.ListSampleResourceAction; import org.opensearch.security.sampleextension.actions.list.ListSampleResourceRestAction; import org.opensearch.security.sampleextension.actions.list.ListSampleResourceTransportAction; +import org.opensearch.security.spi.ResourceSharingUtils; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; @@ -75,6 +76,7 @@ public Collection createComponents( Supplier repositoriesServiceSupplier ) { this.client = client; + ResourceSharingUtils.getInstance().initialize(threadPool, client); return Collections.emptyList(); } diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java similarity index 69% rename from sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java rename to spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java index 9ed643d4a9..8d398fd64f 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/ResourceSharingUtils.java +++ b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java @@ -1,9 +1,8 @@ -package org.opensearch.security.sampleextension.resource; +package org.opensearch.security.spi; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.OpenSearchSecurityException; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; import org.opensearch.client.Client; @@ -18,22 +17,31 @@ public class ResourceSharingUtils { public static final String RESOURCE_SHARING_INDEX = ".resource-sharing"; + private boolean initialized; private ThreadPool threadPool; + private Client client; private ResourceSharingUtils() {} - // Public method to provide access to the singleton instance public static ResourceSharingUtils getInstance() { return ResourceSharingUtils.INSTANCE; } public void initialize(ThreadPool threadPool, Client client) { - if (this.threadPool != null) { - throw new OpenSearchSecurityException("ResourceSharingUtils can only be initialized once."); + if (initialized) { + return; } + initialized = true; this.threadPool = threadPool; + this.client = client; + } + + public boolean isInitialized() { + return initialized; + } - try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) { + private void createResourceSharingIndexIfNotExists() { + try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) { CreateIndexRequest cir = new CreateIndexRequest(RESOURCE_SHARING_INDEX); ActionListener cirListener = ActionListener.wrap( response -> { log.info(RESOURCE_SHARING_INDEX + " created."); }, @@ -42,7 +50,12 @@ public void initialize(ThreadPool threadPool, Client client) { log.info(RESOURCE_SHARING_INDEX + " exists."); } ); - client.admin().indices().create(cir, cirListener); + this.client.admin().indices().create(cir, cirListener); } } + + public boolean indexResourceSharing(Resource resource) { + createResourceSharingIndexIfNotExists(); + return true; + } } diff --git a/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java index 5fbab4c4f9..0c7230cb9f 100644 --- a/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java +++ b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java @@ -26,6 +26,7 @@ import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.core.xcontent.ToXContent; import org.opensearch.security.spi.Resource; +import org.opensearch.security.spi.ResourceSharingUtils; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -75,6 +76,7 @@ protected void doExecute(Task task, CreateResourceRequest request, ActionList private void createResource(CreateResourceRequest request, ActionListener listener) { log.warn("Sample name: " + request.getResource()); Resource sample = request.getResource(); + ResourceSharingUtils.getInstance().indexResourceSharing(sample); try { IndexRequest ir = nodeClient.prepareIndex(resourceIndex) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 509b98f12e..c5af70b99b 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -178,6 +178,7 @@ import org.opensearch.security.securityconf.DynamicConfigFactory; import org.opensearch.security.setting.OpensearchDynamicSetting; import org.opensearch.security.setting.TransportPassiveAuthSetting; +import org.opensearch.security.spi.ResourceSharingUtils; import org.opensearch.security.ssl.ExternalSecurityKeyStore; import org.opensearch.security.ssl.OpenSearchSecureSettingsFactory; import org.opensearch.security.ssl.OpenSearchSecuritySSLPlugin; @@ -1056,6 +1057,8 @@ public Collection createComponents( return components; } + ResourceSharingUtils.getInstance().initialize(threadPool, localClient); + // Register opensearch dynamic settings transportPassiveAuthSetting.registerClusterSettingsChangeListener(clusterService.getClusterSettings()); From 5cf03159b3a0b7c453faf0bf4269ea415e7dda44 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 15 Aug 2024 22:08:10 -0400 Subject: [PATCH 16/20] Create .resource-sharing entry Signed-off-by: Craig Perkins --- .../ListSampleResourceTransportAction.java | 4 +- .../org/opensearch/security/spi/Resource.java | 4 +- .../security/spi/ResourceSharingEntry.java | 27 ++++++++++ .../security/spi/ResourceSharingUtils.java | 53 +++++++++++++++---- .../opensearch/security/spi/ShareWith.java | 40 ++++++++++++++ .../CreateResourceTransportAction.java | 15 +++++- 6 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 spi/src/main/java/org/opensearch/security/spi/ResourceSharingEntry.java create mode 100644 spi/src/main/java/org/opensearch/security/spi/ShareWith.java diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.java index 8f102eef14..8160fe2854 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/list/ListSampleResourceTransportAction.java @@ -21,8 +21,6 @@ import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; -import static org.opensearch.security.sampleextension.SampleExtensionPlugin.RESOURCE_INDEX_NAME; - /** * Transport action for ListSampleResource. */ @@ -40,7 +38,7 @@ public ListSampleResourceTransportAction(TransportService transportService, Acti @Override protected void doExecute(Task task, ListSampleResourceRequest request, ActionListener listener) { try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { - SearchRequest sr = new SearchRequest(RESOURCE_INDEX_NAME); + SearchRequest sr = new SearchRequest(".resource-sharing"); SearchSourceBuilder matchAllQuery = new SearchSourceBuilder(); matchAllQuery.query(new MatchAllQueryBuilder()); sr.source(matchAllQuery); diff --git a/spi/src/main/java/org/opensearch/security/spi/Resource.java b/spi/src/main/java/org/opensearch/security/spi/Resource.java index 39482b4232..98d2b9958d 100644 --- a/spi/src/main/java/org/opensearch/security/spi/Resource.java +++ b/spi/src/main/java/org/opensearch/security/spi/Resource.java @@ -3,4 +3,6 @@ import org.opensearch.core.common.io.stream.NamedWriteable; import org.opensearch.core.xcontent.ToXContentFragment; -public abstract class Resource implements NamedWriteable, ToXContentFragment {} +public abstract class Resource implements NamedWriteable, ToXContentFragment { + protected abstract String getResourceIndex(); +} diff --git a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingEntry.java b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingEntry.java new file mode 100644 index 0000000000..dc3df82c75 --- /dev/null +++ b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingEntry.java @@ -0,0 +1,27 @@ +package org.opensearch.security.spi; + +import java.io.IOException; + +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +public class ResourceSharingEntry implements ToXContentFragment { + private final String resourceIndex; + private final String resourceId; + private final ShareWith shareWith; + + public ResourceSharingEntry(String resourceIndex, String resourceId, ShareWith shareWith) { + this.resourceIndex = resourceIndex; + this.resourceId = resourceId; + this.shareWith = shareWith; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject() + .field("resource_index", resourceIndex) + .field("resource_id", resourceId) + .field("share_with", shareWith) + .endObject(); + } +} diff --git a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java index 8d398fd64f..3cd81eccde 100644 --- a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java +++ b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java @@ -1,15 +1,24 @@ package org.opensearch.security.spi; +import java.io.IOException; +import java.util.concurrent.Callable; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.ToXContent; import org.opensearch.threadpool.ThreadPool; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; + public class ResourceSharingUtils { private final static Logger log = LogManager.getLogger(ResourceSharingUtils.class); @@ -40,22 +49,48 @@ public boolean isInitialized() { return initialized; } - private void createResourceSharingIndexIfNotExists() { + private void createResourceSharingIndexIfNotExists(Callable callable) { try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) { CreateIndexRequest cir = new CreateIndexRequest(RESOURCE_SHARING_INDEX); - ActionListener cirListener = ActionListener.wrap( - response -> { log.info(RESOURCE_SHARING_INDEX + " created."); }, - (failResponse) -> { - /* Index already exists, ignore and continue */ - log.info(RESOURCE_SHARING_INDEX + " exists."); + ActionListener cirListener = ActionListener.wrap(response -> { + log.warn(RESOURCE_SHARING_INDEX + " created."); + callable.call(); + }, (failResponse) -> { + /* Index already exists, ignore and continue */ + log.warn(RESOURCE_SHARING_INDEX + " exists."); + try { + callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); } - ); + }); this.client.admin().indices().create(cir, cirListener); } } - public boolean indexResourceSharing(Resource resource) { - createResourceSharingIndexIfNotExists(); + public boolean indexResourceSharing(String resourceId, Resource resource, ShareWith shareWith, ActionListener listener) + throws IOException { + createResourceSharingIndexIfNotExists(() -> { + ResourceSharingEntry entry = new ResourceSharingEntry(resource.getResourceIndex(), resourceId, shareWith); + + IndexRequest ir = client.prepareIndex(RESOURCE_SHARING_INDEX) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .setSource(entry.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) + .request(); + + log.warn("Index Request: " + ir.toString()); + + ActionListener irListener = ActionListener.wrap(idxResponse -> { + log.warn("Created " + RESOURCE_SHARING_INDEX + " entry."); + listener.onResponse(idxResponse); + }, (failResponse) -> { + log.error(failResponse.getMessage()); + log.error("Failed to create " + RESOURCE_SHARING_INDEX + " entry."); + listener.onFailure(failResponse); + }); + client.index(ir, irListener); + return null; + }); return true; } } diff --git a/spi/src/main/java/org/opensearch/security/spi/ShareWith.java b/spi/src/main/java/org/opensearch/security/spi/ShareWith.java new file mode 100644 index 0000000000..c594310887 --- /dev/null +++ b/spi/src/main/java/org/opensearch/security/spi/ShareWith.java @@ -0,0 +1,40 @@ +package org.opensearch.security.spi; + +import java.io.IOException; +import java.util.List; + +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +public class ShareWith implements ToXContentFragment { + + public final static ShareWith PRIVATE = new ShareWith(List.of(), List.of(), List.of()); + public final static ShareWith PUBLIC = new ShareWith(List.of("*"), List.of("*"), List.of("*")); + + private final List users; + private final List roles; + private final List backendRoles; + + public ShareWith(List users, List roles, List backendRoles) { + this.users = users; + this.roles = roles; + this.backendRoles = backendRoles; + } + + public List getUsers() { + return users; + } + + public List getRoles() { + return roles; + } + + public List getBackendRoles() { + return backendRoles; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().field("users", users).field("roles", roles).field("backendRoles", backendRoles).endObject(); + } +} diff --git a/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java index 0c7230cb9f..b445e42056 100644 --- a/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java +++ b/spi/src/main/java/org/opensearch/security/spi/actions/CreateResourceTransportAction.java @@ -27,6 +27,7 @@ import org.opensearch.core.xcontent.ToXContent; import org.opensearch.security.spi.Resource; import org.opensearch.security.spi.ResourceSharingUtils; +import org.opensearch.security.spi.ShareWith; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -76,7 +77,6 @@ protected void doExecute(Task task, CreateResourceRequest request, ActionList private void createResource(CreateResourceRequest request, ActionListener listener) { log.warn("Sample name: " + request.getResource()); Resource sample = request.getResource(); - ResourceSharingUtils.getInstance().indexResourceSharing(sample); try { IndexRequest ir = nodeClient.prepareIndex(resourceIndex) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) @@ -85,8 +85,19 @@ private void createResource(CreateResourceRequest request, ActionListener resourceSharingListener = ActionListener.wrap(resourceSharingResponse -> { + listener.onResponse(new CreateResourceResponse("Created resource: " + resourceSharingResponse.toString())); + }, listener::onFailure); + ActionListener irListener = ActionListener.wrap(idxResponse -> { - listener.onResponse(new CreateResourceResponse("Created resource: " + idxResponse.toString())); + // TODO Make sure security is an IndexOperationListener and automatically create entry in .resource-sharing + // and set to private + + // Idea, if not entry in .resource-sharing then assume private? Maybe its not necessary to be an IndexOperationListener + log.info("Created resource: " + idxResponse.toString()); + ResourceSharingUtils.getInstance() + .indexResourceSharing(idxResponse.getId(), sample, ShareWith.PUBLIC, resourceSharingListener); + // listener.onResponse(new CreateResourceResponse("Created resource: " + idxResponse.toString())); }, listener::onFailure); nodeClient.index(ir, irListener); } catch (IOException e) { From fb23e6ba9da2572619b72ecbb25f7c36f390de95 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 15 Aug 2024 22:54:18 -0400 Subject: [PATCH 17/20] Truly singleton Signed-off-by: Craig Perkins --- sample-extension-plugin/build.gradle | 20 +++++++++---------- .../plugin-metadata/plugin-security.policy | 3 +++ .../security/spi/ResourceSharingUtils.java | 12 +++++++++-- 3 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 sample-extension-plugin/src/main/plugin-metadata/plugin-security.policy diff --git a/sample-extension-plugin/build.gradle b/sample-extension-plugin/build.gradle index 7420afb315..1920e21dff 100644 --- a/sample-extension-plugin/build.gradle +++ b/sample-extension-plugin/build.gradle @@ -176,13 +176,13 @@ run { // The security zip is added explicitly above but the sample-extension-plugin is added implicitly at some time during evaluation. // Will need to do a deep dive to find out exactly what task adds the sample-extension-plugin and add security there but a temporary hack is to // reorder the plugins list after evaluation but prior to task execution when the plugins are installed. -afterEvaluate { - testClusters.javaRestTest.nodes.each { node -> - def nodePlugins = node.plugins - def firstPlugin = nodePlugins.get(0) - if (firstPlugin.provider == project.bundlePlugin.archiveFile) { - nodePlugins.remove(0) - nodePlugins.add(firstPlugin) - } - } -} +//afterEvaluate { +// testClusters.javaRestTest.nodes.each { node -> +// def nodePlugins = node.plugins +// def firstPlugin = nodePlugins.get(0) +// if (firstPlugin.provider == project.bundlePlugin.archiveFile) { +// nodePlugins.remove(0) +// nodePlugins.add(firstPlugin) +// } +// } +//} diff --git a/sample-extension-plugin/src/main/plugin-metadata/plugin-security.policy b/sample-extension-plugin/src/main/plugin-metadata/plugin-security.policy new file mode 100644 index 0000000000..a5dfc33a87 --- /dev/null +++ b/sample-extension-plugin/src/main/plugin-metadata/plugin-security.policy @@ -0,0 +1,3 @@ +grant { + permission java.lang.RuntimePermission "getClassLoader"; +}; \ No newline at end of file diff --git a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java index 3cd81eccde..46f4e0c9ff 100644 --- a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java +++ b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java @@ -1,7 +1,11 @@ package org.opensearch.security.spi; import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -22,7 +26,7 @@ public class ResourceSharingUtils { private final static Logger log = LogManager.getLogger(ResourceSharingUtils.class); - private static final ResourceSharingUtils INSTANCE = new ResourceSharingUtils(); + private static final Map instances = new ConcurrentHashMap<>(); public static final String RESOURCE_SHARING_INDEX = ".resource-sharing"; @@ -33,7 +37,11 @@ public class ResourceSharingUtils { private ResourceSharingUtils() {} public static ResourceSharingUtils getInstance() { - return ResourceSharingUtils.INSTANCE; + ClassLoader classLoader = AccessController.doPrivileged( + (PrivilegedAction) () -> Thread.currentThread().getContextClassLoader() + ); + instances.computeIfAbsent(classLoader, cl -> new ResourceSharingUtils()); + return instances.get(classLoader); } public void initialize(ThreadPool threadPool, Client client) { From 233f3995b3651994d3fe79b447cc388241ba6a4d Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 16 Aug 2024 12:16:54 -0400 Subject: [PATCH 18/20] IndexOperationListener Signed-off-by: Craig Perkins --- sample-extension-plugin/build.gradle | 4 ++-- .../create/CreateSampleResourceRequest.java | 1 - .../CreateSampleResourceRestAction.java | 4 ++-- .../CreateSampleResourceTransportAction.java | 1 - .../create}/SampleResource.java | 14 +++++++------ ...arch.security.spi.ResourceSharingExtension | 2 +- .../security/spi/ResourceSharingUtils.java | 16 ++++++++++++++- .../security/OpenSearchSecurityPlugin.java | 20 ++++++++++++++++++- 8 files changed, 47 insertions(+), 15 deletions(-) rename sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/{resource => actions/create}/SampleResource.java (84%) diff --git a/sample-extension-plugin/build.gradle b/sample-extension-plugin/build.gradle index 1920e21dff..374e2978e9 100644 --- a/sample-extension-plugin/build.gradle +++ b/sample-extension-plugin/build.gradle @@ -18,7 +18,7 @@ opensearchplugin { name 'opensearch-security-sample-extension' description 'Sample plugin that extends OpenSearch Security Resource Sharing plugin' classname 'org.opensearch.security.sampleextension.SampleExtensionPlugin' - // extendedPlugins = ['opensearch-security'] + extendedPlugins = ['opensearch-security'] } ext { @@ -34,7 +34,7 @@ repositories { } dependencies { - implementation project(path: ":${rootProject.name}-spi", configuration: 'shadow') + compileOnly project(path: ":${rootProject.name}-spi", configuration: 'shadow') } def es_tmp_dir = rootProject.file('build/private/es_tmp').absoluteFile diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java index c5e1ef8166..aaeb92c640 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRequest.java @@ -14,7 +14,6 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; -import org.opensearch.security.sampleextension.resource.SampleResource; import org.opensearch.security.spi.Resource; /** diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java index d7cdf0df1b..72b582a508 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceRestAction.java @@ -17,7 +17,6 @@ import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; -import org.opensearch.security.sampleextension.resource.SampleResource; import org.opensearch.security.spi.actions.CreateResourceRequest; import static java.util.Collections.singletonList; @@ -45,7 +44,8 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client } String name = (String) source.get("name"); - SampleResource resource = new SampleResource(name); + SampleResource resource = new SampleResource(); + resource.setName(name); final CreateResourceRequest createSampleResourceRequest = new CreateResourceRequest<>(resource); return channel -> client.executeLocally( CreateSampleResourceAction.INSTANCE, diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java index f4b2a90fa0..d62cb7f35a 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/CreateSampleResourceTransportAction.java @@ -14,7 +14,6 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.client.Client; import org.opensearch.common.inject.Inject; -import org.opensearch.security.sampleextension.resource.SampleResource; import org.opensearch.security.spi.actions.CreateResourceTransportAction; import org.opensearch.transport.TransportService; diff --git a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/SampleResource.java similarity index 84% rename from sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java rename to sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/SampleResource.java index 4b21953f83..7cff71bdff 100644 --- a/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/resource/SampleResource.java +++ b/sample-extension-plugin/src/main/java/org/opensearch/security/sampleextension/actions/create/SampleResource.java @@ -1,4 +1,4 @@ -package org.opensearch.security.sampleextension.resource; +package org.opensearch.security.sampleextension.actions.create; import java.io.IOException; @@ -12,13 +12,11 @@ public class SampleResource extends Resource implements ResourceSharingExtension { - private final String name; + private String name; - public SampleResource(String name) { - this.name = name; - } + public SampleResource() {} - public SampleResource(StreamInput in) throws IOException { + SampleResource(StreamInput in) throws IOException { this.name = in.readString(); } @@ -46,4 +44,8 @@ public void writeTo(StreamOutput streamOutput) throws IOException { public String getWriteableName() { return "sampled_resource"; } + + public void setName(String name) { + this.name = name; + } } diff --git a/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension b/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension index 904fb99e99..8be61d03af 100644 --- a/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension +++ b/sample-extension-plugin/src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension @@ -3,4 +3,4 @@ # SPDX-License-Identifier: Apache-2.0 # -org.opensearch.security.sampleextension.resource.SampleResource \ No newline at end of file +org.opensearch.security.sampleextension.actions.create.SampleResource \ No newline at end of file diff --git a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java index 46f4e0c9ff..3c8ba1992e 100644 --- a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java +++ b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java @@ -18,12 +18,15 @@ import org.opensearch.client.Client; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.index.engine.Engine; +import org.opensearch.index.shard.IndexingOperationListener; import org.opensearch.threadpool.ThreadPool; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -public class ResourceSharingUtils { +public class ResourceSharingUtils implements IndexingOperationListener { private final static Logger log = LogManager.getLogger(ResourceSharingUtils.class); private static final Map instances = new ConcurrentHashMap<>(); @@ -101,4 +104,15 @@ public boolean indexResourceSharing(String resourceId, Resource resource, ShareW }); return true; } + + @Override + public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult result) { + log.warn("postIndex called on " + shardId.getIndexName()); + + } + + @Override + public void postDelete(ShardId shardId, Engine.Delete delete, Engine.DeleteResult result) { + log.warn("postDelete called on " + shardId.getIndexName()); + } } diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index c5af70b99b..96ecb3cd2c 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -116,6 +116,7 @@ import org.opensearch.indices.IndicesService; import org.opensearch.indices.SystemIndexDescriptor; import org.opensearch.plugins.ClusterPlugin; +import org.opensearch.plugins.ExtensiblePlugin; import org.opensearch.plugins.ExtensionAwarePlugin; import org.opensearch.plugins.IdentityPlugin; import org.opensearch.plugins.MapperPlugin; @@ -178,6 +179,7 @@ import org.opensearch.security.securityconf.DynamicConfigFactory; import org.opensearch.security.setting.OpensearchDynamicSetting; import org.opensearch.security.setting.TransportPassiveAuthSetting; +import org.opensearch.security.spi.ResourceSharingExtension; import org.opensearch.security.spi.ResourceSharingUtils; import org.opensearch.security.ssl.ExternalSecurityKeyStore; import org.opensearch.security.ssl.OpenSearchSecureSettingsFactory; @@ -229,6 +231,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin ClusterPlugin, MapperPlugin, // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings + ExtensiblePlugin, ExtensionAwarePlugin, IdentityPlugin // CS-ENFORCE-SINGLE @@ -266,6 +269,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin private volatile Salt salt; private volatile OpensearchDynamicSetting transportPassiveAuthSetting; private volatile PasswordHasher passwordHasher; + private final Set indicesToListen = new HashSet<>(); public static boolean isActionTraceEnabled() { @@ -705,6 +709,10 @@ public void onIndexModule(IndexModule indexModule) { salt ) ); + if (this.indicesToListen.contains(indexModule.getIndex().getName())) { + indexModule.addIndexOperationListener(ResourceSharingUtils.getInstance()); + log.warn("Security started listening to operations on index {}", indexModule.getIndex().getName()); + } indexModule.forceQueryCacheProvider((indexSettings, nodeCache) -> new QueryCache() { @Override @@ -1029,7 +1037,6 @@ public Collection createComponents( IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier ) { - SSLConfig.registerClusterSettingsChangeListener(clusterService.getClusterSettings()); if (SSLConfig.isSslOnlyMode()) { return super.createComponents( @@ -2120,6 +2127,17 @@ public Optional getSecureSettingFactory(Settings settings return Optional.of(new OpenSearchSecureSettingsFactory(threadPool, sks, sslExceptionHandler, securityRestHandler)); } + // CS-SUPPRESS-SINGLE: RegexpSingleline SPI Extensions are unrelated to OpenSearch extensions + @Override + public void loadExtensions(ExtensiblePlugin.ExtensionLoader loader) { + for (ResourceSharingExtension extension : loader.loadExtensions(ResourceSharingExtension.class)) { + String resourceIndexName = extension.getResourceIndex(); + this.indicesToListen.add(resourceIndexName); + log.warn("Loaded resource, index: {}", resourceIndexName); + } + } + // CS-ENFORCE-SINGLE + @SuppressWarnings("removal") private void tryAddSecurityProvider() { final SecurityManager sm = System.getSecurityManager(); From 7c44fac2eb66189e99d39cd54a75d9ada226a82d Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 19 Aug 2024 15:43:37 -0400 Subject: [PATCH 19/20] WIP Signed-off-by: Craig Perkins --- .../security/spi/ResourceSharingUtils.java | 16 +----- .../security/OpenSearchSecurityPlugin.java | 3 +- .../resource/ResourceSharingListener.java | 52 +++++++++++++++++++ .../security/user/UserFragment.java | 23 ++++++++ 4 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/opensearch/security/resource/ResourceSharingListener.java create mode 100644 src/main/java/org/opensearch/security/user/UserFragment.java diff --git a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java index 3c8ba1992e..46f4e0c9ff 100644 --- a/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java +++ b/spi/src/main/java/org/opensearch/security/spi/ResourceSharingUtils.java @@ -18,15 +18,12 @@ import org.opensearch.client.Client; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; -import org.opensearch.core.index.shard.ShardId; import org.opensearch.core.xcontent.ToXContent; -import org.opensearch.index.engine.Engine; -import org.opensearch.index.shard.IndexingOperationListener; import org.opensearch.threadpool.ThreadPool; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -public class ResourceSharingUtils implements IndexingOperationListener { +public class ResourceSharingUtils { private final static Logger log = LogManager.getLogger(ResourceSharingUtils.class); private static final Map instances = new ConcurrentHashMap<>(); @@ -104,15 +101,4 @@ public boolean indexResourceSharing(String resourceId, Resource resource, ShareW }); return true; } - - @Override - public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult result) { - log.warn("postIndex called on " + shardId.getIndexName()); - - } - - @Override - public void postDelete(ShardId shardId, Engine.Delete delete, Engine.DeleteResult result) { - log.warn("postDelete called on " + shardId.getIndexName()); - } } diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 96ecb3cd2c..94a52b1d91 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -170,6 +170,7 @@ import org.opensearch.security.privileges.PrivilegesInterceptor; import org.opensearch.security.privileges.RestLayerPrivilegesEvaluator; import org.opensearch.security.resolver.IndexResolverReplacer; +import org.opensearch.security.resource.ResourceSharingListener; import org.opensearch.security.rest.DashboardsInfoAction; import org.opensearch.security.rest.SecurityConfigUpdateAction; import org.opensearch.security.rest.SecurityHealthAction; @@ -710,7 +711,7 @@ public void onIndexModule(IndexModule indexModule) { ) ); if (this.indicesToListen.contains(indexModule.getIndex().getName())) { - indexModule.addIndexOperationListener(ResourceSharingUtils.getInstance()); + indexModule.addIndexOperationListener(ResourceSharingListener.getInstance()); log.warn("Security started listening to operations on index {}", indexModule.getIndex().getName()); } indexModule.forceQueryCacheProvider((indexSettings, nodeCache) -> new QueryCache() { diff --git a/src/main/java/org/opensearch/security/resource/ResourceSharingListener.java b/src/main/java/org/opensearch/security/resource/ResourceSharingListener.java new file mode 100644 index 0000000000..ca54e99310 --- /dev/null +++ b/src/main/java/org/opensearch/security/resource/ResourceSharingListener.java @@ -0,0 +1,52 @@ +package org.opensearch.security.resource; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.client.Client; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.index.engine.Engine; +import org.opensearch.index.shard.IndexingOperationListener; +import org.opensearch.threadpool.ThreadPool; + +public class ResourceSharingListener implements IndexingOperationListener { + private final static Logger log = LogManager.getLogger(ResourceSharingListener.class); + + private static final ResourceSharingListener INSTANCE = new ResourceSharingListener(); + + private boolean initialized; + private ThreadPool threadPool; + private Client client; + + private ResourceSharingListener() {} + + public static ResourceSharingListener getInstance() { + return ResourceSharingListener.INSTANCE; + } + + public void initialize(ThreadPool threadPool, Client client) { + if (initialized) { + return; + } + initialized = true; + this.threadPool = threadPool; + this.client = client; + } + + public boolean isInitialized() { + return initialized; + } + + @Override + public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult result) { + log.warn("postIndex called on " + shardId.getIndexName()); + String resourceId = index.id(); + String resourceIndex = shardId.getIndexName(); + + } + + @Override + public void postDelete(ShardId shardId, Engine.Delete delete, Engine.DeleteResult result) { + log.warn("postDelete called on " + shardId.getIndexName()); + } +} diff --git a/src/main/java/org/opensearch/security/user/UserFragment.java b/src/main/java/org/opensearch/security/user/UserFragment.java new file mode 100644 index 0000000000..d42413e589 --- /dev/null +++ b/src/main/java/org/opensearch/security/user/UserFragment.java @@ -0,0 +1,23 @@ +package org.opensearch.security.user; + +import java.io.IOException; + +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +public class UserFragment implements ToXContentFragment { + private final User user; + + public UserFragment(User user) { + this.user = user; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject() + .field("username", user.getName()) + .field("roles", user.getSecurityRoles()) + .field("backend_roles", user.getRoles()) + .endObject(); + } +} From db678cc212e94f188d4c2c4d2337d51d328373cb Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 20 Sep 2024 12:04:42 -0400 Subject: [PATCH 20/20] Fix build issues Signed-off-by: Craig Perkins --- spi/build.gradle | 2 +- .../security/resource/ResourceSharingListener.java | 11 +++++++++++ .../org/opensearch/security/user/UserFragment.java | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/spi/build.gradle b/spi/build.gradle index d36704609e..a0613e5226 100644 --- a/spi/build.gradle +++ b/spi/build.gradle @@ -11,7 +11,7 @@ plugins { id 'jacoco' id 'maven-publish' id 'signing' - id "org.gradle.test-retry" version "1.5.10" + id "org.gradle.test-retry" version "1.6.0" } apply plugin: 'opensearch.java' diff --git a/src/main/java/org/opensearch/security/resource/ResourceSharingListener.java b/src/main/java/org/opensearch/security/resource/ResourceSharingListener.java index ca54e99310..bdf333a844 100644 --- a/src/main/java/org/opensearch/security/resource/ResourceSharingListener.java +++ b/src/main/java/org/opensearch/security/resource/ResourceSharingListener.java @@ -1,3 +1,14 @@ +/* + * 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.resource; import org.apache.logging.log4j.LogManager; diff --git a/src/main/java/org/opensearch/security/user/UserFragment.java b/src/main/java/org/opensearch/security/user/UserFragment.java index d42413e589..b0c9164404 100644 --- a/src/main/java/org/opensearch/security/user/UserFragment.java +++ b/src/main/java/org/opensearch/security/user/UserFragment.java @@ -1,3 +1,14 @@ +/* + * 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.user; import java.io.IOException;