-
Notifications
You must be signed in to change notification settings - Fork 280
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a sample resource plugin to demonstrate resource access control …
…in action Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
- Loading branch information
1 parent
9dfd5a0
commit 561e294
Showing
35 changed files
with
1,628 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/* | ||
* 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 | ||
|
||
|
||
opensearchplugin { | ||
name 'opensearch-sample-resource-plugin' | ||
description 'Sample plugin that extends OpenSearch Resource Plugin' | ||
classname 'org.opensearch.sample.SampleResourcePlugin' | ||
} | ||
|
||
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 { | ||
} | ||
|
||
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 | ||
} |
19 changes: 19 additions & 0 deletions
19
sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* | ||
* 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.sample; | ||
|
||
import org.opensearch.core.common.io.stream.NamedWriteable; | ||
import org.opensearch.core.xcontent.ToXContentFragment; | ||
|
||
public abstract class Resource implements NamedWriteable, ToXContentFragment { | ||
protected abstract String getResourceIndex(); | ||
} |
182 changes: 182 additions & 0 deletions
182
sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/* | ||
* 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.sample; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Supplier; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
import org.opensearch.accesscontrol.resources.ResourceService; | ||
import org.opensearch.action.ActionRequest; | ||
import org.opensearch.client.Client; | ||
import org.opensearch.cluster.metadata.IndexNameExpressionResolver; | ||
import org.opensearch.cluster.node.DiscoveryNodes; | ||
import org.opensearch.cluster.service.ClusterService; | ||
import org.opensearch.common.inject.Inject; | ||
import org.opensearch.common.lifecycle.Lifecycle; | ||
import org.opensearch.common.lifecycle.LifecycleComponent; | ||
import org.opensearch.common.lifecycle.LifecycleListener; | ||
import org.opensearch.common.settings.ClusterSettings; | ||
import org.opensearch.common.settings.IndexScopedSettings; | ||
import org.opensearch.common.settings.Settings; | ||
import org.opensearch.common.settings.SettingsFilter; | ||
import org.opensearch.core.action.ActionResponse; | ||
import org.opensearch.core.common.io.stream.NamedWriteableRegistry; | ||
import org.opensearch.core.xcontent.NamedXContentRegistry; | ||
import org.opensearch.env.Environment; | ||
import org.opensearch.env.NodeEnvironment; | ||
import org.opensearch.indices.SystemIndexDescriptor; | ||
import org.opensearch.plugins.ActionPlugin; | ||
import org.opensearch.plugins.Plugin; | ||
import org.opensearch.plugins.ResourcePlugin; | ||
import org.opensearch.plugins.SystemIndexPlugin; | ||
import org.opensearch.repositories.RepositoriesService; | ||
import org.opensearch.rest.RestController; | ||
import org.opensearch.rest.RestHandler; | ||
import org.opensearch.sample.actions.create.CreateResourceAction; | ||
import org.opensearch.sample.actions.create.CreateResourceRestAction; | ||
import org.opensearch.sample.actions.list.ListAccessibleResourcesAction; | ||
import org.opensearch.sample.actions.list.ListAccessibleResourcesRestAction; | ||
import org.opensearch.sample.actions.share.ShareResourceAction; | ||
import org.opensearch.sample.actions.share.ShareResourceRestAction; | ||
import org.opensearch.sample.actions.verify.VerifyResourceAccessAction; | ||
import org.opensearch.sample.actions.verify.VerifyResourceAccessRestAction; | ||
import org.opensearch.sample.transport.CreateResourceTransportAction; | ||
import org.opensearch.sample.transport.ListAccessibleResourcesTransportAction; | ||
import org.opensearch.sample.transport.ShareResourceTransportAction; | ||
import org.opensearch.sample.transport.VerifyResourceAccessTransportAction; | ||
import org.opensearch.script.ScriptService; | ||
import org.opensearch.threadpool.ThreadPool; | ||
import org.opensearch.watcher.ResourceWatcherService; | ||
|
||
/** | ||
* Sample Resource plugin. | ||
* It uses ".sample_resources" index to manage its resources, and exposes a REST API | ||
* | ||
*/ | ||
public class SampleResourcePlugin extends Plugin implements ActionPlugin, SystemIndexPlugin, ResourcePlugin { | ||
private static final Logger log = LogManager.getLogger(SampleResourcePlugin.class); | ||
|
||
public static final String RESOURCE_INDEX_NAME = ".sample_resource_sharing_plugin"; | ||
|
||
public final static Map<String, Object> INDEX_SETTINGS = Map.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); | ||
|
||
private Client client; | ||
|
||
@Override | ||
public Collection<Object> createComponents( | ||
Client client, | ||
ClusterService clusterService, | ||
ThreadPool threadPool, | ||
ResourceWatcherService resourceWatcherService, | ||
ScriptService scriptService, | ||
NamedXContentRegistry xContentRegistry, | ||
Environment environment, | ||
NodeEnvironment nodeEnvironment, | ||
NamedWriteableRegistry namedWriteableRegistry, | ||
IndexNameExpressionResolver indexNameExpressionResolver, | ||
Supplier<RepositoriesService> repositoriesServiceSupplier | ||
) { | ||
this.client = client; | ||
log.info("Loaded SampleResourcePlugin components."); | ||
return Collections.emptyList(); | ||
} | ||
|
||
@Override | ||
public List<RestHandler> getRestHandlers( | ||
Settings settings, | ||
RestController restController, | ||
ClusterSettings clusterSettings, | ||
IndexScopedSettings indexScopedSettings, | ||
SettingsFilter settingsFilter, | ||
IndexNameExpressionResolver indexNameExpressionResolver, | ||
Supplier<DiscoveryNodes> nodesInCluster | ||
) { | ||
return List.of( | ||
new CreateResourceRestAction(), | ||
new ListAccessibleResourcesRestAction(), | ||
new VerifyResourceAccessRestAction(), | ||
new ShareResourceRestAction() | ||
); | ||
} | ||
|
||
@Override | ||
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() { | ||
return List.of( | ||
new ActionHandler<>(CreateResourceAction.INSTANCE, CreateResourceTransportAction.class), | ||
new ActionHandler<>(ListAccessibleResourcesAction.INSTANCE, ListAccessibleResourcesTransportAction.class), | ||
new ActionHandler<>(ShareResourceAction.INSTANCE, ShareResourceTransportAction.class), | ||
new ActionHandler<>(VerifyResourceAccessAction.INSTANCE, VerifyResourceAccessTransportAction.class) | ||
); | ||
} | ||
|
||
@Override | ||
public Collection<SystemIndexDescriptor> getSystemIndexDescriptors(Settings settings) { | ||
final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(RESOURCE_INDEX_NAME, "Example index with resources"); | ||
return Collections.singletonList(systemIndexDescriptor); | ||
} | ||
|
||
@Override | ||
public String getResourceType() { | ||
return ""; | ||
} | ||
|
||
@Override | ||
public String getResourceIndex() { | ||
return RESOURCE_INDEX_NAME; | ||
} | ||
|
||
@Override | ||
public Collection<Class<? extends LifecycleComponent>> getGuiceServiceClasses() { | ||
final List<Class<? extends LifecycleComponent>> services = new ArrayList<>(1); | ||
services.add(GuiceHolder.class); | ||
return services; | ||
} | ||
|
||
public static class GuiceHolder implements LifecycleComponent { | ||
|
||
private static ResourceService resourceService; | ||
|
||
@Inject | ||
public GuiceHolder(final ResourceService resourceService) { | ||
GuiceHolder.resourceService = resourceService; | ||
} | ||
|
||
public static ResourceService getResourceService() { | ||
return resourceService; | ||
} | ||
|
||
@Override | ||
public void close() {} | ||
|
||
@Override | ||
public Lifecycle.State lifecycleState() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public void addLifecycleListener(LifecycleListener listener) {} | ||
|
||
@Override | ||
public void removeLifecycleListener(LifecycleListener listener) {} | ||
|
||
@Override | ||
public void start() {} | ||
|
||
@Override | ||
public void stop() {} | ||
|
||
} | ||
} |
Oops, something went wrong.