From deb9c4308e2d9f4d53f5cbaca86cf6fb26226f9d Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 20 Nov 2024 02:39:09 -0500 Subject: [PATCH 01/42] Explicitly create index before ingesting document in ComplianceAuditlogTest.testWriteLogDiffsEnabledAndLogRequestBodyDisabled (#4919) Signed-off-by: Craig Perkins --- .../auditlog/compliance/ComplianceAuditlogTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java index 202b211526..ba5b987533 100644 --- a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java +++ b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java @@ -475,11 +475,15 @@ public void testWriteLogDiffsEnabledAndLogRequestBodyDisabled() throws Exception ); updateAuditConfig(AuditTestUtils.createAuditPayload(auditConfig)); + try (Client tc = getClient()) { + rh.executePutRequest("emp", "{\"settings\": {\"index\": {\"number_of_shards\": 1, \"number_of_replicas\": 0}}}"); + } + List messages = TestAuditlogImpl.doThenWaitForMessages(() -> { try (Client tc = getClient()) { rh.executePutRequest("emp/_doc/0?refresh", "{\"name\" : \"Criag\", \"title\" : \"Software Engineer\"}"); } - }, 7); + }, 3); AuditMessage complianceDocWriteMessage = messages.stream() .filter(m -> m.getCategory().equals(AuditCategory.COMPLIANCE_DOC_WRITE)) From 49bc80f71be9744fbc8da646527a5f9c106edbe5 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 20 Nov 2024 02:39:30 -0500 Subject: [PATCH 02/42] Update Gradle to 8.11 (#4920) Signed-off-by: Andriy Redko --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fb602ee2af..82dd18b204 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionSha256Sum=57dafb5c2622c6cc08b993c85b7c06956a2f53536432a30ead46166dbca0f1e9 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 9dfd5a0661ff1086d0041fbbbef41d0e282dcb02 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 20 Nov 2024 14:25:43 -0500 Subject: [PATCH 03/42] Update Gradle to 8.11.1 (#4924) Signed-off-by: Andriy Redko --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 82dd18b204..eb1a55be0e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=57dafb5c2622c6cc08b993c85b7c06956a2f53536432a30ead46166dbca0f1e9 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 831dca30df9eb0dd28c6053641c424633141a90f Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 30 Aug 2024 16:16:19 -0400 Subject: [PATCH 04/42] Add a base setup for resource access evaluation Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 55 +++++++++++++++++-- .../resources/ResourceAccessEvaluator.java | 50 +++++++++++++++++ .../security/resources/package-info.java | 12 ++++ 3 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.java create mode 100644 src/main/java/org/opensearch/security/resources/package-info.java diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 57ffc4df6f..f43e7a931b 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -68,6 +68,8 @@ import org.opensearch.OpenSearchSecurityException; import org.opensearch.SpecialPermission; import org.opensearch.Version; +import org.opensearch.accesscontrol.resources.EntityType; +import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.action.ActionRequest; import org.opensearch.action.search.PitService; import org.opensearch.action.search.SearchScrollAction; @@ -120,6 +122,7 @@ import org.opensearch.plugins.IdentityPlugin; import org.opensearch.plugins.MapperPlugin; import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.ResourceAccessControlPlugin; import org.opensearch.plugins.SecureHttpTransportSettingsProvider; import org.opensearch.plugins.SecureSettingsFactory; import org.opensearch.plugins.SecureTransportSettingsProvider; @@ -173,6 +176,7 @@ import org.opensearch.security.privileges.RestLayerPrivilegesEvaluator; import org.opensearch.security.privileges.dlsfls.DlsFlsBaseContext; import org.opensearch.security.resolver.IndexResolverReplacer; +import org.opensearch.security.resources.ResourceAccessEvaluator; import org.opensearch.security.rest.DashboardsInfoAction; import org.opensearch.security.rest.SecurityConfigUpdateAction; import org.opensearch.security.rest.SecurityHealthAction; @@ -232,7 +236,8 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin MapperPlugin, // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings ExtensionAwarePlugin, - IdentityPlugin + IdentityPlugin, + ResourceAccessControlPlugin // CS-ENFORCE-SINGLE { @@ -268,6 +273,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin private volatile OpensearchDynamicSetting transportPassiveAuthSetting; private volatile PasswordHasher passwordHasher; private volatile DlsFlsBaseContext dlsFlsBaseContext; + private ResourceAccessEvaluator resourceAccessEvaluator; public static boolean isActionTraceEnabled() { @@ -481,6 +487,8 @@ public List run() { } } + + this.resourceAccessEvaluator = new ResourceAccessEvaluator(); } private void verifyTLSVersion(final String settings, final List configuredProtocols) { @@ -1367,7 +1375,7 @@ public List> getSettings() { settings.add(Setting.simpleString(ConfigConstants.SECURITY_CONFIG_INDEX_NAME, Property.NodeScope, Property.Filtered)); settings.add(Setting.groupSetting(ConfigConstants.SECURITY_AUTHCZ_IMPERSONATION_DN + ".", Property.NodeScope)); // not filtered - // here + // here settings.add(Setting.simpleString(ConfigConstants.SECURITY_CERT_OID, Property.NodeScope, Property.Filtered)); @@ -1383,8 +1391,8 @@ public List> getSettings() { );// not filtered here settings.add(Setting.boolSetting(ConfigConstants.SECURITY_NODES_DN_DYNAMIC_CONFIG_ENABLED, false, Property.NodeScope));// not - // filtered - // here + // filtered + // here settings.add( Setting.boolSetting( @@ -1428,8 +1436,8 @@ public List> getSettings() { Setting.boolSetting(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, false, Property.NodeScope, Property.Filtered) ); settings.add(Setting.groupSetting(ConfigConstants.SECURITY_AUTHCZ_REST_IMPERSONATION_USERS + ".", Property.NodeScope)); // not - // filtered - // here + // filtered + // here settings.add(Setting.simpleString(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, Property.NodeScope, Property.Filtered)); settings.add( @@ -2166,6 +2174,41 @@ private void tryAddSecurityProvider() { }); } + @Override + public Map> listAccessibleResources() { + return this.resourceAccessEvaluator.listAccessibleResources(); + } + + @Override + public List listAccessibleResourcesForPlugin(String systemIndexName) { + return this.resourceAccessEvaluator.listAccessibleResourcesForPlugin(systemIndexName); + } + + @Override + public boolean hasPermission(String resourceId, String systemIndexName) { + return this.resourceAccessEvaluator.hasPermission(resourceId, systemIndexName); + } + + @Override + public ResourceSharing shareWith(String resourceId, String systemIndexName, Map> entities) { + return this.resourceAccessEvaluator.shareWith(resourceId, systemIndexName, entities); + } + + @Override + public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> entities) { + return this.resourceAccessEvaluator.revokeAccess(resourceId, systemIndexName, entities); + } + + @Override + public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { + return this.resourceAccessEvaluator.deleteResourceSharingRecord(resourceId, systemIndexName); + } + + @Override + public boolean deleteAllResourceSharingRecordsFor(String entity) { + return this.resourceAccessEvaluator.deleteAllResourceSharingRecordsFor(entity); + } + public static class GuiceHolder implements LifecycleComponent { private static RepositoriesService repositoriesService; diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.java b/src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.java new file mode 100644 index 0000000000..3e4d73eb03 --- /dev/null +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.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. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.resources; + +import java.util.List; +import java.util.Map; + +import org.opensearch.accesscontrol.resources.EntityType; +import org.opensearch.accesscontrol.resources.ResourceSharing; + +public class ResourceAccessEvaluator { + + public Map> listAccessibleResources() { + return Map.of(); + } + + public List listAccessibleResourcesForPlugin(String s) { + return List.of(); + } + + public boolean hasPermission(String resourceId, String systemIndexName) { + return false; + } + + public ResourceSharing shareWith(String resourceId, String systemIndexName, Map> map) { + return null; + } + + public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> map) { + return null; + } + + public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { + return false; + } + + public boolean deleteAllResourceSharingRecordsFor(String entity) { + return false; + } + +} diff --git a/src/main/java/org/opensearch/security/resources/package-info.java b/src/main/java/org/opensearch/security/resources/package-info.java new file mode 100644 index 0000000000..855bdf81af --- /dev/null +++ b/src/main/java/org/opensearch/security/resources/package-info.java @@ -0,0 +1,12 @@ +/* + * 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.resources; From 1c65eff05e2a9687df3333ec68251d29b439260b Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 6 Sep 2024 13:17:35 -0400 Subject: [PATCH 05/42] Adds handler and other access management components for resource sharing Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 43 ++++-- .../resources/ResourceAccessEvaluator.java | 50 ------- .../resources/ResourceAccessHandler.java | 94 ++++++++++++ .../ResourceManagementRepository.java | 47 ++++++ .../ResourceSharingIndexHandler.java | 134 ++++++++++++++++++ .../ResourceSharingIndexListener.java | 82 +++++++++++ .../security/support/ConfigConstants.java | 3 + 7 files changed, 388 insertions(+), 65 deletions(-) delete mode 100644 src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.java create mode 100644 src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java create mode 100644 src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java create mode 100644 src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java create mode 100644 src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index f43e7a931b..27e89f5c31 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -64,12 +64,10 @@ import org.apache.lucene.search.QueryCachingPolicy; import org.apache.lucene.search.Weight; -import org.opensearch.OpenSearchException; -import org.opensearch.OpenSearchSecurityException; -import org.opensearch.SpecialPermission; -import org.opensearch.Version; +import org.opensearch.*; import org.opensearch.accesscontrol.resources.EntityType; import org.opensearch.accesscontrol.resources.ResourceSharing; +import org.opensearch.accesscontrol.resources.ShareWith; import org.opensearch.action.ActionRequest; import org.opensearch.action.search.PitService; import org.opensearch.action.search.SearchScrollAction; @@ -176,7 +174,9 @@ import org.opensearch.security.privileges.RestLayerPrivilegesEvaluator; import org.opensearch.security.privileges.dlsfls.DlsFlsBaseContext; import org.opensearch.security.resolver.IndexResolverReplacer; -import org.opensearch.security.resources.ResourceAccessEvaluator; +import org.opensearch.security.resources.ResourceAccessHandler; +import org.opensearch.security.resources.ResourceManagementRepository; +import org.opensearch.security.resources.ResourceSharingIndexListener; import org.opensearch.security.rest.DashboardsInfoAction; import org.opensearch.security.rest.SecurityConfigUpdateAction; import org.opensearch.security.rest.SecurityHealthAction; @@ -274,6 +274,9 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin private volatile PasswordHasher passwordHasher; private volatile DlsFlsBaseContext dlsFlsBaseContext; private ResourceAccessEvaluator resourceAccessEvaluator; + private ResourceManagementRepository rmr; + private ResourceAccessHandler resourceAccessHandler; + private final Set indicesToListen = new HashSet<>(); public static boolean isActionTraceEnabled() { @@ -488,7 +491,7 @@ public List run() { } - this.resourceAccessEvaluator = new ResourceAccessEvaluator(); + this.resourceAccessHandler = new ResourceAccessHandler(threadPool); } private void verifyTLSVersion(final String settings, final List configuredProtocols) { @@ -716,6 +719,12 @@ public void onIndexModule(IndexModule indexModule) { dlsFlsBaseContext ) ); + + if (this.indicesToListen.contains(indexModule.getIndex().getName())) { + indexModule.addIndexOperationListener(ResourceSharingIndexListener.getInstance()); + log.warn("Security plugin started listening to operations on index {}", indexModule.getIndex().getName()); + } + indexModule.forceQueryCacheProvider((indexSettings, nodeCache) -> new QueryCache() { @Override @@ -1199,6 +1208,8 @@ public Collection createComponents( e.subscribeForChanges(dcf); } + rmr = ResourceManagementRepository.create(settings, threadPool, localClient); + components.add(adminDns); components.add(cr); components.add(xffResolver); @@ -2073,6 +2084,8 @@ public void onNodeStarted(DiscoveryNode localNode) { if (!SSLConfig.isSslOnlyMode() && !client && !disabled && !useClusterStateToInitSecurityConfig(settings)) { cr.initOnNodeStart(); } + // create resource sharing index if absent + rmr.createResourceSharingIndexIfAbsent(); final Set securityModules = ReflectionHelper.getModulesLoaded(); log.info("{} OpenSearch Security modules loaded so far: {}", securityModules.size(), securityModules); } @@ -2176,37 +2189,37 @@ private void tryAddSecurityProvider() { @Override public Map> listAccessibleResources() { - return this.resourceAccessEvaluator.listAccessibleResources(); + return this.resourceAccessHandler.listAccessibleResources(); } @Override public List listAccessibleResourcesForPlugin(String systemIndexName) { - return this.resourceAccessEvaluator.listAccessibleResourcesForPlugin(systemIndexName); + return this.resourceAccessHandler.listAccessibleResourcesForPlugin(systemIndexName); } @Override public boolean hasPermission(String resourceId, String systemIndexName) { - return this.resourceAccessEvaluator.hasPermission(resourceId, systemIndexName); + return this.resourceAccessHandler.hasPermission(resourceId, systemIndexName); } @Override - public ResourceSharing shareWith(String resourceId, String systemIndexName, Map> entities) { - return this.resourceAccessEvaluator.shareWith(resourceId, systemIndexName, entities); + public ResourceSharing shareWith(String resourceId, String systemIndexName, ShareWith shareWith) { + return this.resourceAccessHandler.shareWith(resourceId, systemIndexName, shareWith); } @Override public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> entities) { - return this.resourceAccessEvaluator.revokeAccess(resourceId, systemIndexName, entities); + return this.resourceAccessHandler.revokeAccess(resourceId, systemIndexName, entities); } @Override public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { - return this.resourceAccessEvaluator.deleteResourceSharingRecord(resourceId, systemIndexName); + return this.resourceAccessHandler.deleteResourceSharingRecord(resourceId, systemIndexName); } @Override - public boolean deleteAllResourceSharingRecordsFor(String entity) { - return this.resourceAccessEvaluator.deleteAllResourceSharingRecordsFor(entity); + public boolean deleteAllResourceSharingRecordsForCurrentUser() { + return this.resourceAccessHandler.deleteAllResourceSharingRecordsForCurrentUser(); } public static class GuiceHolder implements LifecycleComponent { diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.java b/src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.java deleted file mode 100644 index 3e4d73eb03..0000000000 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessEvaluator.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.resources; - -import java.util.List; -import java.util.Map; - -import org.opensearch.accesscontrol.resources.EntityType; -import org.opensearch.accesscontrol.resources.ResourceSharing; - -public class ResourceAccessEvaluator { - - public Map> listAccessibleResources() { - return Map.of(); - } - - public List listAccessibleResourcesForPlugin(String s) { - return List.of(); - } - - public boolean hasPermission(String resourceId, String systemIndexName) { - return false; - } - - public ResourceSharing shareWith(String resourceId, String systemIndexName, Map> map) { - return null; - } - - public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> map) { - return null; - } - - public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { - return false; - } - - public boolean deleteAllResourceSharingRecordsFor(String entity) { - return false; - } - -} diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java new file mode 100644 index 0000000000..0861854e13 --- /dev/null +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.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. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.resources; + +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.accesscontrol.resources.EntityType; +import org.opensearch.accesscontrol.resources.ResourceSharing; +import org.opensearch.accesscontrol.resources.ShareWith; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.ThreadPool; + +public class ResourceAccessHandler { + private static final Logger LOGGER = LogManager.getLogger(ResourceAccessHandler.class); + + private final ThreadContext threadContext; + + public ResourceAccessHandler(final ThreadPool threadPool) { + super(); + this.threadContext = threadPool.getThreadContext(); + } + + public Map> listAccessibleResources() { + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Listing accessible resource for: {}", user.getName()); + + // TODO add concrete implementation + return Map.of(); + } + + public List listAccessibleResourcesForPlugin(String systemIndex) { + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Listing accessible resource within a system index {} for : {}", systemIndex, user.getName()); + + // TODO add concrete implementation + return List.of(); + } + + public boolean hasPermission(String resourceId, String systemIndexName) { + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Checking if {} has permission to resource {}", user.getName(), resourceId); + + // TODO add concrete implementation + return false; + } + + public ResourceSharing shareWith(String resourceId, String systemIndexName, ShareWith shareWith) { + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user.getName(), shareWith); + + // TODO add concrete implementation + return null; + } + + public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Revoking access to resource {} created by {} for {}", resourceId, user.getName(), revokeAccess); + + // TODO add concrete implementation + return null; + } + + public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Deleting resource sharing record for resource {} in {} created by {}", resourceId, systemIndexName, user.getName()); + + // TODO add concrete implementation + return false; + } + + public boolean deleteAllResourceSharingRecordsForCurrentUser() { + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Deleting all resource sharing records for resource {}", user.getName()); + + // TODO add concrete implementation + return false; + } + +} diff --git a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java new file mode 100644 index 0000000000..df59516a41 --- /dev/null +++ b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java @@ -0,0 +1,47 @@ +package org.opensearch.security.resources; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.client.Client; +import org.opensearch.common.settings.Settings; +import org.opensearch.security.configuration.ConfigurationRepository; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.threadpool.ThreadPool; + +public class ResourceManagementRepository { + + private static final Logger LOGGER = LogManager.getLogger(ConfigurationRepository.class); + + private final Client client; + + private final ThreadPool threadPool; + + private final ResourceSharingIndexHandler resourceSharingIndexHandler; + + protected ResourceManagementRepository( + final ThreadPool threadPool, + final Client client, + final ResourceSharingIndexHandler resourceSharingIndexHandler + ) { + this.client = client; + this.threadPool = threadPool; + this.resourceSharingIndexHandler = resourceSharingIndexHandler; + } + + public static ResourceManagementRepository create(Settings settings, final ThreadPool threadPool, Client client) { + final var resourceSharingIndex = ConfigConstants.OPENSEARCH_RESOURCE_SHARING_INDEX; + return new ResourceManagementRepository( + threadPool, + client, + new ResourceSharingIndexHandler(resourceSharingIndex, settings, client, threadPool) + ); + } + + public void createResourceSharingIndexIfAbsent() { + // TODO check if this should be wrapped in an atomic completable future + + this.resourceSharingIndexHandler.createResourceSharingIndexIfAbsent(() -> null); + } + +} diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java new file mode 100644 index 0000000000..79ef85e7eb --- /dev/null +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -0,0 +1,134 @@ +/* + * 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.resources; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.accesscontrol.resources.CreatedBy; +import org.opensearch.accesscontrol.resources.ResourceSharing; +import org.opensearch.accesscontrol.resources.ShareWith; +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.settings.Settings; +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 ResourceSharingIndexHandler { + + private final static int MINIMUM_HASH_BITS = 128; + + private static final Logger LOGGER = LogManager.getLogger(ResourceSharingIndexHandler.class); + + private final Settings settings; + + private final Client client; + + private final String resourceSharingIndex; + + private final ThreadPool threadPool; + + public ResourceSharingIndexHandler(final String indexName, final Settings settings, final Client client, ThreadPool threadPool) { + this.resourceSharingIndex = indexName; + this.settings = settings; + this.client = client; + this.threadPool = threadPool; + } + + public final static Map INDEX_SETTINGS = Map.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); + + public void createIndex(ActionListener listener) { + try (final ThreadContext.StoredContext threadContext = client.threadPool().getThreadContext().stashContext()) { + client.admin() + .indices() + .create( + new CreateIndexRequest(resourceSharingIndex).settings(INDEX_SETTINGS).waitForActiveShards(1), + ActionListener.runBefore(ActionListener.wrap(r -> { + if (r.isAcknowledged()) { + listener.onResponse(true); + } else listener.onFailure(new SecurityException("Couldn't create resource sharing index " + resourceSharingIndex)); + }, listener::onFailure), threadContext::restore) + ); + } + } + + // public void createIndexIfAbsent() { + // try { + // final Map indexSettings = ImmutableMap.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); + // final CreateIndexRequest createIndexRequest = new CreateIndexRequest(resourceSharingIndex).settings(indexSettings); + // final boolean ok = client.admin().indices().create(createIndexRequest).actionGet().isAcknowledged(); + // LOGGER.info("Resource sharing index {} created?: {}", resourceSharingIndex, ok); + // } catch (ResourceAlreadyExistsException resourceAlreadyExistsException) { + // LOGGER.info("Index {} already exists", resourceSharingIndex); + // } + // } + + public void createResourceSharingIndexIfAbsent(Callable callable) { + try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) { + CreateIndexRequest cir = new CreateIndexRequest(resourceSharingIndex); + ActionListener cirListener = ActionListener.wrap(response -> { + LOGGER.info("Resource sharing index {} created.", resourceSharingIndex); + callable.call(); + }, (failResponse) -> { + /* Index already exists, ignore and continue */ + LOGGER.info("Index {} already exists.", resourceSharingIndex); + try { + callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + this.client.admin().indices().create(cir, cirListener); + } + } + + public boolean indexResourceSharing( + String resourceId, + String resourceIndex, + CreatedBy createdBy, + ShareWith shareWith, + ActionListener listener + ) throws IOException { + createResourceSharingIndexIfAbsent(() -> { + ResourceSharing entry = new ResourceSharing(resourceIndex, resourceId, createdBy, shareWith); + + IndexRequest ir = client.prepareIndex(resourceSharingIndex) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .setSource(entry.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) + .request(); + + LOGGER.info("Index Request: {}", ir.toString()); + + ActionListener irListener = ActionListener.wrap(idxResponse -> { + LOGGER.info("Created {} entry.", resourceSharingIndex); + listener.onResponse(idxResponse); + }, (failResponse) -> { + LOGGER.error(failResponse.getMessage()); + LOGGER.info("Failed to create {} entry.", resourceSharingIndex); + listener.onFailure(failResponse); + }); + client.index(ir, irListener); + return null; + }); + return true; + } +} diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java new file mode 100644 index 0000000000..7a2af9f3bd --- /dev/null +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java @@ -0,0 +1,82 @@ +/* + * 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.resources; + +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; + +/** + * This class implements an index operation listener for operations performed on resources stored in plugin's indices + * These indices are defined on bootstrap and configured to listen in OpenSearchSecurityPlugin.java + */ +public class ResourceSharingIndexListener implements IndexingOperationListener { + + private final static Logger log = LogManager.getLogger(ResourceSharingIndexListener.class); + + private static final ResourceSharingIndexListener INSTANCE = new ResourceSharingIndexListener(); + + private boolean initialized; + + private ThreadPool threadPool; + + private Client client; + + private ResourceSharingIndexListener() {} + + public static ResourceSharingIndexListener getInstance() { + + return ResourceSharingIndexListener.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) { + + // implement a check to see if a resource was updated + 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) { + + // implement a check to see if a resource was deleted + log.warn("postDelete called on " + shardId.getIndexName()); + } + +} diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index f35afc6489..456e9586ca 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -370,6 +370,9 @@ public enum RolesMappingResolution { // Variable for initial admin password support public static final String OPENSEARCH_INITIAL_ADMIN_PASSWORD = "OPENSEARCH_INITIAL_ADMIN_PASSWORD"; + // Resource sharing index + public static final String OPENSEARCH_RESOURCE_SHARING_INDEX = ".opensearch_resource_sharing"; + public static Set getSettingAsSet( final Settings settings, final String key, From 118cb07b9ff45a6722e3d5e4d222106c6da66de6 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 6 Sep 2024 13:18:12 -0400 Subject: [PATCH 06/42] Adds sample resource plugin Signed-off-by: Darshit Chanpura --- sample-resource-plugin/build.gradle | 167 ++++++++++++++++++ .../opensearch/security/sample/Resource.java | 8 + .../security/sample/SampleResourcePlugin.java | 165 +++++++++++++++++ .../create/CreateSampleResourceAction.java | 30 ++++ .../create/CreateSampleResourceRequest.java | 55 ++++++ .../create/CreateSampleResourceResponse.java | 55 ++++++ .../CreateSampleResourceRestAction.java | 56 ++++++ .../CreateSampleResourceTransportAction.java | 32 ++++ .../sample/actions/create/SampleResource.java | 45 +++++ .../list/ListSampleResourceAction.java | 29 +++ .../list/ListSampleResourceRequest.java | 39 ++++ .../list/ListSampleResourceResponse.java | 55 ++++++ .../list/ListSampleResourceRestAction.java | 44 +++++ .../ListSampleResourceTransportAction.java | 52 ++++++ .../transport/CreateResourceRequest.java | 50 ++++++ .../transport/CreateResourceResponse.java | 55 ++++++ .../CreateResourceTransportAction.java | 103 +++++++++++ .../plugin-metadata/plugin-security.policy | 3 + .../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 | 3 + 26 files changed, 1207 insertions(+) create mode 100644 sample-resource-plugin/build.gradle create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRequest.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceResponse.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRestAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceTransportAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRequest.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceResponse.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRestAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceTransportAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceRequest.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceResponse.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java create mode 100644 sample-resource-plugin/src/main/plugin-metadata/plugin-security.policy create mode 100644 sample-resource-plugin/src/test/resources/security/esnode-key.pem create mode 100644 sample-resource-plugin/src/test/resources/security/esnode.pem create mode 100644 sample-resource-plugin/src/test/resources/security/kirk-key.pem create mode 100644 sample-resource-plugin/src/test/resources/security/kirk.pem create mode 100644 sample-resource-plugin/src/test/resources/security/root-ca.pem create mode 100644 sample-resource-plugin/src/test/resources/security/sample.pem create mode 100644 sample-resource-plugin/src/test/resources/security/test-kirk.jks diff --git a/sample-resource-plugin/build.gradle b/sample-resource-plugin/build.gradle new file mode 100644 index 0000000000..6d4b084580 --- /dev/null +++ b/sample-resource-plugin/build.gradle @@ -0,0 +1,167 @@ +/* + * 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-security-sample-resource-plugin' + description 'Sample plugin that extends OpenSearch Resource Plugin' + classname 'org.opensearch.security.sampleresourceplugin.SampleResourcePlugin' + 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 { +} + +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 +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java new file mode 100644 index 0000000000..6126fdb092 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java @@ -0,0 +1,8 @@ +package org.opensearch.security.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(); +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java new file mode 100644 index 0000000000..58e4daa95c --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java @@ -0,0 +1,165 @@ +/* + * 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.sample; + +import java.util.ArrayList; +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.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.script.ScriptService; +import org.opensearch.security.sample.actions.create.CreateSampleResourceAction; +import org.opensearch.security.sample.actions.create.CreateSampleResourceRestAction; +import org.opensearch.security.sample.actions.create.CreateSampleResourceTransportAction; +import org.opensearch.security.sample.actions.list.ListSampleResourceAction; +import org.opensearch.security.sample.actions.list.ListSampleResourceRestAction; +import org.opensearch.security.sample.actions.list.ListSampleResourceTransportAction; +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_resources"; + + private Client client; + + @Override + public Collection createComponents( + Client client, + ClusterService clusterService, + ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, + ScriptService scriptService, + NamedXContentRegistry xContentRegistry, + Environment environment, + NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier + ) { + this.client = client; + return Collections.emptyList(); + } + + @Override + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { + return List.of(new CreateSampleResourceRestAction(), new ListSampleResourceRestAction()); + } + + @Override + public List> getActions() { + return List.of( + new ActionHandler<>(CreateSampleResourceAction.INSTANCE, CreateSampleResourceTransportAction.class), + new ActionHandler<>(ListSampleResourceAction.INSTANCE, ListSampleResourceTransportAction.class) + ); + } + + @Override + public Collection 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 ""; + } + + @Override + public Collection> getGuiceServiceClasses() { + final List> 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() {} + + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceAction.java new file mode 100644 index 0000000000..1e106d1a47 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceAction.java @@ -0,0 +1,30 @@ +/* + * 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.sample.actions.create; + +import org.opensearch.action.ActionType; +import org.opensearch.security.sample.transport.CreateResourceResponse; + +/** + * 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, CreateResourceResponse::new); + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRequest.java new file mode 100644 index 0000000000..35815f9a17 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRequest.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.sample.actions.create; + +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.security.sample.Resource; + +/** + * Request object for CreateSampleResource transport action + */ +public class CreateSampleResourceRequest extends ActionRequest { + + private final Resource resource; + + /** + * Default constructor + */ + public CreateSampleResourceRequest(Resource resource) { + this.resource = resource; + } + + /** + * Constructor with stream input + * @param in the stream input + * @throws IOException IOException + */ + public CreateSampleResourceRequest(final StreamInput in) throws IOException { + this.resource = new SampleResource(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/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceResponse.java new file mode 100644 index 0000000000..476d63d5fe --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/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.sample.actions.create; + +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-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRestAction.java new file mode 100644 index 0000000000..00e41bbdf9 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRestAction.java @@ -0,0 +1,56 @@ +/* + * 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.sample.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; +import org.opensearch.security.sample.transport.CreateResourceRequest; + +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) throws IOException { + Map source; + try (XContentParser parser = request.contentParser()) { + source = parser.map(); + } + + String name = (String) source.get("name"); + SampleResource resource = new SampleResource(); + resource.setName(name); + final CreateResourceRequest createSampleResourceRequest = new CreateResourceRequest<>(resource); + return channel -> client.executeLocally( + CreateSampleResourceAction.INSTANCE, + createSampleResourceRequest, + new RestToXContentListener<>(channel) + ); + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceTransportAction.java new file mode 100644 index 0000000000..23c84aec82 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceTransportAction.java @@ -0,0 +1,32 @@ +/* + * 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.sample.actions.create; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.client.Client; +import org.opensearch.common.inject.Inject; +import org.opensearch.security.sample.transport.CreateResourceTransportAction; +import org.opensearch.transport.TransportService; + +import static org.opensearch.security.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; + +/** + * Transport action for CreateSampleResource. + */ +public class CreateSampleResourceTransportAction extends CreateResourceTransportAction { + private static final Logger log = LogManager.getLogger(CreateSampleResourceTransportAction.class); + + @Inject + public CreateSampleResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { + super(transportService, actionFilters, nodeClient, CreateSampleResourceAction.NAME, RESOURCE_INDEX_NAME, SampleResource::new); + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java new file mode 100644 index 0000000000..6bc91c369a --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java @@ -0,0 +1,45 @@ +package org.opensearch.security.sample.actions.create; + +import java.io.IOException; + +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.sample.Resource; + +import static org.opensearch.security.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; + +public class SampleResource extends Resource { + + private String name; + + public SampleResource() {} + + SampleResource(StreamInput in) throws IOException { + this.name = in.readString(); + } + + @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); + } + + @Override + public String getWriteableName() { + return "sampled_resource"; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceAction.java new file mode 100644 index 0000000000..89bee6c093 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/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.sample.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-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRequest.java new file mode 100644 index 0000000000..27d1cd6cfd --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/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.sample.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-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceResponse.java new file mode 100644 index 0000000000..021d456cab --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/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.sample.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-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRestAction.java new file mode 100644 index 0000000000..e56fd08179 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/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.sample.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-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceTransportAction.java new file mode 100644 index 0000000000..e04435725e --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceTransportAction.java @@ -0,0 +1,52 @@ +/* + * 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.sample.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; + +/** + * 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-sharing"); + 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-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceRequest.java new file mode 100644 index 0000000000..ea1eb57755 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceRequest.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.sample.transport; + +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.security.sample.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, 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/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceResponse.java new file mode 100644 index 0000000000..892cd74108 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/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.sample.transport; + +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/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java new file mode 100644 index 0000000000..f95e2d5d5a --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java @@ -0,0 +1,103 @@ +/* + * 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.sample.transport; + +import java.io.IOException; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.accesscontrol.resources.ResourceService; +import org.opensearch.accesscontrol.resources.ResourceSharing; +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.sample.Resource; +import org.opensearch.security.sample.SampleResourcePlugin; +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) { + 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 -> { + log.info("Created resource: {}", idxResponse.toString()); + ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); + ResourceSharing sharing = rs.getResourceAccessControlPlugin() + .shareWith(idxResponse.getId(), idxResponse.getIndex(), Map.of()); + log.info("Created resource sharing entry: {}", sharing.toString()); + }, listener::onFailure); + nodeClient.index(ir, irListener); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + // TODO add delete implementation as a separate transport action +} diff --git a/sample-resource-plugin/src/main/plugin-metadata/plugin-security.policy b/sample-resource-plugin/src/main/plugin-metadata/plugin-security.policy new file mode 100644 index 0000000000..a5dfc33a87 --- /dev/null +++ b/sample-resource-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/sample-resource-plugin/src/test/resources/security/esnode-key.pem b/sample-resource-plugin/src/test/resources/security/esnode-key.pem new file mode 100644 index 0000000000..e90562be43 --- /dev/null +++ b/sample-resource-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-resource-plugin/src/test/resources/security/esnode.pem b/sample-resource-plugin/src/test/resources/security/esnode.pem new file mode 100644 index 0000000000..44101f0b37 --- /dev/null +++ b/sample-resource-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-resource-plugin/src/test/resources/security/kirk-key.pem b/sample-resource-plugin/src/test/resources/security/kirk-key.pem new file mode 100644 index 0000000000..1949c26139 --- /dev/null +++ b/sample-resource-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-resource-plugin/src/test/resources/security/kirk.pem b/sample-resource-plugin/src/test/resources/security/kirk.pem new file mode 100644 index 0000000000..36b7e19a75 --- /dev/null +++ b/sample-resource-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-resource-plugin/src/test/resources/security/root-ca.pem b/sample-resource-plugin/src/test/resources/security/root-ca.pem new file mode 100644 index 0000000000..d33f5f7216 --- /dev/null +++ b/sample-resource-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-resource-plugin/src/test/resources/security/sample.pem b/sample-resource-plugin/src/test/resources/security/sample.pem new file mode 100644 index 0000000000..44101f0b37 --- /dev/null +++ b/sample-resource-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-resource-plugin/src/test/resources/security/test-kirk.jks b/sample-resource-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 Date: Fri, 6 Sep 2024 13:18:33 -0400 Subject: [PATCH 07/42] Removes node_modules entry from gitingore Signed-off-by: Darshit Chanpura --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index 6fbfafabac..5eb2da999f 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,3 @@ out/ build/ gradle-build/ .gradle/ - -# nodejs -node_modules/ -package-lock.json From 45d4fa580684cec31acb434f9d251d64d88643b1 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 13:47:23 -0400 Subject: [PATCH 08/42] Handles changes related to scope Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 4 ++-- .../security/resources/ResourceAccessHandler.java | 4 ++-- .../resources/ResourceManagementRepository.java | 11 +++++++++++ .../resources/ResourceSharingIndexHandler.java | 14 ++------------ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 27e89f5c31..62c5cad9fe 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -2198,8 +2198,8 @@ public List listAccessibleResourcesForPlugin(String systemIndexName) { } @Override - public boolean hasPermission(String resourceId, String systemIndexName) { - return this.resourceAccessHandler.hasPermission(resourceId, systemIndexName); + public boolean hasPermission(String resourceId, String systemIndexName, String scope) { + return this.resourceAccessHandler.hasPermission(resourceId, systemIndexName, scope); } @Override diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 0861854e13..142c6b67da 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -51,9 +51,9 @@ public List listAccessibleResourcesForPlugin(String systemIndex) { return List.of(); } - public boolean hasPermission(String resourceId, String systemIndexName) { + public boolean hasPermission(String resourceId, String systemIndexName, String scope) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - LOGGER.info("Checking if {} has permission to resource {}", user.getName(), resourceId); + LOGGER.info("Checking if {} has {} permission to resource {}", user.getName(), scope, resourceId); // TODO add concrete implementation return false; diff --git a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java index df59516a41..7e347a331d 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java +++ b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.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.resources; import org.apache.logging.log4j.LogManager; diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 79ef85e7eb..b6f4b02ade 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -71,20 +71,10 @@ public void createIndex(ActionListener listener) { } } - // public void createIndexIfAbsent() { - // try { - // final Map indexSettings = ImmutableMap.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); - // final CreateIndexRequest createIndexRequest = new CreateIndexRequest(resourceSharingIndex).settings(indexSettings); - // final boolean ok = client.admin().indices().create(createIndexRequest).actionGet().isAcknowledged(); - // LOGGER.info("Resource sharing index {} created?: {}", resourceSharingIndex, ok); - // } catch (ResourceAlreadyExistsException resourceAlreadyExistsException) { - // LOGGER.info("Index {} already exists", resourceSharingIndex); - // } - // } - public void createResourceSharingIndexIfAbsent(Callable callable) { + // TODO: Once stashContext is replaced with switchContext this call will have to be modified try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) { - CreateIndexRequest cir = new CreateIndexRequest(resourceSharingIndex); + CreateIndexRequest cir = new CreateIndexRequest(resourceSharingIndex).settings(INDEX_SETTINGS).waitForActiveShards(1); ActionListener cirListener = ActionListener.wrap(response -> { LOGGER.info("Resource sharing index {} created.", resourceSharingIndex); callable.call(); From ae2464dc83132b21979cae858676fd66861b5c34 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 13:47:47 -0400 Subject: [PATCH 09/42] Updates sample plugin to implement a custom scope Signed-off-by: Darshit Chanpura --- .../security/sample/SampleResourceScope.java | 22 +++++++++++++++++++ .../CreateResourceTransportAction.java | 21 +++++++++++------- 2 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java new file mode 100644 index 0000000000..797f3e517b --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java @@ -0,0 +1,22 @@ +package org.opensearch.security.sample; + +import org.opensearch.accesscontrol.resources.ResourceAccessScope; + +/** + * This class demonstrates a sample implementation of Basic Access Scopes to fit each plugin's use-case. + * The plugin then uses this scope when seeking access evaluation for a user on a particular resource. + */ +enum SampleResourceScope implements ResourceAccessScope { + + SAMPLE_FULL_ACCESS("sample_full_access"); + + private final String name; + + SampleResourceScope(String scopeName) { + this.name = scopeName; + } + + public String getName() { + return name; + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java index f95e2d5d5a..dea075c55e 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java @@ -9,13 +9,14 @@ package org.opensearch.security.sample.transport; import java.io.IOException; -import java.util.Map; +import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.accesscontrol.resources.ResourceService; import org.opensearch.accesscontrol.resources.ResourceSharing; +import org.opensearch.accesscontrol.resources.ShareWith; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; import org.opensearch.action.index.IndexRequest; @@ -86,18 +87,22 @@ private void createResource(CreateResourceRequest request, ActionListener irListener = ActionListener.wrap(idxResponse -> { - log.info("Created resource: {}", idxResponse.toString()); - ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); - ResourceSharing sharing = rs.getResourceAccessControlPlugin() - .shareWith(idxResponse.getId(), idxResponse.getIndex(), Map.of()); - log.info("Created resource sharing entry: {}", sharing.toString()); - }, listener::onFailure); + ActionListener irListener = getIndexResponseActionListener(listener); nodeClient.index(ir, irListener); } catch (IOException e) { throw new RuntimeException(e); } } + private static ActionListener getIndexResponseActionListener(ActionListener listener) { + ShareWith shareWith = new ShareWith(List.of()); + return ActionListener.wrap(idxResponse -> { + log.info("Created resource: {}", idxResponse.toString()); + ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); + ResourceSharing sharing = rs.getResourceAccessControlPlugin().shareWith(idxResponse.getId(), idxResponse.getIndex(), shareWith); + log.info("Created resource sharing entry: {}", sharing.toString()); + }, listener::onFailure); + } + // TODO add delete implementation as a separate transport action } From aea2253a4c19089479394a12fde56ea1e555fae8 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 13:57:17 -0400 Subject: [PATCH 10/42] Fixes Checkstyle and spotless issues Signed-off-by: Darshit Chanpura --- .../java/org/opensearch/security/sample/Resource.java | 11 +++++++++++ .../security/sample/SampleResourceScope.java | 11 +++++++++++ .../sample/actions/create/SampleResource.java | 11 +++++++++++ .../opensearch/security/OpenSearchSecurityPlugin.java | 5 ++++- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java index 6126fdb092..0dd3b856bf 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.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.sample; import org.opensearch.core.common.io.stream.NamedWriteable; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java index 797f3e517b..7a1b304371 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.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.sample; import org.opensearch.accesscontrol.resources.ResourceAccessScope; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java index 6bc91c369a..50c013f7dc 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.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.sample.actions.create; import java.io.IOException; diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 62c5cad9fe..32a412653f 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -64,7 +64,10 @@ import org.apache.lucene.search.QueryCachingPolicy; import org.apache.lucene.search.Weight; -import org.opensearch.*; +import org.opensearch.OpenSearchException; +import org.opensearch.OpenSearchSecurityException; +import org.opensearch.SpecialPermission; +import org.opensearch.Version; import org.opensearch.accesscontrol.resources.EntityType; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; From 1e17dc0b0bd74ec726f68526f3597cbe62550163 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 14:12:38 -0400 Subject: [PATCH 11/42] Fixes initialization error Signed-off-by: Darshit Chanpura --- .../org/opensearch/security/OpenSearchSecurityPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 32a412653f..0a0df9e574 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -493,8 +493,6 @@ public List run() { } } - - this.resourceAccessHandler = new ResourceAccessHandler(threadPool); } private void verifyTLSVersion(final String settings, final List configuredProtocols) { @@ -1211,6 +1209,8 @@ public Collection createComponents( e.subscribeForChanges(dcf); } + resourceAccessHandler = new ResourceAccessHandler(threadPool); + rmr = ResourceManagementRepository.create(settings, threadPool, localClient); components.add(adminDns); From 84746e815b4aff72902bbc6a8e9ad467b594209d Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 14:50:46 -0400 Subject: [PATCH 12/42] Renames sample resource plugin and adds a logger statement Signed-off-by: Darshit Chanpura --- sample-resource-plugin/build.gradle | 5 ++--- .../org/opensearch/security/sample/SampleResourcePlugin.java | 3 ++- settings.gradle | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sample-resource-plugin/build.gradle b/sample-resource-plugin/build.gradle index 6d4b084580..dd04d390b0 100644 --- a/sample-resource-plugin/build.gradle +++ b/sample-resource-plugin/build.gradle @@ -11,10 +11,9 @@ import org.opensearch.gradle.test.RestIntegTestTask opensearchplugin { - name 'opensearch-security-sample-resource-plugin' + name 'opensearch-sample-resource-plugin' description 'Sample plugin that extends OpenSearch Resource Plugin' - classname 'org.opensearch.security.sampleresourceplugin.SampleResourcePlugin' - extendedPlugins = ['opensearch-security'] + classname 'org.opensearch.security.sample.SampleResourcePlugin' } ext { diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java index 58e4daa95c..5d598c5650 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java @@ -81,6 +81,7 @@ public Collection createComponents( Supplier repositoriesServiceSupplier ) { this.client = client; + log.info("Loaded SampleResourcePlugin components."); return Collections.emptyList(); } @@ -118,7 +119,7 @@ public String getResourceType() { @Override public String getResourceIndex() { - return ""; + return RESOURCE_INDEX_NAME; } @Override diff --git a/settings.gradle b/settings.gradle index f2e59414d8..0bb3c5639d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,4 +7,4 @@ rootProject.name = 'opensearch-security' include "sample-resource-plugin" -project(":sample-resource-plugin").name = rootProject.name + "-sample-resource-plugin" +project(":sample-resource-plugin").name = "opensearch-sample-resource-plugin" From 83e4da09ce210143db68b3464dc0dc92e3bbf3cf Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 14:53:10 -0400 Subject: [PATCH 13/42] Changes package name for sample plugin Signed-off-by: Darshit Chanpura --- sample-resource-plugin/build.gradle | 2 +- .../opensearch/{security => }/sample/Resource.java | 2 +- .../sample/SampleResourcePlugin.java | 14 +++++++------- .../{security => }/sample/SampleResourceScope.java | 2 +- .../actions/create/CreateSampleResourceAction.java | 4 ++-- .../create/CreateSampleResourceRequest.java | 4 ++-- .../create/CreateSampleResourceResponse.java | 2 +- .../create/CreateSampleResourceRestAction.java | 4 ++-- .../CreateSampleResourceTransportAction.java | 6 +++--- .../sample/actions/create/SampleResource.java | 6 +++--- .../actions/list/ListSampleResourceAction.java | 2 +- .../actions/list/ListSampleResourceRequest.java | 2 +- .../actions/list/ListSampleResourceResponse.java | 2 +- .../actions/list/ListSampleResourceRestAction.java | 2 +- .../list/ListSampleResourceTransportAction.java | 2 +- .../sample/transport/CreateResourceRequest.java | 4 ++-- .../sample/transport/CreateResourceResponse.java | 2 +- .../transport/CreateResourceTransportAction.java | 6 +++--- 18 files changed, 34 insertions(+), 34 deletions(-) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/Resource.java (93%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/SampleResourcePlugin.java (91%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/SampleResourceScope.java (95%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/create/CreateSampleResourceAction.java (85%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/create/CreateSampleResourceRequest.java (92%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/create/CreateSampleResourceResponse.java (96%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/create/CreateSampleResourceRestAction.java (93%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/create/CreateSampleResourceTransportAction.java (82%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/create/SampleResource.java (87%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/list/ListSampleResourceAction.java (93%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/list/ListSampleResourceRequest.java (95%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/list/ListSampleResourceResponse.java (96%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/list/ListSampleResourceRestAction.java (96%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/actions/list/ListSampleResourceTransportAction.java (97%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/transport/CreateResourceRequest.java (92%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/transport/CreateResourceResponse.java (96%) rename sample-resource-plugin/src/main/java/org/opensearch/{security => }/sample/transport/CreateResourceTransportAction.java (96%) diff --git a/sample-resource-plugin/build.gradle b/sample-resource-plugin/build.gradle index dd04d390b0..e9822c1f22 100644 --- a/sample-resource-plugin/build.gradle +++ b/sample-resource-plugin/build.gradle @@ -13,7 +13,7 @@ import org.opensearch.gradle.test.RestIntegTestTask opensearchplugin { name 'opensearch-sample-resource-plugin' description 'Sample plugin that extends OpenSearch Resource Plugin' - classname 'org.opensearch.security.sample.SampleResourcePlugin' + classname 'org.opensearch.sample.SampleResourcePlugin' } ext { diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java similarity index 93% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java index 0dd3b856bf..36e74f1624 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/Resource.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java @@ -9,7 +9,7 @@ * GitHub history for details. */ -package org.opensearch.security.sample; +package org.opensearch.sample; import org.opensearch.core.common.io.stream.NamedWriteable; import org.opensearch.core.xcontent.ToXContentFragment; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java similarity index 91% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java index 5d598c5650..bb272b2201 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourcePlugin.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java @@ -6,7 +6,7 @@ * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ -package org.opensearch.security.sample; +package org.opensearch.sample; import java.util.ArrayList; import java.util.Collection; @@ -44,13 +44,13 @@ import org.opensearch.repositories.RepositoriesService; import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; +import org.opensearch.sample.actions.create.CreateSampleResourceAction; +import org.opensearch.sample.actions.create.CreateSampleResourceRestAction; +import org.opensearch.sample.actions.create.CreateSampleResourceTransportAction; +import org.opensearch.sample.actions.list.ListSampleResourceAction; +import org.opensearch.sample.actions.list.ListSampleResourceRestAction; +import org.opensearch.sample.actions.list.ListSampleResourceTransportAction; import org.opensearch.script.ScriptService; -import org.opensearch.security.sample.actions.create.CreateSampleResourceAction; -import org.opensearch.security.sample.actions.create.CreateSampleResourceRestAction; -import org.opensearch.security.sample.actions.create.CreateSampleResourceTransportAction; -import org.opensearch.security.sample.actions.list.ListSampleResourceAction; -import org.opensearch.security.sample.actions.list.ListSampleResourceRestAction; -import org.opensearch.security.sample.actions.list.ListSampleResourceTransportAction; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java similarity index 95% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java index 7a1b304371..2784de45b7 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/SampleResourceScope.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java @@ -9,7 +9,7 @@ * GitHub history for details. */ -package org.opensearch.security.sample; +package org.opensearch.sample; import org.opensearch.accesscontrol.resources.ResourceAccessScope; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceAction.java similarity index 85% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceAction.java index 1e106d1a47..fce62be629 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceAction.java @@ -6,10 +6,10 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.create; +package org.opensearch.sample.actions.create; import org.opensearch.action.ActionType; -import org.opensearch.security.sample.transport.CreateResourceResponse; +import org.opensearch.sample.transport.CreateResourceResponse; /** * Action to create a sample resource diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRequest.java similarity index 92% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRequest.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRequest.java index 35815f9a17..a509031b0b 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRequest.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRequest.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.create; +package org.opensearch.sample.actions.create; import java.io.IOException; @@ -14,7 +14,7 @@ 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.sample.Resource; +import org.opensearch.sample.Resource; /** * Request object for CreateSampleResource transport action diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceResponse.java similarity index 96% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceResponse.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceResponse.java index 476d63d5fe..86796bfff5 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceResponse.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceResponse.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.create; +package org.opensearch.sample.actions.create; import java.io.IOException; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRestAction.java similarity index 93% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRestAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRestAction.java index 00e41bbdf9..f422835168 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceRestAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRestAction.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.create; +package org.opensearch.sample.actions.create; import java.io.IOException; import java.util.List; @@ -17,7 +17,7 @@ import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; -import org.opensearch.security.sample.transport.CreateResourceRequest; +import org.opensearch.sample.transport.CreateResourceRequest; import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.POST; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceTransportAction.java similarity index 82% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceTransportAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceTransportAction.java index 23c84aec82..53d9817fbc 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/CreateSampleResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceTransportAction.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.create; +package org.opensearch.sample.actions.create; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,10 +14,10 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.client.Client; import org.opensearch.common.inject.Inject; -import org.opensearch.security.sample.transport.CreateResourceTransportAction; +import org.opensearch.sample.transport.CreateResourceTransportAction; import org.opensearch.transport.TransportService; -import static org.opensearch.security.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; +import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; /** * Transport action for CreateSampleResource. diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java similarity index 87% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java index 50c013f7dc..d2528c92be 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/create/SampleResource.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java @@ -9,16 +9,16 @@ * GitHub history for details. */ -package org.opensearch.security.sample.actions.create; +package org.opensearch.sample.actions.create; import java.io.IOException; 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.sample.Resource; +import org.opensearch.sample.Resource; -import static org.opensearch.security.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; +import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; public class SampleResource extends Resource { diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceAction.java similarity index 93% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceAction.java index 89bee6c093..17f50cda30 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceAction.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.list; +package org.opensearch.sample.actions.list; import org.opensearch.action.ActionType; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRequest.java similarity index 95% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRequest.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRequest.java index 27d1cd6cfd..ffadf6abbb 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRequest.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRequest.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.list; +package org.opensearch.sample.actions.list; import java.io.IOException; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceResponse.java similarity index 96% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceResponse.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceResponse.java index 021d456cab..aaf6bfcd3e 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceResponse.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceResponse.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.list; +package org.opensearch.sample.actions.list; import java.io.IOException; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRestAction.java similarity index 96% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRestAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRestAction.java index e56fd08179..3f01bb5e2c 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceRestAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRestAction.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.list; +package org.opensearch.sample.actions.list; import java.util.List; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceTransportAction.java similarity index 97% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceTransportAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceTransportAction.java index e04435725e..ece829fe0d 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/actions/list/ListSampleResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceTransportAction.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.actions.list; +package org.opensearch.sample.actions.list; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchResponse; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceRequest.java similarity index 92% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceRequest.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceRequest.java index ea1eb57755..f23735e7f3 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceRequest.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceRequest.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.transport; +package org.opensearch.sample.transport; import java.io.IOException; @@ -14,7 +14,7 @@ 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.sample.Resource; +import org.opensearch.sample.Resource; /** * Request object for CreateSampleResource transport action diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceResponse.java similarity index 96% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceResponse.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceResponse.java index 892cd74108..12d7671ac4 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceResponse.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceResponse.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.transport; +package org.opensearch.sample.transport; import java.io.IOException; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java similarity index 96% rename from sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java index dea075c55e..5e2eb6d723 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/security/sample/transport/CreateResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.security.sample.transport; +package org.opensearch.sample.transport; import java.io.IOException; import java.util.List; @@ -29,8 +29,8 @@ import org.opensearch.core.action.ActionListener; import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.core.xcontent.ToXContent; -import org.opensearch.security.sample.Resource; -import org.opensearch.security.sample.SampleResourcePlugin; +import org.opensearch.sample.Resource; +import org.opensearch.sample.SampleResourcePlugin; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; From 4b9b9b13bb79029a1fb96e8e8d38525e3aad1ba8 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 18:10:57 -0400 Subject: [PATCH 14/42] Re-organizes and renames sample plugin files Signed-off-by: Darshit Chanpura --- .../sample/SampleResourcePlugin.java | 33 ++++---- ...eAction.java => CreateResourceAction.java} | 7 +- .../create}/CreateResourceRequest.java | 12 +-- .../create}/CreateResourceResponse.java | 2 +- ...ion.java => CreateResourceRestAction.java} | 11 ++- .../create/CreateSampleResourceRequest.java | 55 ------------- .../CreateSampleResourceTransportAction.java | 32 -------- .../sample/actions/create/SampleResource.java | 2 +- ...ava => ListAccessibleResourcesAction.java} | 8 +- ...va => ListAccessibleResourcesRequest.java} | 6 +- .../list/ListAccessibleResourcesResponse.java | 46 +++++++++++ ...=> ListAccessibleResourcesRestAction.java} | 12 +-- .../ListSampleResourceTransportAction.java | 52 ------------- .../actions/share/ShareResourceAction.java | 26 +++++++ .../actions/share/ShareResourceRequest.java | 52 +++++++++++++ .../ShareResourceResponse.java} | 11 +-- .../share/ShareResourceRestAction.java | 51 ++++++++++++ .../verify/VerifyResourceAccessAction.java | 25 ++++++ .../verify/VerifyResourceAccessRequest.java | 69 +++++++++++++++++ .../VerifyResourceAccessResponse.java} | 11 +-- .../VerifyResourceAccessRestAction.java | 52 +++++++++++++ .../CreateResourceTransportAction.java | 32 +++----- ...istAccessibleResourcesTransportAction.java | 56 ++++++++++++++ .../ShareResourceTransportAction.java | 77 +++++++++++++++++++ .../VerifyResourceAccessTransportAction.java | 58 ++++++++++++++ .../resources/ResourceAccessHandler.java | 6 +- 26 files changed, 583 insertions(+), 221 deletions(-) rename sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/{CreateSampleResourceAction.java => CreateResourceAction.java} (67%) rename sample-resource-plugin/src/main/java/org/opensearch/sample/{transport => actions/create}/CreateResourceRequest.java (73%) rename sample-resource-plugin/src/main/java/org/opensearch/sample/{transport => actions/create}/CreateResourceResponse.java (96%) rename sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/{CreateSampleResourceRestAction.java => CreateResourceRestAction.java} (75%) delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRequest.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceTransportAction.java rename sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/{ListSampleResourceAction.java => ListAccessibleResourcesAction.java} (63%) rename sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/{ListSampleResourceRequest.java => ListAccessibleResourcesRequest.java} (81%) create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java rename sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/{ListSampleResourceRestAction.java => ListAccessibleResourcesRestAction.java} (68%) delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceTransportAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java rename sample-resource-plugin/src/main/java/org/opensearch/sample/actions/{list/ListSampleResourceResponse.java => share/ShareResourceResponse.java} (78%) create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java rename sample-resource-plugin/src/main/java/org/opensearch/sample/actions/{create/CreateSampleResourceResponse.java => verify/VerifyResourceAccessResponse.java} (81%) create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java create mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java index bb272b2201..abc9ed4de7 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java @@ -8,10 +8,7 @@ */ package org.opensearch.sample; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.function.Supplier; import org.apache.logging.log4j.LogManager; @@ -44,12 +41,16 @@ import org.opensearch.repositories.RepositoriesService; import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; -import org.opensearch.sample.actions.create.CreateSampleResourceAction; -import org.opensearch.sample.actions.create.CreateSampleResourceRestAction; -import org.opensearch.sample.actions.create.CreateSampleResourceTransportAction; -import org.opensearch.sample.actions.list.ListSampleResourceAction; -import org.opensearch.sample.actions.list.ListSampleResourceRestAction; -import org.opensearch.sample.actions.list.ListSampleResourceTransportAction; +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.verify.VerifyResourceAccessAction; +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; @@ -62,7 +63,9 @@ 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_resources"; + public static final String RESOURCE_INDEX_NAME = ".sample_resource_sharing_plugin"; + + public final static Map INDEX_SETTINGS = Map.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); private Client client; @@ -95,14 +98,16 @@ public List getRestHandlers( IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster ) { - return List.of(new CreateSampleResourceRestAction(), new ListSampleResourceRestAction()); + return List.of(new CreateResourceRestAction(), new ListAccessibleResourcesRestAction()); } @Override public List> getActions() { return List.of( - new ActionHandler<>(CreateSampleResourceAction.INSTANCE, CreateSampleResourceTransportAction.class), - new ActionHandler<>(ListSampleResourceAction.INSTANCE, ListSampleResourceTransportAction.class) + new ActionHandler<>(CreateResourceAction.INSTANCE, CreateResourceTransportAction.class), + new ActionHandler<>(ListAccessibleResourcesAction.INSTANCE, ListAccessibleResourcesTransportAction.class), + new ActionHandler<>(ShareResourceAction.INSTANCE, ShareResourceTransportAction.class), + new ActionHandler<>(VerifyResourceAccessAction.INSTANCE, VerifyResourceAccessTransportAction.class) ); } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java similarity index 67% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java index fce62be629..5ddcc79008 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java @@ -9,22 +9,21 @@ package org.opensearch.sample.actions.create; import org.opensearch.action.ActionType; -import org.opensearch.sample.transport.CreateResourceResponse; /** * Action to create a sample resource */ -public class CreateSampleResourceAction extends ActionType { +public class CreateResourceAction extends ActionType { /** * Create sample resource action instance */ - public static final CreateSampleResourceAction INSTANCE = new CreateSampleResourceAction(); + public static final CreateResourceAction INSTANCE = new CreateResourceAction(); /** * Create sample resource action name */ public static final String NAME = "cluster:admin/sampleresource/create"; - private CreateSampleResourceAction() { + private CreateResourceAction() { super(NAME, CreateResourceResponse::new); } } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRequest.java similarity index 73% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceRequest.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRequest.java index f23735e7f3..b31a4b7f2b 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceRequest.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRequest.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.sample.transport; +package org.opensearch.sample.actions.create; import java.io.IOException; @@ -19,19 +19,19 @@ /** * Request object for CreateSampleResource transport action */ -public class CreateResourceRequest extends ActionRequest { +public class CreateResourceRequest extends ActionRequest { - private final T resource; + private final Resource resource; /** * Default constructor */ - public CreateResourceRequest(T resource) { + public CreateResourceRequest(Resource resource) { this.resource = resource; } - public CreateResourceRequest(StreamInput in, Reader resourceReader) throws IOException { - this.resource = resourceReader.read(in); + public CreateResourceRequest(StreamInput in) throws IOException { + this.resource = in.readNamedWriteable(Resource.class); } @Override diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceResponse.java similarity index 96% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceResponse.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceResponse.java index 12d7671ac4..6b966ed08d 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceResponse.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceResponse.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.sample.transport; +package org.opensearch.sample.actions.create; import java.io.IOException; diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRestAction.java similarity index 75% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRestAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRestAction.java index f422835168..86346cc279 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRestAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRestAction.java @@ -17,18 +17,17 @@ import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; -import org.opensearch.sample.transport.CreateResourceRequest; import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.POST; -public class CreateSampleResourceRestAction extends BaseRestHandler { +public class CreateResourceRestAction extends BaseRestHandler { - public CreateSampleResourceRestAction() {} + public CreateResourceRestAction() {} @Override public List routes() { - return singletonList(new Route(POST, "/_plugins/resource_sharing_example/resource")); + return singletonList(new Route(POST, "/_plugins/sample_resource_sharing/resource")); } @Override @@ -46,9 +45,9 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client String name = (String) source.get("name"); SampleResource resource = new SampleResource(); resource.setName(name); - final CreateResourceRequest createSampleResourceRequest = new CreateResourceRequest<>(resource); + final CreateResourceRequest createSampleResourceRequest = new CreateResourceRequest(resource); return channel -> client.executeLocally( - CreateSampleResourceAction.INSTANCE, + CreateResourceAction.INSTANCE, createSampleResourceRequest, new RestToXContentListener<>(channel) ); diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRequest.java deleted file mode 100644 index a509031b0b..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.actions.create; - -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.sample.Resource; - -/** - * Request object for CreateSampleResource transport action - */ -public class CreateSampleResourceRequest extends ActionRequest { - - private final Resource resource; - - /** - * Default constructor - */ - public CreateSampleResourceRequest(Resource resource) { - this.resource = resource; - } - - /** - * Constructor with stream input - * @param in the stream input - * @throws IOException IOException - */ - public CreateSampleResourceRequest(final StreamInput in) throws IOException { - this.resource = new SampleResource(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/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceTransportAction.java deleted file mode 100644 index 53d9817fbc..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceTransportAction.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.actions.create; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.action.support.ActionFilters; -import org.opensearch.client.Client; -import org.opensearch.common.inject.Inject; -import org.opensearch.sample.transport.CreateResourceTransportAction; -import org.opensearch.transport.TransportService; - -import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; - -/** - * Transport action for CreateSampleResource. - */ -public class CreateSampleResourceTransportAction extends CreateResourceTransportAction { - private static final Logger log = LogManager.getLogger(CreateSampleResourceTransportAction.class); - - @Inject - public CreateSampleResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { - super(transportService, actionFilters, nodeClient, CreateSampleResourceAction.NAME, RESOURCE_INDEX_NAME, SampleResource::new); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java index d2528c92be..1566abfe69 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java @@ -47,7 +47,7 @@ public void writeTo(StreamOutput streamOutput) throws IOException { @Override public String getWriteableName() { - return "sampled_resource"; + return "sample_resource"; } public void setName(String name) { diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java similarity index 63% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java index 17f50cda30..cc7e4769f6 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java @@ -13,17 +13,17 @@ /** * Action to list sample resources */ -public class ListSampleResourceAction extends ActionType { +public class ListAccessibleResourcesAction extends ActionType { /** * List sample resource action instance */ - public static final ListSampleResourceAction INSTANCE = new ListSampleResourceAction(); + public static final ListAccessibleResourcesAction INSTANCE = new ListAccessibleResourcesAction(); /** * List sample resource action name */ public static final String NAME = "cluster:admin/sampleresource/list"; - private ListSampleResourceAction() { - super(NAME, ListSampleResourceResponse::new); + private ListAccessibleResourcesAction() { + super(NAME, ListAccessibleResourcesResponse::new); } } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRequest.java similarity index 81% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRequest.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRequest.java index ffadf6abbb..b4c0961774 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRequest.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRequest.java @@ -18,16 +18,16 @@ /** * Request object for ListSampleResource transport action */ -public class ListSampleResourceRequest extends ActionRequest { +public class ListAccessibleResourcesRequest extends ActionRequest { - public ListSampleResourceRequest() {} + public ListAccessibleResourcesRequest() {} /** * Constructor with stream input * @param in the stream input * @throws IOException IOException */ - public ListSampleResourceRequest(final StreamInput in) throws IOException {} + public ListAccessibleResourcesRequest(final StreamInput in) throws IOException {} @Override public void writeTo(final StreamOutput out) throws IOException {} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java new file mode 100644 index 0000000000..47a8f88e4e --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java @@ -0,0 +1,46 @@ +/* + * 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.actions.list; + +import java.io.IOException; +import java.util.List; + +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 ListAccessibleResourcesRequest + */ +public class ListAccessibleResourcesResponse extends ActionResponse implements ToXContentObject { + private final List resourceIds; + + public ListAccessibleResourcesResponse(List resourceIds) { + this.resourceIds = resourceIds; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeStringArray(resourceIds.toArray(new String[0])); + } + + public ListAccessibleResourcesResponse(final StreamInput in) throws IOException { + resourceIds = in.readStringList(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("resource-ids", resourceIds); + builder.endObject(); + return builder; + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRestAction.java similarity index 68% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRestAction.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRestAction.java index 3f01bb5e2c..bb921fce00 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceRestAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRestAction.java @@ -18,13 +18,13 @@ import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.GET; -public class ListSampleResourceRestAction extends BaseRestHandler { +public class ListAccessibleResourcesRestAction extends BaseRestHandler { - public ListSampleResourceRestAction() {} + public ListAccessibleResourcesRestAction() {} @Override public List routes() { - return singletonList(new Route(GET, "/_plugins/resource_sharing_example/resource")); + return singletonList(new Route(GET, "/_plugins/sample_resource_sharing/resource")); } @Override @@ -34,10 +34,10 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { - final ListSampleResourceRequest listSampleResourceRequest = new ListSampleResourceRequest(); + final ListAccessibleResourcesRequest listAccessibleResourcesRequest = new ListAccessibleResourcesRequest(); return channel -> client.executeLocally( - ListSampleResourceAction.INSTANCE, - listSampleResourceRequest, + ListAccessibleResourcesAction.INSTANCE, + listAccessibleResourcesRequest, new RestToXContentListener<>(channel) ); } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceTransportAction.java deleted file mode 100644 index ece829fe0d..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceTransportAction.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.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; - -/** - * 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-sharing"); - 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-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java new file mode 100644 index 0000000000..152caf8c8c --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java @@ -0,0 +1,26 @@ +/* + * 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.actions.share; + +import org.opensearch.action.ActionType; + +public class ShareResourceAction extends ActionType { + /** + * List sample resource action instance + */ + public static final ShareResourceAction INSTANCE = new ShareResourceAction(); + /** + * List sample resource action name + */ + public static final String NAME = "cluster:admin/sampleresource/share"; + + private ShareResourceAction() { + super(NAME, ShareResourceResponse::new); + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java new file mode 100644 index 0000000000..01866fd516 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java @@ -0,0 +1,52 @@ +/* + * 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.actions.share; + +import java.io.IOException; + +import org.opensearch.accesscontrol.resources.ShareWith; +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; + +public class ShareResourceRequest extends ActionRequest { + + private final String resourceId; + private final ShareWith shareWith; + + public ShareResourceRequest(String resourceId, ShareWith shareWith) { + this.resourceId = resourceId; + this.shareWith = shareWith; + } + + public ShareResourceRequest(StreamInput in) throws IOException { + this.resourceId = in.readString(); + this.shareWith = in.readNamedWriteable(ShareWith.class); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeString(resourceId); + out.writeNamedWriteable(shareWith); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + public String getResourceId() { + return resourceId; + } + + public ShareWith getShareWith() { + return shareWith; + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceResponse.java similarity index 78% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceResponse.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceResponse.java index aaf6bfcd3e..a6a85d206d 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListSampleResourceResponse.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceResponse.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.sample.actions.list; +package org.opensearch.sample.actions.share; import java.io.IOException; @@ -16,10 +16,7 @@ import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; -/** - * Response to a ListSampleResourceRequest - */ -public class ListSampleResourceResponse extends ActionResponse implements ToXContentObject { +public class ShareResourceResponse extends ActionResponse implements ToXContentObject { private final String message; /** @@ -27,7 +24,7 @@ public class ListSampleResourceResponse extends ActionResponse implements ToXCon * * @param message The message */ - public ListSampleResourceResponse(String message) { + public ShareResourceResponse(String message) { this.message = message; } @@ -41,7 +38,7 @@ public void writeTo(StreamOutput out) throws IOException { * * @param in the stream input */ - public ListSampleResourceResponse(final StreamInput in) throws IOException { + public ShareResourceResponse(final StreamInput in) throws IOException { message = in.readString(); } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java new file mode 100644 index 0000000000..87bc083f2e --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.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.sample.actions.share; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.opensearch.accesscontrol.resources.ShareWith; +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; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.GET; + +public class ShareResourceRestAction extends BaseRestHandler { + + public ShareResourceRestAction() {} + + @Override + public List routes() { + return singletonList(new Route(GET, "/_plugins/sample_resource_sharing/share/{resource_id}")); + } + + @Override + public String getName() { + return "list_sample_resources"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + Map source; + try (XContentParser parser = request.contentParser()) { + source = parser.map(); + } + + String resourceId = (String) source.get("resource_id"); + ShareWith shareWith = (ShareWith) source.get("share_with"); + final ShareResourceRequest shareResourceRequest = new ShareResourceRequest(resourceId, shareWith); + return channel -> client.executeLocally(ShareResourceAction.INSTANCE, shareResourceRequest, new RestToXContentListener<>(channel)); + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java new file mode 100644 index 0000000000..2e57786a13 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java @@ -0,0 +1,25 @@ +/* + * 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.actions.verify; + +import org.opensearch.action.ActionType; + +/** + * Action to verify resource access for current user + */ +public class VerifyResourceAccessAction extends ActionType { + + public static final VerifyResourceAccessAction INSTANCE = new VerifyResourceAccessAction(); + + public static final String NAME = "cluster:admin/sampleresource/verify/resource_access"; + + private VerifyResourceAccessAction() { + super(NAME, VerifyResourceAccessResponse::new); + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java new file mode 100644 index 0000000000..e9b20118db --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java @@ -0,0 +1,69 @@ +/* + * 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.actions.verify; + +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; + +public class VerifyResourceAccessRequest extends ActionRequest { + + private final String resourceId; + + private final String sourceIdx; + + private final String scope; + + /** + * Default constructor + */ + public VerifyResourceAccessRequest(String resourceId, String sourceIdx, String scope) { + this.resourceId = resourceId; + this.sourceIdx = sourceIdx; + this.scope = scope; + } + + /** + * Constructor with stream input + * @param in the stream input + * @throws IOException IOException + */ + public VerifyResourceAccessRequest(final StreamInput in) throws IOException { + this.resourceId = in.readString(); + this.sourceIdx = in.readString(); + this.scope = in.readString(); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeString(resourceId); + out.writeString(sourceIdx); + out.writeString(scope); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + public String getResourceId() { + return resourceId; + } + + public String getSourceIdx() { + return sourceIdx; + } + + public String getScope() { + return scope; + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessResponse.java similarity index 81% rename from sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceResponse.java rename to sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessResponse.java index 86796bfff5..660ac03f71 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateSampleResourceResponse.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessResponse.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.sample.actions.create; +package org.opensearch.sample.actions.verify; import java.io.IOException; @@ -16,10 +16,7 @@ import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; -/** - * Response to a CreateSampleResourceRequest - */ -public class CreateSampleResourceResponse extends ActionResponse implements ToXContentObject { +public class VerifyResourceAccessResponse extends ActionResponse implements ToXContentObject { private final String message; /** @@ -27,7 +24,7 @@ public class CreateSampleResourceResponse extends ActionResponse implements ToXC * * @param message The message */ - public CreateSampleResourceResponse(String message) { + public VerifyResourceAccessResponse(String message) { this.message = message; } @@ -41,7 +38,7 @@ public void writeTo(StreamOutput out) throws IOException { * * @param in the stream input */ - public CreateSampleResourceResponse(final StreamInput in) throws IOException { + public VerifyResourceAccessResponse(final StreamInput in) throws IOException { message = in.readString(); } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java new file mode 100644 index 0000000000..34bfed4e9f --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java @@ -0,0 +1,52 @@ +/* + * 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.actions.verify; + +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; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.POST; + +public class VerifyResourceAccessRestAction extends BaseRestHandler { + + public VerifyResourceAccessRestAction() {} + + @Override + public List routes() { + return singletonList(new Route(POST, "/_plugins/sample_resource_sharing/verify_resource_access")); + } + + @Override + public String getName() { + return "verify_resource_access"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + Map source; + try (XContentParser parser = request.contentParser()) { + source = parser.map(); + } + + String resourceIdx = (String) source.get("resource_idx"); + String sourceIdx = (String) source.get("source_idx"); + String scope = (String) source.get("scope"); + + // final CreateResourceRequest createSampleResourceRequest = new CreateResourceRequest<>(resource); + return channel -> client.executeLocally(VerifyResourceAccessAction.INSTANCE, null, new RestToXContentListener<>(channel)); + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java index 5e2eb6d723..d3bb8f19b2 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java @@ -17,8 +17,6 @@ import org.opensearch.accesscontrol.resources.ResourceService; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; -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; @@ -27,10 +25,11 @@ 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.sample.Resource; import org.opensearch.sample.SampleResourcePlugin; +import org.opensearch.sample.actions.create.CreateResourceRequest; +import org.opensearch.sample.actions.create.CreateResourceResponse; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -39,9 +38,7 @@ /** * Transport action for CreateSampleResource. */ -public class CreateResourceTransportAction extends HandledTransportAction< - CreateResourceRequest, - CreateResourceResponse> { +public class CreateResourceTransportAction extends HandledTransportAction { private static final Logger log = LogManager.getLogger(CreateResourceTransportAction.class); private final TransportService transportService; @@ -53,31 +50,25 @@ public CreateResourceTransportAction( ActionFilters actionFilters, Client nodeClient, String actionName, - String resourceIndex, - Writeable.Reader resourceReader + String resourceIndex ) { - super(actionName, transportService, actionFilters, (in) -> new CreateResourceRequest(in, resourceReader)); + super(actionName, transportService, actionFilters, (in) -> new CreateResourceRequest(in)); this.transportService = transportService; this.nodeClient = nodeClient; this.resourceIndex = resourceIndex; } @Override - protected void doExecute(Task task, CreateResourceRequest request, ActionListener listener) { + 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); + createResource(request, listener); + listener.onResponse(new CreateResourceResponse("Resource " + request.getResource() + " created successfully.")); + } catch (Exception e) { + listener.onFailure(e); } } - private void createResource(CreateResourceRequest request, ActionListener listener) { + private void createResource(CreateResourceRequest request, ActionListener listener) { Resource sample = request.getResource(); try { IndexRequest ir = nodeClient.prepareIndex(resourceIndex) @@ -104,5 +95,4 @@ private static ActionListener getIndexResponseActionListener(Acti }, listener::onFailure); } - // TODO add delete implementation as a separate transport action } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java new file mode 100644 index 0000000000..c4734ad928 --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java @@ -0,0 +1,56 @@ +/* + * 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.transport; + +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.accesscontrol.resources.ResourceService; +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.sample.SampleResourcePlugin; +import org.opensearch.sample.actions.list.ListAccessibleResourcesAction; +import org.opensearch.sample.actions.list.ListAccessibleResourcesRequest; +import org.opensearch.sample.actions.list.ListAccessibleResourcesResponse; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; + +/** + * Transport action for ListSampleResource. + */ +public class ListAccessibleResourcesTransportAction extends HandledTransportAction< + ListAccessibleResourcesRequest, + ListAccessibleResourcesResponse> { + private static final Logger log = LogManager.getLogger(ListAccessibleResourcesTransportAction.class); + + @Inject + public ListAccessibleResourcesTransportAction(TransportService transportService, ActionFilters actionFilters) { + super(ListAccessibleResourcesAction.NAME, transportService, actionFilters, ListAccessibleResourcesRequest::new); + } + + @Override + protected void doExecute(Task task, ListAccessibleResourcesRequest request, ActionListener listener) { + try { + ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); + List resourceIds = rs.getResourceAccessControlPlugin().listAccessibleResourcesForPlugin(RESOURCE_INDEX_NAME); + log.info("Successfully fetched accessible resources for current user"); + listener.onResponse(new ListAccessibleResourcesResponse(resourceIds)); + } catch (Exception e) { + log.info("Failed to list accessible resources for current user: ", e); + listener.onFailure(e); + } + + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java new file mode 100644 index 0000000000..0dfab3fade --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java @@ -0,0 +1,77 @@ +/* + * 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.transport; + +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.accesscontrol.resources.ResourceService; +import org.opensearch.accesscontrol.resources.ResourceSharing; +import org.opensearch.accesscontrol.resources.ShareWith; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.client.Client; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.sample.SampleResourcePlugin; +import org.opensearch.sample.actions.share.ShareResourceRequest; +import org.opensearch.sample.actions.share.ShareResourceResponse; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; + +/** + * Transport action for CreateSampleResource. + */ +public class ShareResourceTransportAction extends HandledTransportAction { + private static final Logger log = LogManager.getLogger(ShareResourceTransportAction.class); + + private final TransportService transportService; + private final Client nodeClient; + private final String resourceIndex; + + public ShareResourceTransportAction( + TransportService transportService, + ActionFilters actionFilters, + Client nodeClient, + String actionName, + String resourceIndex + ) { + super(actionName, transportService, actionFilters, ShareResourceRequest::new); + this.transportService = transportService; + this.nodeClient = nodeClient; + this.resourceIndex = resourceIndex; + } + + @Override + protected void doExecute(Task task, ShareResourceRequest request, ActionListener listener) { + try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { + shareResource(request); + listener.onResponse(new ShareResourceResponse("Resource " + request.getResourceId() + " shared successfully.")); + } catch (Exception e) { + listener.onFailure(e); + } + } + + private void shareResource(ShareResourceRequest request) { + try { + ShareWith shareWith = new ShareWith(List.of()); + ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); + ResourceSharing sharing = rs.getResourceAccessControlPlugin() + .shareWith(request.getResourceId(), RESOURCE_INDEX_NAME, shareWith); + log.info("Shared resource : {} with {}", request.getResourceId(), sharing.toString()); + } catch (Exception e) { + log.info("Failed to share resource {}", request.getResourceId(), e); + throw e; + } + } +} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java new file mode 100644 index 0000000000..947dcec59e --- /dev/null +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java @@ -0,0 +1,58 @@ +/* + * 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.transport; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.accesscontrol.resources.ResourceService; +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.sample.SampleResourcePlugin; +import org.opensearch.sample.actions.verify.VerifyResourceAccessAction; +import org.opensearch.sample.actions.verify.VerifyResourceAccessRequest; +import org.opensearch.sample.actions.verify.VerifyResourceAccessResponse; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +public class VerifyResourceAccessTransportAction extends HandledTransportAction { + private static final Logger log = LogManager.getLogger(VerifyResourceAccessTransportAction.class); + + @Inject + public VerifyResourceAccessTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { + super(VerifyResourceAccessAction.NAME, transportService, actionFilters, VerifyResourceAccessRequest::new); + } + + @Override + protected void doExecute(Task task, VerifyResourceAccessRequest request, ActionListener listener) { + try { + ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); + boolean hasRequestedScopeAccess = rs.getResourceAccessControlPlugin() + .hasPermission(request.getResourceId(), request.getSourceIdx(), request.getScope()); + + StringBuilder sb = new StringBuilder(); + sb.append("User does"); + sb.append(hasRequestedScopeAccess ? " " : " not "); + sb.append("have requested scope "); + sb.append(request.getScope()); + sb.append(" access to "); + sb.append(request.getResourceId()); + + log.info(sb.toString()); + listener.onResponse(new VerifyResourceAccessResponse(sb.toString())); + } catch (Exception e) { + log.info("Failed to check user permissions for resource {}", request.getResourceId(), e); + listener.onFailure(e); + } + } + +} diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 142c6b67da..9c26811dc9 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -17,6 +17,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.accesscontrol.resources.CreatedBy; import org.opensearch.accesscontrol.resources.EntityType; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; @@ -61,10 +62,11 @@ public boolean hasPermission(String resourceId, String systemIndexName, String s public ResourceSharing shareWith(String resourceId, String systemIndexName, ShareWith shareWith) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user.getName(), shareWith); + LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user, shareWith); // TODO add concrete implementation - return null; + CreatedBy c = new CreatedBy("", null); + return new ResourceSharing(systemIndexName, resourceId, c, shareWith); } public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { From 81216f17d6b61ef11b4c393362b92ecd2b861477 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 18:24:05 -0400 Subject: [PATCH 15/42] Updates method references to conform to core Signed-off-by: Darshit Chanpura --- .../sample/transport/CreateResourceTransportAction.java | 2 ++ .../ListAccessibleResourcesTransportAction.java | 2 +- .../sample/transport/ShareResourceTransportAction.java | 2 ++ .../opensearch/security/OpenSearchSecurityPlugin.java | 9 ++------- .../security/resources/ResourceAccessHandler.java | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java index d3bb8f19b2..44d18ef846 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java @@ -23,6 +23,7 @@ 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; @@ -45,6 +46,7 @@ public class CreateResourceTransportAction extends HandledTransportAction listener) { try { ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); - List resourceIds = rs.getResourceAccessControlPlugin().listAccessibleResourcesForPlugin(RESOURCE_INDEX_NAME); + List resourceIds = rs.getResourceAccessControlPlugin().listAccessibleResourcesInPlugin(RESOURCE_INDEX_NAME); log.info("Successfully fetched accessible resources for current user"); listener.onResponse(new ListAccessibleResourcesResponse(resourceIds)); } catch (Exception e) { diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java index 0dfab3fade..ff1541773e 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java @@ -19,6 +19,7 @@ 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.sample.SampleResourcePlugin; @@ -39,6 +40,7 @@ public class ShareResourceTransportAction extends HandledTransportAction> listAccessibleResources() { - return this.resourceAccessHandler.listAccessibleResources(); - } - - @Override - public List listAccessibleResourcesForPlugin(String systemIndexName) { - return this.resourceAccessHandler.listAccessibleResourcesForPlugin(systemIndexName); + public List listAccessibleResourcesInPlugin(String systemIndexName) { + return this.resourceAccessHandler.listAccessibleResourcesInPlugin(systemIndexName); } @Override diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 9c26811dc9..838785ee7f 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -44,7 +44,7 @@ public Map> listAccessibleResources() { return Map.of(); } - public List listAccessibleResourcesForPlugin(String systemIndex) { + public List listAccessibleResourcesInPlugin(String systemIndex) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Listing accessible resource within a system index {} for : {}", systemIndex, user.getName()); From 1e33dad85da54f7074a2f50715006fd7e30a5e57 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 4 Oct 2024 18:58:31 -0400 Subject: [PATCH 16/42] Fixes compile errors Signed-off-by: Darshit Chanpura --- .../actions/create/CreateResourceAction.java | 2 +- .../list/ListAccessibleResourcesAction.java | 2 +- .../actions/share/ShareResourceAction.java | 2 +- .../verify/VerifyResourceAccessAction.java | 2 +- .../CreateResourceTransportAction.java | 16 +++++--------- .../ShareResourceTransportAction.java | 22 ++++--------------- 6 files changed, 13 insertions(+), 33 deletions(-) diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java index 5ddcc79008..e7c02278ab 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java @@ -21,7 +21,7 @@ public class CreateResourceAction extends ActionType { /** * Create sample resource action name */ - public static final String NAME = "cluster:admin/sampleresource/create"; + public static final String NAME = "cluster:admin/sample-resource-plugin/create"; private CreateResourceAction() { super(NAME, CreateResourceResponse::new); diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java index cc7e4769f6..b4e9e29e22 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java @@ -21,7 +21,7 @@ public class ListAccessibleResourcesAction extends ActionType { /** * List sample resource action name */ - public static final String NAME = "cluster:admin/sampleresource/share"; + public static final String NAME = "cluster:admin/sample-resource-plugin/share"; private ShareResourceAction() { super(NAME, ShareResourceResponse::new); diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java index 2e57786a13..1378d561f5 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java @@ -17,7 +17,7 @@ public class VerifyResourceAccessAction extends ActionType new CreateResourceRequest(in)); + public CreateResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { + super(CreateResourceAction.NAME, transportService, actionFilters, CreateResourceRequest::new); this.transportService = transportService; this.nodeClient = nodeClient; - this.resourceIndex = resourceIndex; } @Override @@ -73,7 +67,7 @@ protected void doExecute(Task task, CreateResourceRequest request, ActionListene private void createResource(CreateResourceRequest request, ActionListener listener) { Resource sample = request.getResource(); try { - IndexRequest ir = nodeClient.prepareIndex(resourceIndex) + IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .setSource(sample.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) .request(); diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java index ff1541773e..ccbfc31b78 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java @@ -18,11 +18,10 @@ import org.opensearch.accesscontrol.resources.ShareWith; 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.sample.SampleResourcePlugin; +import org.opensearch.sample.actions.share.ShareResourceAction; import org.opensearch.sample.actions.share.ShareResourceRequest; import org.opensearch.sample.actions.share.ShareResourceResponse; import org.opensearch.tasks.Task; @@ -36,27 +35,14 @@ public class ShareResourceTransportAction extends HandledTransportAction { private static final Logger log = LogManager.getLogger(ShareResourceTransportAction.class); - private final TransportService transportService; - private final Client nodeClient; - private final String resourceIndex; - @Inject - public ShareResourceTransportAction( - TransportService transportService, - ActionFilters actionFilters, - Client nodeClient, - String actionName, - String resourceIndex - ) { - super(actionName, transportService, actionFilters, ShareResourceRequest::new); - this.transportService = transportService; - this.nodeClient = nodeClient; - this.resourceIndex = resourceIndex; + public ShareResourceTransportAction(TransportService transportService, ActionFilters actionFilters) { + super(ShareResourceAction.NAME, transportService, actionFilters, ShareResourceRequest::new); } @Override protected void doExecute(Task task, ShareResourceRequest request, ActionListener listener) { - try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { + try { shareResource(request); listener.onResponse(new ShareResourceResponse("Resource " + request.getResourceId() + " shared successfully.")); } catch (Exception e) { From a671cc16a7ff47b351509c2cd31c86ac386cb264 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Mon, 14 Oct 2024 17:12:55 -0400 Subject: [PATCH 17/42] Fixes some names and method implementations Signed-off-by: Darshit Chanpura --- .../org/opensearch/sample/SampleResourcePlugin.java | 9 ++++++++- .../org/opensearch/sample/SampleResourceScope.java | 2 +- .../sample/actions/share/ShareResourceRestAction.java | 2 +- .../transport/CreateResourceTransportAction.java | 11 ++++++++++- .../services/org.opensearch.plugins.ResourcePlugin | 1 + 5 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java index abc9ed4de7..a96a3d52ff 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java @@ -46,7 +46,9 @@ 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; @@ -98,7 +100,12 @@ public List getRestHandlers( IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster ) { - return List.of(new CreateResourceRestAction(), new ListAccessibleResourcesRestAction()); + return List.of( + new CreateResourceRestAction(), + new ListAccessibleResourcesRestAction(), + new VerifyResourceAccessRestAction(), + new ShareResourceRestAction() + ); } @Override diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java index 2784de45b7..90df0d3764 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java @@ -17,7 +17,7 @@ * This class demonstrates a sample implementation of Basic Access Scopes to fit each plugin's use-case. * The plugin then uses this scope when seeking access evaluation for a user on a particular resource. */ -enum SampleResourceScope implements ResourceAccessScope { +public enum SampleResourceScope implements ResourceAccessScope { SAMPLE_FULL_ACCESS("sample_full_access"); diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java index 87bc083f2e..347fb49e68 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java @@ -33,7 +33,7 @@ public List routes() { @Override public String getName() { - return "list_sample_resources"; + return "share_sample_resources"; } @Override diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java index 985d80b919..2de452a5de 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java @@ -17,6 +17,7 @@ import org.opensearch.accesscontrol.resources.ResourceService; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; +import org.opensearch.accesscontrol.resources.SharedWithScope; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.support.ActionFilters; @@ -29,6 +30,7 @@ import org.opensearch.core.xcontent.ToXContent; import org.opensearch.sample.Resource; import org.opensearch.sample.SampleResourcePlugin; +import org.opensearch.sample.SampleResourceScope; import org.opensearch.sample.actions.create.CreateResourceAction; import org.opensearch.sample.actions.create.CreateResourceRequest; import org.opensearch.sample.actions.create.CreateResourceResponse; @@ -60,6 +62,7 @@ protected void doExecute(Task task, CreateResourceRequest request, ActionListene createResource(request, listener); listener.onResponse(new CreateResourceResponse("Resource " + request.getResource() + " created successfully.")); } catch (Exception e) { + log.info("Failed to create resource", e); listener.onFailure(e); } } @@ -82,7 +85,13 @@ private void createResource(CreateResourceRequest request, ActionListener getIndexResponseActionListener(ActionListener listener) { - ShareWith shareWith = new ShareWith(List.of()); + SharedWithScope.SharedWithPerScope sharedWithPerScope = new SharedWithScope.SharedWithPerScope( + List.of(), + List.of(), + List.of() + ); + SharedWithScope sharedWithScope = new SharedWithScope(SampleResourceScope.SAMPLE_FULL_ACCESS.getName(), sharedWithPerScope); + ShareWith shareWith = new ShareWith(List.of(sharedWithScope)); return ActionListener.wrap(idxResponse -> { log.info("Created resource: {}", idxResponse.toString()); ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); diff --git a/sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin b/sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin new file mode 100644 index 0000000000..1ca89eaf74 --- /dev/null +++ b/sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin @@ -0,0 +1 @@ +org.opensearch.sample.SampleResourcePlugin \ No newline at end of file From 47b73da2bc522c462d9db6c3ad7acbeff0c1caf9 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Mon, 14 Oct 2024 17:15:13 -0400 Subject: [PATCH 18/42] Adds few concrete method implementations in security plugin Signed-off-by: Darshit Chanpura --- .../CreateResourceTransportAction.java | 6 +- .../security/OpenSearchSecurityPlugin.java | 31 +++- .../resources/ResourceAccessHandler.java | 159 +++++++++++++++--- .../ResourceManagementRepository.java | 16 +- .../ResourceSharingIndexHandler.java | 97 ++++++----- .../ResourceSharingIndexListener.java | 23 ++- 6 files changed, 252 insertions(+), 80 deletions(-) diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java index 2de452a5de..8bff7b44a3 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java @@ -85,11 +85,7 @@ private void createResource(CreateResourceRequest request, ActionListener getIndexResponseActionListener(ActionListener listener) { - SharedWithScope.SharedWithPerScope sharedWithPerScope = new SharedWithScope.SharedWithPerScope( - List.of(), - List.of(), - List.of() - ); + SharedWithScope.SharedWithPerScope sharedWithPerScope = new SharedWithScope.SharedWithPerScope(List.of(), List.of(), List.of()); SharedWithScope sharedWithScope = new SharedWithScope(SampleResourceScope.SAMPLE_FULL_ACCESS.getName(), sharedWithPerScope); ShareWith shareWith = new ShareWith(List.of(sharedWithScope)); return ActionListener.wrap(idxResponse -> { diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index e7f013d936..e8b2e45cd4 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -69,6 +69,7 @@ import org.opensearch.SpecialPermission; import org.opensearch.Version; import org.opensearch.accesscontrol.resources.EntityType; +import org.opensearch.accesscontrol.resources.ResourceService; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; import org.opensearch.action.ActionRequest; @@ -119,11 +120,13 @@ 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; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.ResourceAccessControlPlugin; +import org.opensearch.plugins.ResourcePlugin; import org.opensearch.plugins.SecureHttpTransportSettingsProvider; import org.opensearch.plugins.SecureSettingsFactory; import org.opensearch.plugins.SecureTransportSettingsProvider; @@ -179,6 +182,7 @@ import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resources.ResourceAccessHandler; import org.opensearch.security.resources.ResourceManagementRepository; +import org.opensearch.security.resources.ResourceSharingIndexHandler; import org.opensearch.security.resources.ResourceSharingIndexListener; import org.opensearch.security.rest.DashboardsInfoAction; import org.opensearch.security.rest.SecurityConfigUpdateAction; @@ -237,10 +241,11 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin implements ClusterPlugin, MapperPlugin, + IdentityPlugin, + ResourceAccessControlPlugin, // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings ExtensionAwarePlugin, - IdentityPlugin, - ResourceAccessControlPlugin + ExtensiblePlugin // CS-ENFORCE-SINGLE { @@ -845,6 +850,20 @@ public void onQueryPhase(SearchContext searchContext, long tookInNanos) { } } + // CS-SUPPRESS-SINGLE: RegexpSingleline Extensions manager used to allow/disallow TLS connections to extensions + @Override + public void loadExtensions(ExtensionLoader loader) { + + log.info("Loading resource plugins"); + for (ResourcePlugin resourcePlugin : loader.loadExtensions(ResourcePlugin.class)) { + String resourceIndex = resourcePlugin.getResourceIndex(); + + this.indicesToListen.add(resourceIndex); + log.info("Loaded resource plugin: {}, index: {}", resourcePlugin, resourceIndex); + } + } + // CS-ENFORCE-SINGLE + @Override public List getActionFilters() { List filters = new ArrayList<>(1); @@ -1209,9 +1228,11 @@ public Collection createComponents( e.subscribeForChanges(dcf); } - resourceAccessHandler = new ResourceAccessHandler(threadPool); + final var resourceSharingIndex = ConfigConstants.OPENSEARCH_RESOURCE_SHARING_INDEX; + ResourceSharingIndexHandler rsIndexHandler = new ResourceSharingIndexHandler(resourceSharingIndex, localClient, threadPool); + resourceAccessHandler = new ResourceAccessHandler(threadPool, rsIndexHandler, adminDns); - rmr = ResourceManagementRepository.create(settings, threadPool, localClient); + rmr = ResourceManagementRepository.create(settings, threadPool, localClient, rsIndexHandler); components.add(adminDns); components.add(cr); @@ -2087,6 +2108,7 @@ public void onNodeStarted(DiscoveryNode localNode) { if (!SSLConfig.isSslOnlyMode() && !client && !disabled && !useClusterStateToInitSecurityConfig(settings)) { cr.initOnNodeStart(); } + // create resource sharing index if absent rmr.createResourceSharingIndexIfAbsent(); final Set securityModules = ReflectionHelper.getModulesLoaded(); @@ -2226,6 +2248,7 @@ public static class GuiceHolder implements LifecycleComponent { private static RemoteClusterService remoteClusterService; private static IndicesService indicesService; private static PitService pitService; + private static ResourceService resourceService; // CS-SUPPRESS-SINGLE: RegexpSingleline Extensions manager used to allow/disallow TLS connections to extensions private static ExtensionsManager extensionsManager; diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 838785ee7f..32fa077e71 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -11,8 +11,11 @@ package org.opensearch.security.resources; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -21,7 +24,9 @@ import org.opensearch.accesscontrol.resources.EntityType; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; +import org.opensearch.accesscontrol.resources.SharedWithScope; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.User; import org.opensearch.threadpool.ThreadPool; @@ -30,67 +35,177 @@ public class ResourceAccessHandler { private static final Logger LOGGER = LogManager.getLogger(ResourceAccessHandler.class); private final ThreadContext threadContext; - - public ResourceAccessHandler(final ThreadPool threadPool) { + private final ResourceSharingIndexHandler resourceSharingIndexHandler; + private final AdminDNs adminDNs; + + public ResourceAccessHandler( + final ThreadPool threadPool, + final ResourceSharingIndexHandler resourceSharingIndexHandler, + AdminDNs adminDns + ) { super(); this.threadContext = threadPool.getThreadContext(); - } - - public Map> listAccessibleResources() { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - LOGGER.info("Listing accessible resource for: {}", user.getName()); - - // TODO add concrete implementation - return Map.of(); + this.resourceSharingIndexHandler = resourceSharingIndexHandler; + this.adminDNs = adminDns; } public List listAccessibleResourcesInPlugin(String systemIndex) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + if (user == null) { + LOGGER.info("Unable to fetch user details "); + return Collections.emptyList(); + } + LOGGER.info("Listing accessible resource within a system index {} for : {}", systemIndex, user.getName()); - // TODO add concrete implementation - return List.of(); + // TODO check if user is admin, if yes all resources should be accessible + if (adminDNs.isAdmin(user)) { + return loadAllResources(systemIndex); + } + + Set result = new HashSet<>(); + + // 0. Own resources + result.addAll(loadOwnResources(systemIndex, user.getName())); + + // 1. By username + result.addAll(loadSharedWithResources(systemIndex, Set.of(user.getName()), "users")); + + // 2. By roles + Set roles = user.getSecurityRoles(); + result.addAll(loadSharedWithResources(systemIndex, roles, "roles")); + + // 3. By backend_roles + Set backendRoles = user.getRoles(); + result.addAll(loadSharedWithResources(systemIndex, backendRoles, "backend_roles")); + + return result.stream().toList(); } public boolean hasPermission(String resourceId, String systemIndexName, String scope) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Checking if {} has {} permission to resource {}", user.getName(), scope, resourceId); - // TODO add concrete implementation + Set userRoles = user.getSecurityRoles(); + Set userBackendRoles = user.getRoles(); + + ResourceSharing document = this.resourceSharingIndexHandler.fetchDocumentById(systemIndexName, resourceId); + if (document == null) { + LOGGER.warn("Resource {} not found in index {}", resourceId, systemIndexName); + return false; // If the document doesn't exist, no permissions can be granted + } + + if (isSharedWithEveryone(document) + || isOwnerOfResource(document, user.getName()) + || isSharedWithUser(document, user.getName(), scope) + || isSharedWithGroup(document, userRoles, scope) + || isSharedWithGroup(document, userBackendRoles, scope)) { + LOGGER.info("User {} has {} access to {}", user.getName(), scope, resourceId); + return true; + } + + LOGGER.info("User {} does not have {} access to {} ", user.getName(), scope, resourceId); return false; } public ResourceSharing shareWith(String resourceId, String systemIndexName, ShareWith shareWith) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user, shareWith); + LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user, shareWith.toString()); - // TODO add concrete implementation - CreatedBy c = new CreatedBy("", null); - return new ResourceSharing(systemIndexName, resourceId, c, shareWith); + // TODO fix this to fetch user-name correctly, need to hydrate user context since context might have been stashed. + // (persistentHeader?) + CreatedBy createdBy = new CreatedBy("", ""); + return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, createdBy, shareWith); } public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Revoking access to resource {} created by {} for {}", resourceId, user.getName(), revokeAccess); - // TODO add concrete implementation - return null; + return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess); } public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Deleting resource sharing record for resource {} in {} created by {}", resourceId, systemIndexName, user.getName()); - // TODO add concrete implementation - return false; + ResourceSharing document = this.resourceSharingIndexHandler.fetchDocumentById(systemIndexName, resourceId); + if (document == null) { + LOGGER.info("Document {} does not exist in index {}", resourceId, systemIndexName); + return false; + } + if (!(adminDNs.isAdmin(user) || isOwnerOfResource(document, user.getName()))) { + LOGGER.info("User {} does not have access to delete the record {} ", user.getName(), resourceId); + return false; + } + return this.resourceSharingIndexHandler.deleteResourceSharingRecord(resourceId, systemIndexName); } public boolean deleteAllResourceSharingRecordsForCurrentUser() { final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Deleting all resource sharing records for resource {}", user.getName()); - // TODO add concrete implementation + return this.resourceSharingIndexHandler.deleteAllRecordsForUser(user.getName()); + } + + // Helper methods + + private List loadAllResources(String systemIndex) { + return this.resourceSharingIndexHandler.fetchAllDocuments(systemIndex); + } + + private List loadOwnResources(String systemIndex, String username) { + // TODO check if this magic variable can be replaced + return this.resourceSharingIndexHandler.fetchDocumentsByField(systemIndex, "created_by.user", username); + } + + private List loadSharedWithResources(String systemIndex, Set accessWays, String shareWithType) { + return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(systemIndex, accessWays, shareWithType); + } + + private boolean isOwnerOfResource(ResourceSharing document, String userName) { + return document.getCreatedBy() != null && document.getCreatedBy().getUser().equals(userName); + } + + private boolean isSharedWithUser(ResourceSharing document, String userName, String scope) { + return checkSharing(document, "users", userName, scope); + } + + private boolean isSharedWithGroup(ResourceSharing document, Set roles, String scope) { + for (String role : roles) { + if (checkSharing(document, "roles", role, scope)) { + return true; + } + } return false; } + private boolean isSharedWithEveryone(ResourceSharing document) { + return document.getShareWith() != null + && document.getShareWith().getSharedWithScopes().stream().anyMatch(sharedWithScope -> sharedWithScope.getScope().equals("*")); + } + + private boolean checkSharing(ResourceSharing document, String sharingType, String identifier, String scope) { + if (document.getShareWith() == null) { + return false; + } + + return document.getShareWith() + .getSharedWithScopes() + .stream() + .filter(sharedWithScope -> sharedWithScope.getScope().equals(scope)) + .findFirst() + .map(sharedWithScope -> { + SharedWithScope.SharedWithPerScope scopePermissions = sharedWithScope.getSharedWithPerScope(); + + return switch (sharingType) { + case "users" -> scopePermissions.getUsers().contains(identifier); + case "roles" -> scopePermissions.getRoles().contains(identifier); + case "backend_roles" -> scopePermissions.getBackendRoles().contains(identifier); + default -> false; + }; + }) + .orElse(false); // Return false if no matching scope is found + } + } diff --git a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java index 7e347a331d..da3678728d 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java +++ b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java @@ -17,7 +17,6 @@ import org.opensearch.client.Client; import org.opensearch.common.settings.Settings; import org.opensearch.security.configuration.ConfigurationRepository; -import org.opensearch.security.support.ConfigConstants; import org.opensearch.threadpool.ThreadPool; public class ResourceManagementRepository { @@ -40,13 +39,14 @@ protected ResourceManagementRepository( this.resourceSharingIndexHandler = resourceSharingIndexHandler; } - public static ResourceManagementRepository create(Settings settings, final ThreadPool threadPool, Client client) { - final var resourceSharingIndex = ConfigConstants.OPENSEARCH_RESOURCE_SHARING_INDEX; - return new ResourceManagementRepository( - threadPool, - client, - new ResourceSharingIndexHandler(resourceSharingIndex, settings, client, threadPool) - ); + public static ResourceManagementRepository create( + Settings settings, + final ThreadPool threadPool, + Client client, + ResourceSharingIndexHandler resourceSharingIndexHandler + ) { + + return new ResourceManagementRepository(threadPool, client, resourceSharingIndexHandler); } public void createResourceSharingIndexIfAbsent() { diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index b6f4b02ade..b175ad53d0 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -10,13 +10,16 @@ package org.opensearch.security.resources; import java.io.IOException; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Callable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.accesscontrol.resources.CreatedBy; +import org.opensearch.accesscontrol.resources.EntityType; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; import org.opensearch.action.admin.indices.create.CreateIndexRequest; @@ -25,7 +28,6 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; -import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; import org.opensearch.core.xcontent.ToXContent; @@ -39,38 +41,20 @@ public class ResourceSharingIndexHandler { private static final Logger LOGGER = LogManager.getLogger(ResourceSharingIndexHandler.class); - private final Settings settings; - private final Client client; private final String resourceSharingIndex; private final ThreadPool threadPool; - public ResourceSharingIndexHandler(final String indexName, final Settings settings, final Client client, ThreadPool threadPool) { + public ResourceSharingIndexHandler(final String indexName, final Client client, ThreadPool threadPool) { this.resourceSharingIndex = indexName; - this.settings = settings; this.client = client; this.threadPool = threadPool; } public final static Map INDEX_SETTINGS = Map.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); - public void createIndex(ActionListener listener) { - try (final ThreadContext.StoredContext threadContext = client.threadPool().getThreadContext().stashContext()) { - client.admin() - .indices() - .create( - new CreateIndexRequest(resourceSharingIndex).settings(INDEX_SETTINGS).waitForActiveShards(1), - ActionListener.runBefore(ActionListener.wrap(r -> { - if (r.isAcknowledged()) { - listener.onResponse(true); - } else listener.onFailure(new SecurityException("Couldn't create resource sharing index " + resourceSharingIndex)); - }, listener::onFailure), threadContext::restore) - ); - } - } - public void createResourceSharingIndexIfAbsent(Callable callable) { // TODO: Once stashContext is replaced with switchContext this call will have to be modified try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) { @@ -91,14 +75,10 @@ public void createResourceSharingIndexIfAbsent(Callable callable) { } } - public boolean indexResourceSharing( - String resourceId, - String resourceIndex, - CreatedBy createdBy, - ShareWith shareWith, - ActionListener listener - ) throws IOException { - createResourceSharingIndexIfAbsent(() -> { + public boolean indexResourceSharing(String resourceId, String resourceIndex, CreatedBy createdBy, ShareWith shareWith) + throws IOException { + + try { ResourceSharing entry = new ResourceSharing(resourceIndex, resourceId, createdBy, shareWith); IndexRequest ir = client.prepareIndex(resourceSharingIndex) @@ -108,17 +88,58 @@ public boolean indexResourceSharing( LOGGER.info("Index Request: {}", ir.toString()); - ActionListener irListener = ActionListener.wrap(idxResponse -> { - LOGGER.info("Created {} entry.", resourceSharingIndex); - listener.onResponse(idxResponse); - }, (failResponse) -> { - LOGGER.error(failResponse.getMessage()); - LOGGER.info("Failed to create {} entry.", resourceSharingIndex); - listener.onFailure(failResponse); - }); + ActionListener irListener = ActionListener.wrap( + idxResponse -> { LOGGER.info("Created {} entry.", resourceSharingIndex); }, + (failResponse) -> { + LOGGER.error(failResponse.getMessage()); + LOGGER.info("Failed to create {} entry.", resourceSharingIndex); + } + ); client.index(ir, irListener); - return null; - }); + } catch (Exception e) { + LOGGER.info("Failed to create {} entry.", resourceSharingIndex, e); + return false; + } return true; } + + public List fetchDocumentsByField(String systemIndex, String field, String value) { + LOGGER.info("Fetching documents from index: {}, where {} = {}", systemIndex, field, value); + + return List.of(); + } + + public List fetchAllDocuments(String systemIndex) { + LOGGER.info("Fetching all documents from index: {}", systemIndex); + return List.of(); + } + + public List fetchDocumentsForAllScopes(String systemIndex, Set accessWays, String shareWithType) { + return List.of(); + } + + public ResourceSharing fetchDocumentById(String systemIndexName, String resourceId) { + return null; + } + + public ResourceSharing updateResourceSharingInfo(String resourceId, String systemIndexName, CreatedBy createdBy, ShareWith shareWith) { + try { + boolean success = indexResourceSharing(resourceId, systemIndexName, createdBy, shareWith); + return success ? new ResourceSharing(resourceId, systemIndexName, createdBy, shareWith) : null; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { + return null; + } + + public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { + return false; + } + + public boolean deleteAllRecordsForUser(String name) { + return false; + } } diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java index 7a2af9f3bd..d6b1180d46 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java @@ -8,13 +8,17 @@ package org.opensearch.security.resources; +import java.io.IOException; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.accesscontrol.resources.CreatedBy; 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.security.support.ConfigConstants; import org.opensearch.threadpool.ThreadPool; /** @@ -26,6 +30,7 @@ public class ResourceSharingIndexListener implements IndexingOperationListener { private final static Logger log = LogManager.getLogger(ResourceSharingIndexListener.class); private static final ResourceSharingIndexListener INSTANCE = new ResourceSharingIndexListener(); + private ResourceSharingIndexHandler resourceSharingIndexHandler; private boolean initialized; @@ -52,6 +57,12 @@ public void initialize(ThreadPool threadPool, Client client) { this.threadPool = threadPool; this.client = client; + this.resourceSharingIndexHandler = new ResourceSharingIndexHandler( + ConfigConstants.OPENSEARCH_RESOURCE_SHARING_INDEX, + client, + threadPool + ); + ; } @@ -60,19 +71,25 @@ public boolean isInitialized() { } @Override - public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult result) { // implement a check to see if a resource was updated - log.warn("postIndex called on " + shardId.getIndexName()); + log.info("postIndex called on {}", shardId.getIndexName()); String resourceId = index.id(); String resourceIndex = shardId.getIndexName(); + + try { + this.resourceSharingIndexHandler.indexResourceSharing(resourceId, resourceIndex, new CreatedBy("bleh", ""), null); + log.info("successfully indexed resource {}", resourceId); + } catch (IOException e) { + log.info("failed to index resource {}", resourceId); + throw new RuntimeException(e); + } } @Override - public void postDelete(ShardId shardId, Engine.Delete delete, Engine.DeleteResult result) { // implement a check to see if a resource was deleted From 8942a800623449ebb626cda5a58e5e9bd1791c21 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Tue, 15 Oct 2024 01:09:57 -0400 Subject: [PATCH 19/42] Adds capability to introduce index listeners for all resource plugins Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 37 ++++++++++--------- .../transport/SecurityInterceptorTests.java | 4 +- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index e8b2e45cd4..96aa7c2bf6 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -120,7 +120,6 @@ 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; @@ -244,8 +243,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin IdentityPlugin, ResourceAccessControlPlugin, // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings - ExtensionAwarePlugin, - ExtensiblePlugin + ExtensionAwarePlugin // CS-ENFORCE-SINGLE { @@ -726,6 +724,7 @@ public void onIndexModule(IndexModule indexModule) { ) ); + log.info("Indices to listen to: {}", this.indicesToListen); if (this.indicesToListen.contains(indexModule.getIndex().getName())) { indexModule.addIndexOperationListener(ResourceSharingIndexListener.getInstance()); log.warn("Security plugin started listening to operations on index {}", indexModule.getIndex().getName()); @@ -850,20 +849,6 @@ public void onQueryPhase(SearchContext searchContext, long tookInNanos) { } } - // CS-SUPPRESS-SINGLE: RegexpSingleline Extensions manager used to allow/disallow TLS connections to extensions - @Override - public void loadExtensions(ExtensionLoader loader) { - - log.info("Loading resource plugins"); - for (ResourcePlugin resourcePlugin : loader.loadExtensions(ResourcePlugin.class)) { - String resourceIndex = resourcePlugin.getResourceIndex(); - - this.indicesToListen.add(resourceIndex); - log.info("Loaded resource plugin: {}, index: {}", resourcePlugin, resourceIndex); - } - } - // CS-ENFORCE-SINGLE - @Override public List getActionFilters() { List filters = new ArrayList<>(1); @@ -2111,6 +2096,15 @@ public void onNodeStarted(DiscoveryNode localNode) { // create resource sharing index if absent rmr.createResourceSharingIndexIfAbsent(); + + log.info("Loading resource plugins"); + for (ResourcePlugin resourcePlugin : OpenSearchSecurityPlugin.GuiceHolder.getResourceService().listResourcePlugins()) { + String resourceIndex = resourcePlugin.getResourceIndex(); + + this.indicesToListen.add(resourceIndex); + log.info("Loaded resource plugin: {}, index: {}", resourcePlugin, resourceIndex); + } + final Set securityModules = ReflectionHelper.getModulesLoaded(); log.info("{} OpenSearch Security modules loaded so far: {}", securityModules.size(), securityModules); } @@ -2128,6 +2122,7 @@ public Collection> getGuiceServiceClasses() final List> services = new ArrayList<>(1); services.add(GuiceHolder.class); + log.info("Guice service classes loaded"); return services; } @@ -2259,13 +2254,15 @@ public GuiceHolder( final TransportService remoteClusterService, IndicesService indicesService, PitService pitService, - ExtensionsManager extensionsManager + ExtensionsManager extensionsManager, + ResourceService resourceService ) { GuiceHolder.repositoriesService = repositoriesService; GuiceHolder.remoteClusterService = remoteClusterService.getRemoteClusterService(); GuiceHolder.indicesService = indicesService; GuiceHolder.pitService = pitService; GuiceHolder.extensionsManager = extensionsManager; + GuiceHolder.resourceService = resourceService; } // CS-ENFORCE-SINGLE @@ -2291,6 +2288,10 @@ public static ExtensionsManager getExtensionsManager() { } // CS-ENFORCE-SINGLE + public static ResourceService getResourceService() { + return resourceService; + } + @Override public void close() {} diff --git a/src/test/java/org/opensearch/security/transport/SecurityInterceptorTests.java b/src/test/java/org/opensearch/security/transport/SecurityInterceptorTests.java index d12fafb247..0f7d5c59c5 100644 --- a/src/test/java/org/opensearch/security/transport/SecurityInterceptorTests.java +++ b/src/test/java/org/opensearch/security/transport/SecurityInterceptorTests.java @@ -20,6 +20,7 @@ import org.junit.Test; import org.opensearch.Version; +import org.opensearch.accesscontrol.resources.ResourceService; import org.opensearch.action.search.PitService; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.node.DiscoveryNode; @@ -171,7 +172,8 @@ public void setup() { transportService, mock(IndicesService.class), mock(PitService.class), - mock(ExtensionsManager.class) + mock(ExtensionsManager.class), + mock(ResourceService.class) ); // CS-ENFORCE-SINGLE From 2b06603da9d2742efa2c8d984fac044984a246ba Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Mon, 11 Nov 2024 13:08:18 -0500 Subject: [PATCH 20/42] Removes sampleplugin to be added in a separate PR Signed-off-by: Darshit Chanpura --- sample-resource-plugin/build.gradle | 166 ---------------- .../java/org/opensearch/sample/Resource.java | 19 -- .../sample/SampleResourcePlugin.java | 178 ------------------ .../sample/SampleResourceScope.java | 33 ---- .../actions/create/CreateResourceAction.java | 29 --- .../actions/create/CreateResourceRequest.java | 50 ----- .../create/CreateResourceResponse.java | 55 ------ .../create/CreateResourceRestAction.java | 55 ------ .../sample/actions/create/SampleResource.java | 56 ------ .../list/ListAccessibleResourcesAction.java | 29 --- .../list/ListAccessibleResourcesRequest.java | 39 ---- .../list/ListAccessibleResourcesResponse.java | 46 ----- .../ListAccessibleResourcesRestAction.java | 44 ----- .../actions/share/ShareResourceAction.java | 26 --- .../actions/share/ShareResourceRequest.java | 52 ----- .../actions/share/ShareResourceResponse.java | 52 ----- .../share/ShareResourceRestAction.java | 51 ----- .../verify/VerifyResourceAccessAction.java | 25 --- .../verify/VerifyResourceAccessRequest.java | 69 ------- .../verify/VerifyResourceAccessResponse.java | 52 ----- .../VerifyResourceAccessRestAction.java | 52 ----- .../CreateResourceTransportAction.java | 99 ---------- ...istAccessibleResourcesTransportAction.java | 56 ------ .../ShareResourceTransportAction.java | 65 ------- .../VerifyResourceAccessTransportAction.java | 58 ------ .../plugin-metadata/plugin-security.policy | 3 - .../org.opensearch.plugins.ResourcePlugin | 1 - .../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 3766 -> 0 bytes 34 files changed, 1621 deletions(-) delete mode 100644 sample-resource-plugin/build.gradle delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRequest.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceResponse.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRestAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRequest.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRestAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceResponse.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessResponse.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java delete mode 100644 sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java delete mode 100644 sample-resource-plugin/src/main/plugin-metadata/plugin-security.policy delete mode 100644 sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin delete mode 100644 sample-resource-plugin/src/test/resources/security/esnode-key.pem delete mode 100644 sample-resource-plugin/src/test/resources/security/esnode.pem delete mode 100644 sample-resource-plugin/src/test/resources/security/kirk-key.pem delete mode 100644 sample-resource-plugin/src/test/resources/security/kirk.pem delete mode 100644 sample-resource-plugin/src/test/resources/security/root-ca.pem delete mode 100644 sample-resource-plugin/src/test/resources/security/sample.pem delete mode 100644 sample-resource-plugin/src/test/resources/security/test-kirk.jks diff --git a/sample-resource-plugin/build.gradle b/sample-resource-plugin/build.gradle deleted file mode 100644 index e9822c1f22..0000000000 --- a/sample-resource-plugin/build.gradle +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java deleted file mode 100644 index 36e74f1624..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/Resource.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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(); -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java deleted file mode 100644 index a96a3d52ff..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourcePlugin.java +++ /dev/null @@ -1,178 +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.sample; - -import java.util.*; -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 INDEX_SETTINGS = Map.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); - - private Client client; - - @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier - ) { - this.client = client; - log.info("Loaded SampleResourcePlugin components."); - return Collections.emptyList(); - } - - @Override - public List getRestHandlers( - Settings settings, - RestController restController, - ClusterSettings clusterSettings, - IndexScopedSettings indexScopedSettings, - SettingsFilter settingsFilter, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier nodesInCluster - ) { - return List.of( - new CreateResourceRestAction(), - new ListAccessibleResourcesRestAction(), - new VerifyResourceAccessRestAction(), - new ShareResourceRestAction() - ); - } - - @Override - public List> 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 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> getGuiceServiceClasses() { - final List> 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() {} - - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java deleted file mode 100644 index 90df0d3764..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceScope.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.accesscontrol.resources.ResourceAccessScope; - -/** - * This class demonstrates a sample implementation of Basic Access Scopes to fit each plugin's use-case. - * The plugin then uses this scope when seeking access evaluation for a user on a particular resource. - */ -public enum SampleResourceScope implements ResourceAccessScope { - - SAMPLE_FULL_ACCESS("sample_full_access"); - - private final String name; - - SampleResourceScope(String scopeName) { - this.name = scopeName; - } - - public String getName() { - return name; - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java deleted file mode 100644 index e7c02278ab..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceAction.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.actions.create; - -import org.opensearch.action.ActionType; - -/** - * Action to create a sample resource - */ -public class CreateResourceAction extends ActionType { - /** - * Create sample resource action instance - */ - public static final CreateResourceAction INSTANCE = new CreateResourceAction(); - /** - * Create sample resource action name - */ - public static final String NAME = "cluster:admin/sample-resource-plugin/create"; - - private CreateResourceAction() { - super(NAME, CreateResourceResponse::new); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRequest.java deleted file mode 100644 index b31a4b7f2b..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRequest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.actions.create; - -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.sample.Resource; - -/** - * Request object for CreateSampleResource transport action - */ -public class CreateResourceRequest extends ActionRequest { - - private final Resource resource; - - /** - * Default constructor - */ - public CreateResourceRequest(Resource resource) { - this.resource = resource; - } - - public CreateResourceRequest(StreamInput in) throws IOException { - this.resource = in.readNamedWriteable(Resource.class); - } - - @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/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceResponse.java deleted file mode 100644 index 6b966ed08d..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceResponse.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.actions.create; - -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/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRestAction.java deleted file mode 100644 index 86346cc279..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/CreateResourceRestAction.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.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; - -import static java.util.Collections.singletonList; -import static org.opensearch.rest.RestRequest.Method.POST; - -public class CreateResourceRestAction extends BaseRestHandler { - - public CreateResourceRestAction() {} - - @Override - public List routes() { - return singletonList(new Route(POST, "/_plugins/sample_resource_sharing/resource")); - } - - @Override - public String getName() { - return "create_sample_resource"; - } - - @Override - 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"); - SampleResource resource = new SampleResource(); - resource.setName(name); - final CreateResourceRequest createSampleResourceRequest = new CreateResourceRequest(resource); - return channel -> client.executeLocally( - CreateResourceAction.INSTANCE, - createSampleResourceRequest, - new RestToXContentListener<>(channel) - ); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java deleted file mode 100644 index 1566abfe69..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/create/SampleResource.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.actions.create; - -import java.io.IOException; - -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.sample.Resource; - -import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; - -public class SampleResource extends Resource { - - private String name; - - public SampleResource() {} - - SampleResource(StreamInput in) throws IOException { - this.name = in.readString(); - } - - @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); - } - - @Override - public String getWriteableName() { - return "sample_resource"; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java deleted file mode 100644 index b4e9e29e22..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesAction.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.actions.list; - -import org.opensearch.action.ActionType; - -/** - * Action to list sample resources - */ -public class ListAccessibleResourcesAction extends ActionType { - /** - * List sample resource action instance - */ - public static final ListAccessibleResourcesAction INSTANCE = new ListAccessibleResourcesAction(); - /** - * List sample resource action name - */ - public static final String NAME = "cluster:admin/sample-resource-plugin/list"; - - private ListAccessibleResourcesAction() { - super(NAME, ListAccessibleResourcesResponse::new); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRequest.java deleted file mode 100644 index b4c0961774..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRequest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.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 ListAccessibleResourcesRequest extends ActionRequest { - - public ListAccessibleResourcesRequest() {} - - /** - * Constructor with stream input - * @param in the stream input - * @throws IOException IOException - */ - public ListAccessibleResourcesRequest(final StreamInput in) throws IOException {} - - @Override - public void writeTo(final StreamOutput out) throws IOException {} - - @Override - public ActionRequestValidationException validate() { - return null; - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java deleted file mode 100644 index 47a8f88e4e..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesResponse.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.actions.list; - -import java.io.IOException; -import java.util.List; - -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 ListAccessibleResourcesRequest - */ -public class ListAccessibleResourcesResponse extends ActionResponse implements ToXContentObject { - private final List resourceIds; - - public ListAccessibleResourcesResponse(List resourceIds) { - this.resourceIds = resourceIds; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeStringArray(resourceIds.toArray(new String[0])); - } - - public ListAccessibleResourcesResponse(final StreamInput in) throws IOException { - resourceIds = in.readStringList(); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.field("resource-ids", resourceIds); - builder.endObject(); - return builder; - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRestAction.java deleted file mode 100644 index bb921fce00..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/list/ListAccessibleResourcesRestAction.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.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 ListAccessibleResourcesRestAction extends BaseRestHandler { - - public ListAccessibleResourcesRestAction() {} - - @Override - public List routes() { - return singletonList(new Route(GET, "/_plugins/sample_resource_sharing/resource")); - } - - @Override - public String getName() { - return "list_sample_resources"; - } - - @Override - public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) { - final ListAccessibleResourcesRequest listAccessibleResourcesRequest = new ListAccessibleResourcesRequest(); - return channel -> client.executeLocally( - ListAccessibleResourcesAction.INSTANCE, - listAccessibleResourcesRequest, - new RestToXContentListener<>(channel) - ); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java deleted file mode 100644 index d362b1927c..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceAction.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.actions.share; - -import org.opensearch.action.ActionType; - -public class ShareResourceAction extends ActionType { - /** - * List sample resource action instance - */ - public static final ShareResourceAction INSTANCE = new ShareResourceAction(); - /** - * List sample resource action name - */ - public static final String NAME = "cluster:admin/sample-resource-plugin/share"; - - private ShareResourceAction() { - super(NAME, ShareResourceResponse::new); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java deleted file mode 100644 index 01866fd516..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRequest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.actions.share; - -import java.io.IOException; - -import org.opensearch.accesscontrol.resources.ShareWith; -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; - -public class ShareResourceRequest extends ActionRequest { - - private final String resourceId; - private final ShareWith shareWith; - - public ShareResourceRequest(String resourceId, ShareWith shareWith) { - this.resourceId = resourceId; - this.shareWith = shareWith; - } - - public ShareResourceRequest(StreamInput in) throws IOException { - this.resourceId = in.readString(); - this.shareWith = in.readNamedWriteable(ShareWith.class); - } - - @Override - public void writeTo(final StreamOutput out) throws IOException { - out.writeString(resourceId); - out.writeNamedWriteable(shareWith); - } - - @Override - public ActionRequestValidationException validate() { - return null; - } - - public String getResourceId() { - return resourceId; - } - - public ShareWith getShareWith() { - return shareWith; - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceResponse.java deleted file mode 100644 index a6a85d206d..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceResponse.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.actions.share; - -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; - -public class ShareResourceResponse extends ActionResponse implements ToXContentObject { - private final String message; - - /** - * Default constructor - * - * @param message The message - */ - public ShareResourceResponse(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 ShareResourceResponse(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-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java deleted file mode 100644 index 347fb49e68..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/share/ShareResourceRestAction.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.actions.share; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import org.opensearch.accesscontrol.resources.ShareWith; -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; - -import static java.util.Collections.singletonList; -import static org.opensearch.rest.RestRequest.Method.GET; - -public class ShareResourceRestAction extends BaseRestHandler { - - public ShareResourceRestAction() {} - - @Override - public List routes() { - return singletonList(new Route(GET, "/_plugins/sample_resource_sharing/share/{resource_id}")); - } - - @Override - public String getName() { - return "share_sample_resources"; - } - - @Override - public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { - Map source; - try (XContentParser parser = request.contentParser()) { - source = parser.map(); - } - - String resourceId = (String) source.get("resource_id"); - ShareWith shareWith = (ShareWith) source.get("share_with"); - final ShareResourceRequest shareResourceRequest = new ShareResourceRequest(resourceId, shareWith); - return channel -> client.executeLocally(ShareResourceAction.INSTANCE, shareResourceRequest, new RestToXContentListener<>(channel)); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java deleted file mode 100644 index 1378d561f5..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessAction.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.actions.verify; - -import org.opensearch.action.ActionType; - -/** - * Action to verify resource access for current user - */ -public class VerifyResourceAccessAction extends ActionType { - - public static final VerifyResourceAccessAction INSTANCE = new VerifyResourceAccessAction(); - - public static final String NAME = "cluster:admin/sample-resource-plugin/verify/resource_access"; - - private VerifyResourceAccessAction() { - super(NAME, VerifyResourceAccessResponse::new); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java deleted file mode 100644 index e9b20118db..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRequest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.actions.verify; - -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; - -public class VerifyResourceAccessRequest extends ActionRequest { - - private final String resourceId; - - private final String sourceIdx; - - private final String scope; - - /** - * Default constructor - */ - public VerifyResourceAccessRequest(String resourceId, String sourceIdx, String scope) { - this.resourceId = resourceId; - this.sourceIdx = sourceIdx; - this.scope = scope; - } - - /** - * Constructor with stream input - * @param in the stream input - * @throws IOException IOException - */ - public VerifyResourceAccessRequest(final StreamInput in) throws IOException { - this.resourceId = in.readString(); - this.sourceIdx = in.readString(); - this.scope = in.readString(); - } - - @Override - public void writeTo(final StreamOutput out) throws IOException { - out.writeString(resourceId); - out.writeString(sourceIdx); - out.writeString(scope); - } - - @Override - public ActionRequestValidationException validate() { - return null; - } - - public String getResourceId() { - return resourceId; - } - - public String getSourceIdx() { - return sourceIdx; - } - - public String getScope() { - return scope; - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessResponse.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessResponse.java deleted file mode 100644 index 660ac03f71..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessResponse.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.actions.verify; - -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; - -public class VerifyResourceAccessResponse extends ActionResponse implements ToXContentObject { - private final String message; - - /** - * Default constructor - * - * @param message The message - */ - public VerifyResourceAccessResponse(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 VerifyResourceAccessResponse(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-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java deleted file mode 100644 index 34bfed4e9f..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/actions/verify/VerifyResourceAccessRestAction.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.actions.verify; - -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; - -import static java.util.Collections.singletonList; -import static org.opensearch.rest.RestRequest.Method.POST; - -public class VerifyResourceAccessRestAction extends BaseRestHandler { - - public VerifyResourceAccessRestAction() {} - - @Override - public List routes() { - return singletonList(new Route(POST, "/_plugins/sample_resource_sharing/verify_resource_access")); - } - - @Override - public String getName() { - return "verify_resource_access"; - } - - @Override - public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { - Map source; - try (XContentParser parser = request.contentParser()) { - source = parser.map(); - } - - String resourceIdx = (String) source.get("resource_idx"); - String sourceIdx = (String) source.get("source_idx"); - String scope = (String) source.get("scope"); - - // final CreateResourceRequest createSampleResourceRequest = new CreateResourceRequest<>(resource); - return channel -> client.executeLocally(VerifyResourceAccessAction.INSTANCE, null, new RestToXContentListener<>(channel)); - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java deleted file mode 100644 index 8bff7b44a3..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/CreateResourceTransportAction.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.transport; - -import java.io.IOException; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.accesscontrol.resources.ResourceService; -import org.opensearch.accesscontrol.resources.ResourceSharing; -import org.opensearch.accesscontrol.resources.ShareWith; -import org.opensearch.accesscontrol.resources.SharedWithScope; -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.sample.Resource; -import org.opensearch.sample.SampleResourcePlugin; -import org.opensearch.sample.SampleResourceScope; -import org.opensearch.sample.actions.create.CreateResourceAction; -import org.opensearch.sample.actions.create.CreateResourceRequest; -import org.opensearch.sample.actions.create.CreateResourceResponse; -import org.opensearch.tasks.Task; -import org.opensearch.transport.TransportService; - -import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; - -/** - * Transport action for CreateSampleResource. - */ -public class CreateResourceTransportAction extends HandledTransportAction { - private static final Logger log = LogManager.getLogger(CreateResourceTransportAction.class); - - private final TransportService transportService; - private final Client nodeClient; - - @Inject - public CreateResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { - super(CreateResourceAction.NAME, transportService, actionFilters, CreateResourceRequest::new); - this.transportService = transportService; - this.nodeClient = nodeClient; - } - - @Override - protected void doExecute(Task task, CreateResourceRequest request, ActionListener listener) { - try (ThreadContext.StoredContext ignore = transportService.getThreadPool().getThreadContext().stashContext()) { - createResource(request, listener); - listener.onResponse(new CreateResourceResponse("Resource " + request.getResource() + " created successfully.")); - } catch (Exception e) { - log.info("Failed to create resource", e); - listener.onFailure(e); - } - } - - private void createResource(CreateResourceRequest request, ActionListener listener) { - 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 = getIndexResponseActionListener(listener); - nodeClient.index(ir, irListener); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static ActionListener getIndexResponseActionListener(ActionListener listener) { - SharedWithScope.SharedWithPerScope sharedWithPerScope = new SharedWithScope.SharedWithPerScope(List.of(), List.of(), List.of()); - SharedWithScope sharedWithScope = new SharedWithScope(SampleResourceScope.SAMPLE_FULL_ACCESS.getName(), sharedWithPerScope); - ShareWith shareWith = new ShareWith(List.of(sharedWithScope)); - return ActionListener.wrap(idxResponse -> { - log.info("Created resource: {}", idxResponse.toString()); - ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); - ResourceSharing sharing = rs.getResourceAccessControlPlugin().shareWith(idxResponse.getId(), idxResponse.getIndex(), shareWith); - log.info("Created resource sharing entry: {}", sharing.toString()); - }, listener::onFailure); - } - -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java deleted file mode 100644 index d56eb6d291..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ListAccessibleResourcesTransportAction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.transport; - -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.accesscontrol.resources.ResourceService; -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.sample.SampleResourcePlugin; -import org.opensearch.sample.actions.list.ListAccessibleResourcesAction; -import org.opensearch.sample.actions.list.ListAccessibleResourcesRequest; -import org.opensearch.sample.actions.list.ListAccessibleResourcesResponse; -import org.opensearch.tasks.Task; -import org.opensearch.transport.TransportService; - -import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; - -/** - * Transport action for ListSampleResource. - */ -public class ListAccessibleResourcesTransportAction extends HandledTransportAction< - ListAccessibleResourcesRequest, - ListAccessibleResourcesResponse> { - private static final Logger log = LogManager.getLogger(ListAccessibleResourcesTransportAction.class); - - @Inject - public ListAccessibleResourcesTransportAction(TransportService transportService, ActionFilters actionFilters) { - super(ListAccessibleResourcesAction.NAME, transportService, actionFilters, ListAccessibleResourcesRequest::new); - } - - @Override - protected void doExecute(Task task, ListAccessibleResourcesRequest request, ActionListener listener) { - try { - ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); - List resourceIds = rs.getResourceAccessControlPlugin().listAccessibleResourcesInPlugin(RESOURCE_INDEX_NAME); - log.info("Successfully fetched accessible resources for current user"); - listener.onResponse(new ListAccessibleResourcesResponse(resourceIds)); - } catch (Exception e) { - log.info("Failed to list accessible resources for current user: ", e); - listener.onFailure(e); - } - - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java deleted file mode 100644 index ccbfc31b78..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/ShareResourceTransportAction.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.transport; - -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.accesscontrol.resources.ResourceService; -import org.opensearch.accesscontrol.resources.ResourceSharing; -import org.opensearch.accesscontrol.resources.ShareWith; -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.sample.SampleResourcePlugin; -import org.opensearch.sample.actions.share.ShareResourceAction; -import org.opensearch.sample.actions.share.ShareResourceRequest; -import org.opensearch.sample.actions.share.ShareResourceResponse; -import org.opensearch.tasks.Task; -import org.opensearch.transport.TransportService; - -import static org.opensearch.sample.SampleResourcePlugin.RESOURCE_INDEX_NAME; - -/** - * Transport action for CreateSampleResource. - */ -public class ShareResourceTransportAction extends HandledTransportAction { - private static final Logger log = LogManager.getLogger(ShareResourceTransportAction.class); - - @Inject - public ShareResourceTransportAction(TransportService transportService, ActionFilters actionFilters) { - super(ShareResourceAction.NAME, transportService, actionFilters, ShareResourceRequest::new); - } - - @Override - protected void doExecute(Task task, ShareResourceRequest request, ActionListener listener) { - try { - shareResource(request); - listener.onResponse(new ShareResourceResponse("Resource " + request.getResourceId() + " shared successfully.")); - } catch (Exception e) { - listener.onFailure(e); - } - } - - private void shareResource(ShareResourceRequest request) { - try { - ShareWith shareWith = new ShareWith(List.of()); - ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); - ResourceSharing sharing = rs.getResourceAccessControlPlugin() - .shareWith(request.getResourceId(), RESOURCE_INDEX_NAME, shareWith); - log.info("Shared resource : {} with {}", request.getResourceId(), sharing.toString()); - } catch (Exception e) { - log.info("Failed to share resource {}", request.getResourceId(), e); - throw e; - } - } -} diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java deleted file mode 100644 index 947dcec59e..0000000000 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/transport/VerifyResourceAccessTransportAction.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.transport; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.accesscontrol.resources.ResourceService; -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.sample.SampleResourcePlugin; -import org.opensearch.sample.actions.verify.VerifyResourceAccessAction; -import org.opensearch.sample.actions.verify.VerifyResourceAccessRequest; -import org.opensearch.sample.actions.verify.VerifyResourceAccessResponse; -import org.opensearch.tasks.Task; -import org.opensearch.transport.TransportService; - -public class VerifyResourceAccessTransportAction extends HandledTransportAction { - private static final Logger log = LogManager.getLogger(VerifyResourceAccessTransportAction.class); - - @Inject - public VerifyResourceAccessTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { - super(VerifyResourceAccessAction.NAME, transportService, actionFilters, VerifyResourceAccessRequest::new); - } - - @Override - protected void doExecute(Task task, VerifyResourceAccessRequest request, ActionListener listener) { - try { - ResourceService rs = SampleResourcePlugin.GuiceHolder.getResourceService(); - boolean hasRequestedScopeAccess = rs.getResourceAccessControlPlugin() - .hasPermission(request.getResourceId(), request.getSourceIdx(), request.getScope()); - - StringBuilder sb = new StringBuilder(); - sb.append("User does"); - sb.append(hasRequestedScopeAccess ? " " : " not "); - sb.append("have requested scope "); - sb.append(request.getScope()); - sb.append(" access to "); - sb.append(request.getResourceId()); - - log.info(sb.toString()); - listener.onResponse(new VerifyResourceAccessResponse(sb.toString())); - } catch (Exception e) { - log.info("Failed to check user permissions for resource {}", request.getResourceId(), e); - listener.onFailure(e); - } - } - -} diff --git a/sample-resource-plugin/src/main/plugin-metadata/plugin-security.policy b/sample-resource-plugin/src/main/plugin-metadata/plugin-security.policy deleted file mode 100644 index a5dfc33a87..0000000000 --- a/sample-resource-plugin/src/main/plugin-metadata/plugin-security.policy +++ /dev/null @@ -1,3 +0,0 @@ -grant { - permission java.lang.RuntimePermission "getClassLoader"; -}; \ No newline at end of file diff --git a/sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin b/sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin deleted file mode 100644 index 1ca89eaf74..0000000000 --- a/sample-resource-plugin/src/main/resources/META-INF/services/org.opensearch.plugins.ResourcePlugin +++ /dev/null @@ -1 +0,0 @@ -org.opensearch.sample.SampleResourcePlugin \ No newline at end of file diff --git a/sample-resource-plugin/src/test/resources/security/esnode-key.pem b/sample-resource-plugin/src/test/resources/security/esnode-key.pem deleted file mode 100644 index e90562be43..0000000000 --- a/sample-resource-plugin/src/test/resources/security/esnode-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------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-resource-plugin/src/test/resources/security/esnode.pem b/sample-resource-plugin/src/test/resources/security/esnode.pem deleted file mode 100644 index 44101f0b37..0000000000 --- a/sample-resource-plugin/src/test/resources/security/esnode.pem +++ /dev/null @@ -1,25 +0,0 @@ ------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-resource-plugin/src/test/resources/security/kirk-key.pem b/sample-resource-plugin/src/test/resources/security/kirk-key.pem deleted file mode 100644 index 1949c26139..0000000000 --- a/sample-resource-plugin/src/test/resources/security/kirk-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------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-resource-plugin/src/test/resources/security/kirk.pem b/sample-resource-plugin/src/test/resources/security/kirk.pem deleted file mode 100644 index 36b7e19a75..0000000000 --- a/sample-resource-plugin/src/test/resources/security/kirk.pem +++ /dev/null @@ -1,27 +0,0 @@ ------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-resource-plugin/src/test/resources/security/root-ca.pem b/sample-resource-plugin/src/test/resources/security/root-ca.pem deleted file mode 100644 index d33f5f7216..0000000000 --- a/sample-resource-plugin/src/test/resources/security/root-ca.pem +++ /dev/null @@ -1,28 +0,0 @@ ------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-resource-plugin/src/test/resources/security/sample.pem b/sample-resource-plugin/src/test/resources/security/sample.pem deleted file mode 100644 index 44101f0b37..0000000000 --- a/sample-resource-plugin/src/test/resources/security/sample.pem +++ /dev/null @@ -1,25 +0,0 @@ ------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-resource-plugin/src/test/resources/security/test-kirk.jks b/sample-resource-plugin/src/test/resources/security/test-kirk.jks deleted file mode 100644 index 6c8c5ef77e20980f8c78295b159256b805da6a28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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 Date: Mon, 11 Nov 2024 13:20:06 -0500 Subject: [PATCH 21/42] Updates settings.gradle Signed-off-by: Darshit Chanpura --- settings.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/settings.gradle b/settings.gradle index 0bb3c5639d..1c3e7ff5aa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,3 @@ */ rootProject.name = 'opensearch-security' - -include "sample-resource-plugin" -project(":sample-resource-plugin").name = "opensearch-sample-resource-plugin" From 45b002ecfdc21733ef482e76f9df50ab797f31e8 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 20 Nov 2024 17:41:36 -0500 Subject: [PATCH 22/42] Fixes imports Signed-off-by: Darshit Chanpura --- .../java/org/opensearch/security/OpenSearchSecurityPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 96aa7c2bf6..1931486eb8 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -43,6 +43,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -279,7 +280,6 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin private volatile OpensearchDynamicSetting transportPassiveAuthSetting; private volatile PasswordHasher passwordHasher; private volatile DlsFlsBaseContext dlsFlsBaseContext; - private ResourceAccessEvaluator resourceAccessEvaluator; private ResourceManagementRepository rmr; private ResourceAccessHandler resourceAccessHandler; private final Set indicesToListen = new HashSet<>(); From a30be5779b0c285a7b1cc6e654b77893c2c19896 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 27 Nov 2024 14:18:16 -0500 Subject: [PATCH 23/42] Adds concrete implementations of remainder methods Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 8 +- .../security/auth/BackendRegistry.java | 4 + .../security/filter/SecurityFilter.java | 1 + .../resources/ResourceAccessHandler.java | 36 +- .../ResourceManagementRepository.java | 37 +- .../ResourceSharingIndexHandler.java | 907 +++++++++++++++++- .../ResourceSharingIndexListener.java | 40 +- 7 files changed, 930 insertions(+), 103 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 1931486eb8..ccee464e01 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -726,7 +726,9 @@ public void onIndexModule(IndexModule indexModule) { log.info("Indices to listen to: {}", this.indicesToListen); if (this.indicesToListen.contains(indexModule.getIndex().getName())) { - indexModule.addIndexOperationListener(ResourceSharingIndexListener.getInstance()); + ResourceSharingIndexListener resourceSharingIndexListener = ResourceSharingIndexListener.getInstance(); + resourceSharingIndexListener.initialize(threadPool, localClient); + indexModule.addIndexOperationListener(resourceSharingIndexListener); log.warn("Security plugin started listening to operations on index {}", indexModule.getIndex().getName()); } @@ -1205,7 +1207,7 @@ public Collection createComponents( // NOTE: We need to create DefaultInterClusterRequestEvaluator before creating ConfigurationRepository since the latter requires // security index to be accessible which means - // communciation with other nodes is already up. However for the communication to be up, there needs to be trusted nodes_dn. Hence + // communication with other nodes is already up. However for the communication to be up, there needs to be trusted nodes_dn. Hence // the base values from opensearch.yml // is used to first establish trust between same cluster nodes and there after dynamic config is loaded if enabled. if (DEFAULT_INTERCLUSTER_REQUEST_EVALUATOR_CLASS.equals(className)) { @@ -1217,7 +1219,7 @@ public Collection createComponents( ResourceSharingIndexHandler rsIndexHandler = new ResourceSharingIndexHandler(resourceSharingIndex, localClient, threadPool); resourceAccessHandler = new ResourceAccessHandler(threadPool, rsIndexHandler, adminDns); - rmr = ResourceManagementRepository.create(settings, threadPool, localClient, rsIndexHandler); + rmr = ResourceManagementRepository.create(rsIndexHandler); components.add(adminDns); components.add(cr); diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index 0b00bcf943..eb9bb504fd 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -224,6 +224,7 @@ public boolean authenticate(final SecurityRequestChannel request) { if (adminDns.isAdminDN(sslPrincipal)) { // PKI authenticated REST call threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, new User(sslPrincipal)); + threadContext.putPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER, new User(sslPrincipal)); auditLog.logSucceededLogin(sslPrincipal, true, null, request); return true; } @@ -389,6 +390,8 @@ public boolean authenticate(final SecurityRequestChannel request) { final User impersonatedUser = impersonate(request, authenticatedUser); threadPool.getThreadContext() .putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, impersonatedUser == null ? authenticatedUser : impersonatedUser); + threadPool.getThreadContext() + .putPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER, impersonatedUser == null ? authenticatedUser : impersonatedUser); auditLog.logSucceededLogin( (impersonatedUser == null ? authenticatedUser : impersonatedUser).getName(), false, @@ -422,6 +425,7 @@ public boolean authenticate(final SecurityRequestChannel request) { anonymousUser.setRequestedTenant(tenant); threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, anonymousUser); + threadPool.getThreadContext().putPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER, anonymousUser); auditLog.logSucceededLogin(anonymousUser.getName(), false, null, request); if (isDebugEnabled) { log.debug("Anonymous User is authenticated"); diff --git a/src/main/java/org/opensearch/security/filter/SecurityFilter.java b/src/main/java/org/opensearch/security/filter/SecurityFilter.java index 3323c9e38a..b2ede030a7 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityFilter.java @@ -345,6 +345,7 @@ private void ap log.info("Transport auth in passive mode and no user found. Injecting default user"); user = User.DEFAULT_TRANSPORT_USER; threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, user); + threadContext.putPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER, user); } else { log.error( "No user found for " diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 32fa077e71..d5e79a1fdf 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -49,41 +49,41 @@ public ResourceAccessHandler( this.adminDNs = adminDns; } - public List listAccessibleResourcesInPlugin(String systemIndex) { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + public List listAccessibleResourcesInPlugin(String pluginIndex) { + final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); if (user == null) { LOGGER.info("Unable to fetch user details "); return Collections.emptyList(); } - LOGGER.info("Listing accessible resource within a system index {} for : {}", systemIndex, user.getName()); + LOGGER.info("Listing accessible resource within a system index {} for : {}", pluginIndex, user.getName()); - // TODO check if user is admin, if yes all resources should be accessible + // check if user is admin, if yes all resources should be accessible if (adminDNs.isAdmin(user)) { - return loadAllResources(systemIndex); + return loadAllResources(pluginIndex); } Set result = new HashSet<>(); // 0. Own resources - result.addAll(loadOwnResources(systemIndex, user.getName())); + result.addAll(loadOwnResources(pluginIndex, user.getName())); // 1. By username - result.addAll(loadSharedWithResources(systemIndex, Set.of(user.getName()), "users")); + result.addAll(loadSharedWithResources(pluginIndex, Set.of(user.getName()), EntityType.USERS.toString())); // 2. By roles Set roles = user.getSecurityRoles(); - result.addAll(loadSharedWithResources(systemIndex, roles, "roles")); + result.addAll(loadSharedWithResources(pluginIndex, roles, EntityType.ROLES.toString())); // 3. By backend_roles Set backendRoles = user.getRoles(); - result.addAll(loadSharedWithResources(systemIndex, backendRoles, "backend_roles")); + result.addAll(loadSharedWithResources(pluginIndex, backendRoles, EntityType.BACKEND_ROLES.toString())); return result.stream().toList(); } public boolean hasPermission(String resourceId, String systemIndexName, String scope) { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Checking if {} has {} permission to resource {}", user.getName(), scope, resourceId); Set userRoles = user.getSecurityRoles(); @@ -109,24 +109,22 @@ public boolean hasPermission(String resourceId, String systemIndexName, String s } public ResourceSharing shareWith(String resourceId, String systemIndexName, ShareWith shareWith) { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user, shareWith.toString()); - // TODO fix this to fetch user-name correctly, need to hydrate user context since context might have been stashed. - // (persistentHeader?) - CreatedBy createdBy = new CreatedBy("", ""); + CreatedBy createdBy = new CreatedBy(user.getName()); return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, createdBy, shareWith); } public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Revoking access to resource {} created by {} for {}", resourceId, user.getName(), revokeAccess); return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess); } public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Deleting resource sharing record for resource {} in {} created by {}", resourceId, systemIndexName, user.getName()); ResourceSharing document = this.resourceSharingIndexHandler.fetchDocumentById(systemIndexName, resourceId); @@ -142,7 +140,7 @@ public boolean deleteResourceSharingRecord(String resourceId, String systemIndex } public boolean deleteAllResourceSharingRecordsForCurrentUser() { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Deleting all resource sharing records for resource {}", user.getName()); return this.resourceSharingIndexHandler.deleteAllRecordsForUser(user.getName()); @@ -159,8 +157,8 @@ private List loadOwnResources(String systemIndex, String username) { return this.resourceSharingIndexHandler.fetchDocumentsByField(systemIndex, "created_by.user", username); } - private List loadSharedWithResources(String systemIndex, Set accessWays, String shareWithType) { - return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(systemIndex, accessWays, shareWithType); + private List loadSharedWithResources(String systemIndex, Set entities, String shareWithType) { + return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(systemIndex, entities, shareWithType); } private boolean isOwnerOfResource(ResourceSharing document, String userName) { diff --git a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java index da3678728d..84749153f5 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java +++ b/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java @@ -11,44 +11,25 @@ package org.opensearch.security.resources; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.client.Client; -import org.opensearch.common.settings.Settings; -import org.opensearch.security.configuration.ConfigurationRepository; -import org.opensearch.threadpool.ThreadPool; - public class ResourceManagementRepository { - private static final Logger LOGGER = LogManager.getLogger(ConfigurationRepository.class); - - private final Client client; - - private final ThreadPool threadPool; - private final ResourceSharingIndexHandler resourceSharingIndexHandler; - protected ResourceManagementRepository( - final ThreadPool threadPool, - final Client client, - final ResourceSharingIndexHandler resourceSharingIndexHandler - ) { - this.client = client; - this.threadPool = threadPool; + protected ResourceManagementRepository(final ResourceSharingIndexHandler resourceSharingIndexHandler) { this.resourceSharingIndexHandler = resourceSharingIndexHandler; } - public static ResourceManagementRepository create( - Settings settings, - final ThreadPool threadPool, - Client client, - ResourceSharingIndexHandler resourceSharingIndexHandler - ) { + public static ResourceManagementRepository create(ResourceSharingIndexHandler resourceSharingIndexHandler) { - return new ResourceManagementRepository(threadPool, client, resourceSharingIndexHandler); + return new ResourceManagementRepository(resourceSharingIndexHandler); } + /** + * Creates the resource sharing index if it doesn't already exist. + * This method is called during the initialization phase of the repository. + * It ensures that the index is set up with the necessary mappings and settings + * before any operations are performed on the index. + */ public void createResourceSharingIndexIfAbsent() { // TODO check if this should be wrapped in an atomic completable future diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index b175ad53d0..5568ee06d6 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -10,35 +10,44 @@ package org.opensearch.security.resources; import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.Callable; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.lucene.search.join.ScoreMode; -import org.opensearch.accesscontrol.resources.CreatedBy; -import org.opensearch.accesscontrol.resources.EntityType; -import org.opensearch.accesscontrol.resources.ResourceSharing; -import org.opensearch.accesscontrol.resources.ShareWith; +import org.opensearch.accesscontrol.resources.*; 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.search.*; import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; +import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.index.query.BoolQueryBuilder; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.index.reindex.*; +import org.opensearch.script.Script; +import org.opensearch.script.ScriptType; +import org.opensearch.search.Scroll; +import org.opensearch.search.SearchHit; +import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.threadpool.ThreadPool; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; public class ResourceSharingIndexHandler { - private final static int MINIMUM_HASH_BITS = 128; - private static final Logger LOGGER = LogManager.getLogger(ResourceSharingIndexHandler.class); private final Client client; @@ -55,6 +64,25 @@ public ResourceSharingIndexHandler(final String indexName, final Client client, public final static Map INDEX_SETTINGS = Map.of("index.number_of_shards", 1, "index.auto_expand_replicas", "0-all"); + /** + * Creates the resource sharing index if it doesn't already exist. + * This method initializes the index with predefined mappings and settings + * for storing resource sharing information. + * The index will be created with the following structure: + * - source_idx (keyword): The source index containing the original document + * - resource_id (keyword): The ID of the shared resource + * - created_by (object): Information about the user who created the sharing + * - user (keyword): Username of the creator + * - share_with (object): Access control configuration for shared resources + * - [group_name] (object): Name of the access group + * - users (array): List of users with access + * - roles (array): List of roles with access + * - backend_roles (array): List of backend roles with access + * + * @throws RuntimeException if there are issues reading/writing index settings + * or communicating with the cluster + */ + public void createResourceSharingIndexIfAbsent(Callable callable) { // TODO: Once stashContext is replaced with switchContext this call will have to be modified try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) { @@ -75,7 +103,29 @@ public void createResourceSharingIndexIfAbsent(Callable callable) { } } - public boolean indexResourceSharing(String resourceId, String resourceIndex, CreatedBy createdBy, ShareWith shareWith) + /** + * Creates or updates a resource sharing record in the dedicated resource sharing index. + * This method handles the persistence of sharing metadata for resources, including + * the creator information and sharing permissions. + * + * @param resourceId The unique identifier of the resource being shared + * @param resourceIndex The source index where the original resource is stored + * @param createdBy Object containing information about the user creating/updating the sharing + * @param shareWith Object containing the sharing permissions' configuration. Can be null for initial creation. + * When provided, it should contain the access control settings for different groups: + * { + * "group_name": { + * "users": ["user1", "user2"], + * "roles": ["role1", "role2"], + * "backend_roles": ["backend_role1"] + * } + * } + * + * @return ResourceSharing Returns resourceSharing object if the operation was successful, null otherwise + * @throws IOException if there are issues with index operations or JSON processing + */ + + public ResourceSharing indexResourceSharing(String resourceId, String resourceIndex, CreatedBy createdBy, ShareWith shareWith) throws IOException { try { @@ -88,58 +138,839 @@ public boolean indexResourceSharing(String resourceId, String resourceIndex, Cre LOGGER.info("Index Request: {}", ir.toString()); - ActionListener irListener = ActionListener.wrap( - idxResponse -> { LOGGER.info("Created {} entry.", resourceSharingIndex); }, - (failResponse) -> { - LOGGER.error(failResponse.getMessage()); - LOGGER.info("Failed to create {} entry.", resourceSharingIndex); - } - ); + ActionListener irListener = ActionListener.wrap(idxResponse -> { + LOGGER.info("Successfully created {} entry.", resourceSharingIndex); + }, (failResponse) -> { + LOGGER.error(failResponse.getMessage()); + LOGGER.info("Failed to create {} entry.", resourceSharingIndex); + }); client.index(ir, irListener); + return entry; } catch (Exception e) { LOGGER.info("Failed to create {} entry.", resourceSharingIndex, e); - return false; + return null; } - return true; } - public List fetchDocumentsByField(String systemIndex, String field, String value) { - LOGGER.info("Fetching documents from index: {}, where {} = {}", systemIndex, field, value); + /** + * Fetches all resource sharing records that match the specified system index. This method retrieves + * a list of resource IDs associated with the given system index from the resource sharing index. + * + *

The method executes the following steps: + *

    + *
  1. Creates a search request with term query matching the system index
  2. + *
  3. Applies source filtering to only fetch resource_id field
  4. + *
  5. Executes the search with a limit of 10000 documents
  6. + *
  7. Processes the results to extract resource IDs
  8. + *
+ * + *

Example query structure: + *

+        * {
+        *   "query": {
+        *     "term": {
+        *       "source_idx": "system_index_name"
+        *     }
+        *   },
+        *   "_source": ["resource_id"],
+        *   "size": 10000
+        * }
+        * 
+ * + * @param pluginIndex The source index to match against the source_idx field + * @return List containing resource IDs that belong to the specified system index. + * Returns an empty list if: + *
    + *
  • No matching documents are found
  • + *
  • An error occurs during the search operation
  • + *
  • The system index parameter is invalid
  • + *
+ * + * @apiNote This method: + *
    + *
  • Uses source filtering for optimal performance
  • + *
  • Performs exact matching on the source_idx field
  • + *
  • Returns an empty list instead of throwing exceptions
  • + *
+ */ + public List fetchAllDocuments(String pluginIndex) { + LOGGER.debug("Fetching all documents from {} where source_idx = {}", resourceSharingIndex, pluginIndex); + + try { + SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(QueryBuilders.termQuery("source_idx", pluginIndex)); + searchSourceBuilder.size(10000); // TODO check what size should be set here. + + searchSourceBuilder.fetchSource(new String[] { "resource_id" }, null); + + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = client.search(searchRequest).actionGet(); + + List resourceIds = new ArrayList<>(); - return List.of(); + SearchHit[] hits = searchResponse.getHits().getHits(); + for (SearchHit hit : hits) { + Map sourceAsMap = hit.getSourceAsMap(); + if (sourceAsMap != null && sourceAsMap.containsKey("resource_id")) { + resourceIds.add(sourceAsMap.get("resource_id").toString()); + } + } + + LOGGER.debug("Found {} documents in {} for source_idx: {}", resourceIds.size(), resourceSharingIndex, pluginIndex); + + return resourceIds; + + } catch (Exception e) { + LOGGER.error("Failed to fetch documents from {} for source_idx: {}", resourceSharingIndex, pluginIndex, e); + return List.of(); + } } - public List fetchAllDocuments(String systemIndex) { - LOGGER.info("Fetching all documents from index: {}", systemIndex); - return List.of(); + /** + * Fetches documents that match the specified system index and have specific access type values. + * This method uses scroll API to handle large result sets efficiently. + * + *

The method executes the following steps: + *

    + *
  1. Validates the entityType parameter
  2. + *
  3. Creates a scrolling search request with a compound query
  4. + *
  5. Processes results in batches using scroll API
  6. + *
  7. Collects all matching resource IDs
  8. + *
  9. Cleans up scroll context
  10. + *
+ * + *

Example query structure: + *

+    * {
+    *   "query": {
+    *     "bool": {
+    *       "must": [
+    *         { "term": { "source_idx": "system_index_name" } },
+    *         {
+    *           "bool": {
+    *             "should": [
+    *               {
+    *                 "nested": {
+    *                   "path": "share_with.*.entityType",
+    *                   "query": {
+    *                     "term": { "share_with.*.entityType": "entity_value" }
+    *                   }
+    *                 }
+    *               }
+    *             ],
+    *             "minimum_should_match": 1
+    *           }
+    *         }
+    *       ]
+    *     }
+    *   },
+    *   "_source": ["resource_id"],
+    *   "size": 1000
+    * }
+    * 
+ * + * @param pluginIndex The source index to match against the source_idx field + * @param entities Set of values to match in the specified entityType field + * @param entityType The type of association with the resource. Must be one of: + *
    + *
  • "users" - for user-based access
  • + *
  • "roles" - for role-based access
  • + *
  • "backend_roles" - for backend role-based access
  • + *
+ * @return List List of resource IDs that match the criteria. The list may be empty + * if no matches are found + * + * @throws RuntimeException if the search operation fails + * + * @apiNote This method: + *
    + *
  • Uses scroll API with 1-minute timeout
  • + *
  • Processes results in batches of 1000 documents
  • + *
  • Performs source filtering for optimization
  • + *
  • Uses nested queries for accessing array elements
  • + *
  • Properly cleans up scroll context after use
  • + *
+ */ + + public List fetchDocumentsForAllScopes(String pluginIndex, Set entities, String entityType) { + LOGGER.debug("Fetching documents from index: {}, where share_with.*.{} contains any of {}", pluginIndex, entityType, entities); + + List resourceIds = new ArrayList<>(); + final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); + + try { + SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); + searchRequest.scroll(scroll); + + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("source_idx", pluginIndex)); + + BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery(); + for (String entity : entities) { + shouldQuery.should( + QueryBuilders.nestedQuery( + "share_with.*." + entityType, + QueryBuilders.termQuery("share_with.*." + entityType, entity), + ScoreMode.None + ) + ); + } + shouldQuery.minimumShouldMatch(1); + boolQuery.must(shouldQuery); + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery) + .size(1000) + .fetchSource(new String[] { "resource_id" }, null); + + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = client.search(searchRequest).actionGet(); + String scrollId = searchResponse.getScrollId(); + SearchHit[] hits = searchResponse.getHits().getHits(); + + while (hits != null && hits.length > 0) { + for (SearchHit hit : hits) { + Map sourceAsMap = hit.getSourceAsMap(); + if (sourceAsMap != null && sourceAsMap.containsKey("resource_id")) { + resourceIds.add(sourceAsMap.get("resource_id").toString()); + } + } + + SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); + scrollRequest.scroll(scroll); + searchResponse = client.execute(SearchScrollAction.INSTANCE, scrollRequest).actionGet(); + scrollId = searchResponse.getScrollId(); + hits = searchResponse.getHits().getHits(); + } + + ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); + clearScrollRequest.addScrollId(scrollId); + client.clearScroll(clearScrollRequest).actionGet(); + + LOGGER.debug("Found {} documents matching the criteria in {}", resourceIds.size(), resourceSharingIndex); + + return resourceIds; + + } catch (Exception e) { + LOGGER.error( + "Failed to fetch documents from {} for criteria - systemIndex: {}, shareWithType: {}, accessWays: {}", + resourceSharingIndex, + pluginIndex, + entityType, + entities, + e + ); + throw new RuntimeException("Failed to fetch documents: " + e.getMessage(), e); + } } - public List fetchDocumentsForAllScopes(String systemIndex, Set accessWays, String shareWithType) { - return List.of(); + /** + * Fetches documents from the resource sharing index that match a specific field value. + * This method uses scroll API to efficiently handle large result sets and performs exact + * matching on both system index and the specified field. + * + *

The method executes the following steps: + *

    + *
  1. Validates input parameters for null/empty values
  2. + *
  3. Creates a scrolling search request with a bool query
  4. + *
  5. Processes results in batches using scroll API
  6. + *
  7. Extracts resource IDs from matching documents
  8. + *
  9. Cleans up scroll context after completion
  10. + *
+ * + *

Example query structure: + *

+     * {
+     *   "query": {
+     *     "bool": {
+     *       "must": [
+     *         { "term": { "source_idx": "system_index_value" } },
+     *         { "term": { "field_name": "field_value" } }
+     *       ]
+     *     }
+     *   },
+     *   "_source": ["resource_id"],
+     *   "size": 1000
+     * }
+     * 
+ * + * @param systemIndex The source index to match against the source_idx field + * @param field The field name to search in. Must be a valid field in the index mapping + * @param value The value to match for the specified field. Performs exact term matching + * @return List List of resource IDs that match the criteria. Returns an empty list + * if no matches are found + * + * @throws IllegalArgumentException if any parameter is null or empty + * @throws RuntimeException if the search operation fails, wrapping the underlying exception + * + * @apiNote This method: + *
    + *
  • Uses scroll API with 1-minute timeout for handling large result sets
  • + *
  • Performs exact term matching (not analyzed) on field values
  • + *
  • Processes results in batches of 1000 documents
  • + *
  • Uses source filtering to only fetch resource_id field
  • + *
  • Automatically cleans up scroll context after use
  • + *
+ * + * Example usage: + *
+     * List resources = fetchDocumentsByField("myIndex", "status", "active");
+     * 
+ */ + + public List fetchDocumentsByField(String systemIndex, String field, String value) { + if (StringUtils.isBlank(systemIndex) || StringUtils.isBlank(field) || StringUtils.isBlank(value)) { + throw new IllegalArgumentException("systemIndex, field, and value must not be null or empty"); + } + + LOGGER.debug("Fetching documents from index: {}, where {} = {}", systemIndex, field, value); + + List resourceIds = new ArrayList<>(); + final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); + + try { + // Create initial search request + SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); + searchRequest.scroll(scroll); + + // Build the query + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() + .must(QueryBuilders.termQuery("source_idx", systemIndex)) + .must(QueryBuilders.termQuery(field, value)); + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery) + .size(1000) + .fetchSource(new String[] { "resource_id" }, null); + + searchRequest.source(searchSourceBuilder); + + // Execute initial search + SearchResponse searchResponse = client.search(searchRequest).actionGet(); + String scrollId = searchResponse.getScrollId(); + SearchHit[] hits = searchResponse.getHits().getHits(); + + // Process results in batches + while (hits != null && hits.length > 0) { + for (SearchHit hit : hits) { + Map sourceAsMap = hit.getSourceAsMap(); + if (sourceAsMap != null && sourceAsMap.containsKey("resource_id")) { + resourceIds.add(sourceAsMap.get("resource_id").toString()); + } + } + + SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); + scrollRequest.scroll(scroll); + searchResponse = client.execute(SearchScrollAction.INSTANCE, scrollRequest).actionGet(); + scrollId = searchResponse.getScrollId(); + hits = searchResponse.getHits().getHits(); + } + + // Clear scroll + ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); + clearScrollRequest.addScrollId(scrollId); + client.clearScroll(clearScrollRequest).actionGet(); + + LOGGER.debug("Found {} documents in {} where {} = {}", resourceIds.size(), resourceSharingIndex, field, value); + + return resourceIds; + + } catch (Exception e) { + LOGGER.error("Failed to fetch documents from {} where {} = {}", resourceSharingIndex, field, value, e); + throw new RuntimeException("Failed to fetch documents: " + e.getMessage(), e); + } } - public ResourceSharing fetchDocumentById(String systemIndexName, String resourceId) { - return null; + /** + * Fetches a specific resource sharing document by its resource ID and system index. + * This method performs an exact match search and parses the result into a ResourceSharing object. + * + *

The method executes the following steps: + *

    + *
  1. Validates input parameters for null/empty values
  2. + *
  3. Creates a search request with a bool query for exact matching
  4. + *
  5. Executes the search with a limit of 1 document
  6. + *
  7. Parses the result using XContent parser if found
  8. + *
  9. Returns null if no matching document exists
  10. + *
+ * + *

Example query structure: + *

+    * {
+    *   "query": {
+    *     "bool": {
+    *       "must": [
+    *         { "term": { "source_idx": "system_index_name" } },
+    *         { "term": { "resource_id": "resource_id_value" } }
+    *       ]
+    *     }
+    *   },
+    *   "size": 1
+    * }
+    * 
+ * + * @param pluginIndex The source index to match against the source_idx field + * @param resourceId The resource ID to fetch. Must exactly match the resource_id field + * @return ResourceSharing object if a matching document is found, null if no document + * matches the criteria + * + * @throws IllegalArgumentException if systemIndexName or resourceId is null or empty + * @throws RuntimeException if the search operation fails or parsing errors occur, + * wrapping the underlying exception + * + * @apiNote This method: + *
    + *
  • Uses term queries for exact matching
  • + *
  • Expects only one matching document per resource ID
  • + *
  • Uses XContent parsing for consistent object creation
  • + *
  • Returns null instead of throwing exceptions for non-existent documents
  • + *
  • Provides detailed logging for troubleshooting
  • + *
+ * + * Example usage: + *
+    * ResourceSharing sharing = fetchDocumentById("myIndex", "resource123");
+    * if (sharing != null) {
+    *     // Process the resource sharing object
+    * }
+    * 
+ */ + + public ResourceSharing fetchDocumentById(String pluginIndex, String resourceId) { + // Input validation + if (StringUtils.isBlank(pluginIndex) || StringUtils.isBlank(resourceId)) { + throw new IllegalArgumentException("systemIndexName and resourceId must not be null or empty"); + } + + LOGGER.debug("Fetching document from index: {}, with resourceId: {}", pluginIndex, resourceId); + + try { + SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); + + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() + .must(QueryBuilders.termQuery("source_idx", pluginIndex)) + .must(QueryBuilders.termQuery("resource_id", resourceId)); + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery).size(1); // We only need one document since + // a resource must have only one + // sharing entry + + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = client.search(searchRequest).actionGet(); + + SearchHit[] hits = searchResponse.getHits().getHits(); + if (hits.length == 0) { + LOGGER.debug("No document found for resourceId: {} in index: {}", resourceId, pluginIndex); + return null; + } + + SearchHit hit = hits[0]; + try ( + XContentParser parser = XContentType.JSON.xContent() + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, hit.getSourceAsString()) + ) { + + parser.nextToken(); + + ResourceSharing resourceSharing = ResourceSharing.fromXContent(parser); + + LOGGER.debug("Successfully fetched document for resourceId: {} from index: {}", resourceId, pluginIndex); + + return resourceSharing; + } + + } catch (Exception e) { + LOGGER.error("Failed to fetch document for resourceId: {} from index: {}", resourceId, pluginIndex, e); + throw new RuntimeException("Failed to fetch document: " + e.getMessage(), e); + } } - public ResourceSharing updateResourceSharingInfo(String resourceId, String systemIndexName, CreatedBy createdBy, ShareWith shareWith) { + /** + * Updates resource sharing entries that match the specified source index and resource ID + * using the provided update script. This method performs an update-by-query operation + * in the resource sharing index. + * + *

The method executes the following steps: + *

    + *
  1. Creates a bool query to match exact source index and resource ID
  2. + *
  3. Constructs an update-by-query request with the query and update script
  4. + *
  5. Executes the update operation
  6. + *
  7. Returns success/failure status based on update results
  8. + *
+ * + *

Example document matching structure: + *

+     * {
+     *   "source_idx": "source_index_name",
+     *   "resource_id": "resource_id_value",
+     *   "share_with": {
+     *     // sharing configuration to be updated
+     *   }
+     * }
+     * 
+ * + * @param sourceIdx The source index to match in the query (exact match) + * @param resourceId The resource ID to match in the query (exact match) + * @param updateScript The script containing the update operations to be performed. + * This script defines how the matching documents should be modified + * @return boolean true if at least one document was updated, false if no documents + * were found or update failed + * + * @apiNote This method: + *
    + *
  • Uses term queries for exact matching of source_idx and resource_id
  • + *
  • Returns false for both "no matching documents" and "operation failure" cases
  • + *
  • Logs the complete update request for debugging purposes
  • + *
  • Provides detailed logging for success and failure scenarios
  • + *
+ * + * @implNote The update operation uses a bool query with two must clauses: + *
+     * {
+     *   "query": {
+     *     "bool": {
+     *       "must": [
+     *         { "term": { "source_idx": sourceIdx } },
+     *         { "term": { "resource_id": resourceId } }
+     *       ]
+     *     }
+     *   }
+     * }
+     * 
+ */ + private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId, Script updateScript) { try { - boolean success = indexResourceSharing(resourceId, systemIndexName, createdBy, shareWith); - return success ? new ResourceSharing(resourceId, systemIndexName, createdBy, shareWith) : null; - } catch (IOException e) { - throw new RuntimeException(e); + // Create a bool query to match both fields + BoolQueryBuilder query = QueryBuilders.boolQuery() + .must(QueryBuilders.termQuery("source_idx", sourceIdx)) + .must(QueryBuilders.termQuery("resource_id", resourceId)); + + UpdateByQueryRequest ubq = new UpdateByQueryRequest(resourceSharingIndex).setQuery(query).setScript(updateScript); + + LOGGER.info("Update By Query Request: {}", ubq.toString()); + + BulkByScrollResponse response = client.execute(UpdateByQueryAction.INSTANCE, ubq).actionGet(); + + if (response.getUpdated() > 0) { + LOGGER.info("Successfully updated {} documents in {}.", response.getUpdated(), resourceSharingIndex); + return true; + } else { + LOGGER.info( + "No documents found to update in {} for source_idx: {} and resource_id: {}", + resourceSharingIndex, + sourceIdx, + resourceId + ); + return false; + } + + } catch (Exception e) { + LOGGER.error("Failed to update documents in {}.", resourceSharingIndex, e); + return false; } } + /** + * Updates the sharing configuration for an existing resource in the resource sharing index. + * This method modifies the sharing permissions for a specific resource identified by its + * resource ID and source index. + * + * @param resourceId The unique identifier of the resource whose sharing configuration needs to be updated + * @param sourceIdx The source index where the original resource is stored + * @param shareWith Updated sharing configuration object containing access control settings: + * { + * "scope": { + * "users": ["user1", "user2"], + * "roles": ["role1", "role2"], + * "backend_roles": ["backend_role1"] + * } + * } + * @return ResourceSharing Returns resourceSharing object if the update was successful, null otherwise + * @throws RuntimeException if there's an error during the update operation + */ + public ResourceSharing updateResourceSharingInfo(String resourceId, String sourceIdx, CreatedBy createdBy, ShareWith shareWith) { + Script updateScript = new Script( + ScriptType.INLINE, + "painless", + "ctx._source.shareWith = params.newShareWith", + Collections.singletonMap("newShareWith", shareWith) + ); + + boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, updateScript); + return success ? new ResourceSharing(resourceId, sourceIdx, createdBy, shareWith) : null; + } + + /** + * Revokes access for specified entities from a resource sharing document. This method removes the specified + * entities (users, roles, or backend roles) from the existing sharing configuration while preserving other + * sharing settings. + * + *

The method performs the following steps: + *

    + *
  1. Fetches the existing document
  2. + *
  3. Removes specified entities from their respective lists in all sharing groups
  4. + *
  5. Updates the document if modifications were made
  6. + *
  7. Returns the updated resource sharing configuration
  8. + *
+ * + *

Example document structure: + *

+     * {
+     *   "source_idx": "system_index_name",
+     *   "resource_id": "resource_id",
+     *   "share_with": {
+     *     "group_name": {
+     *       "users": ["user1", "user2"],
+     *       "roles": ["role1", "role2"],
+     *       "backend_roles": ["backend_role1"]
+     *     }
+     *   }
+     * }
+     * 
+ * + * @param resourceId The ID of the resource from which to revoke access + * @param systemIndexName The name of the system index where the resource exists + * @param revokeAccess A map containing entity types (USER, ROLE, BACKEND_ROLE) and their corresponding + * values to be removed from the sharing configuration + * @return The updated ResourceSharing object after revoking access, or null if the document doesn't exist + * @throws IllegalArgumentException if resourceId, systemIndexName is null/empty, or if revokeAccess is null/empty + * @throws RuntimeException if the update operation fails or encounters an error + * + * @see EntityType + * @see ResourceSharing + * + * @apiNote This method modifies the existing document. If no modifications are needed (i.e., specified + * entities don't exist in the current configuration), the original document is returned unchanged. + * @example + *
+     * Map> revokeAccess = new HashMap<>();
+     * revokeAccess.put(EntityType.USER, Arrays.asList("user1", "user2"));
+     * revokeAccess.put(EntityType.ROLE, Arrays.asList("role1"));
+     * ResourceSharing updated = revokeAccess("resourceId", "systemIndex", revokeAccess);
+     * 
+ */ + public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { - return null; + // TODO; check if this needs to be done per scope rather than for all scopes + + // Input validation + if (StringUtils.isBlank(resourceId) || StringUtils.isBlank(systemIndexName) || revokeAccess == null || revokeAccess.isEmpty()) { + throw new IllegalArgumentException("resourceId, systemIndexName, and revokeAccess must not be null or empty"); + } + + LOGGER.debug("Revoking access for resource {} in {} for entities: {}", resourceId, systemIndexName, revokeAccess); + + try { + // First fetch the existing document + ResourceSharing existingResource = fetchDocumentById(systemIndexName, resourceId); + if (existingResource == null) { + LOGGER.warn("No document found for resourceId: {} in index: {}", resourceId, systemIndexName); + return null; + } + + ShareWith shareWith = existingResource.getShareWith(); + boolean modified = false; + + if (shareWith != null) { + for (SharedWithScope sharedWithScope : shareWith.getSharedWithScopes()) { + SharedWithScope.SharedWithPerScope sharedWithPerScope = sharedWithScope.getSharedWithPerScope(); + + for (Map.Entry> entry : revokeAccess.entrySet()) { + EntityType entityType = entry.getKey(); + List entities = entry.getValue(); + + // Check if the entity type exists in the share_with configuration + switch (entityType) { + case USERS: + if (sharedWithPerScope.getUsers() != null) { + modified = sharedWithPerScope.getUsers().removeAll(entities) || modified; + } + break; + case ROLES: + if (sharedWithPerScope.getRoles() != null) { + modified = sharedWithPerScope.getRoles().removeAll(entities) || modified; + } + break; + case BACKEND_ROLES: + if (sharedWithPerScope.getBackendRoles() != null) { + modified = sharedWithPerScope.getBackendRoles().removeAll(entities) || modified; + } + break; + } + } + } + } + + if (!modified) { + LOGGER.debug("No modifications needed for resource: {}", resourceId); + return existingResource; + } + + // Update resource sharing info + return updateResourceSharingInfo(resourceId, systemIndexName, existingResource.getCreatedBy(), shareWith); + + } catch (Exception e) { + LOGGER.error("Failed to revoke access for resource: {} in index: {}", resourceId, systemIndexName, e); + throw new RuntimeException("Failed to revoke access: " + e.getMessage(), e); + } } - public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { - return false; + /** + * Deletes resource sharing records that match the specified source index and resource ID. + * This method performs a delete-by-query operation in the resource sharing index. + * + *

The method executes the following steps: + *

    + *
  1. Creates a delete-by-query request with a bool query
  2. + *
  3. Matches documents based on exact source index and resource ID
  4. + *
  5. Executes the delete operation with immediate refresh
  6. + *
  7. Returns the success/failure status based on deletion results
  8. + *
+ * + *

Example document structure that will be deleted: + *

+     * {
+     *   "source_idx": "source_index_name",
+     *   "resource_id": "resource_id_value",
+     *   "share_with": {
+     *     // sharing configuration
+     *   }
+     * }
+     * 
+ * + * @param sourceIdx The source index to match in the query (exact match) + * @param resourceId The resource ID to match in the query (exact match) + * @return boolean true if at least one document was deleted, false if no documents were found or deletion failed + * + * @implNote The delete operation uses a bool query with two must clauses to ensure exact matching: + *
+     * {
+     *   "query": {
+     *     "bool": {
+     *       "must": [
+     *         { "term": { "source_idx": sourceIdx } },
+     *         { "term": { "resource_id": resourceId } }
+     *       ]
+     *     }
+     *   }
+     * }
+     * 
+ */ + public boolean deleteResourceSharingRecord(String resourceId, String sourceIdx) { + LOGGER.info("Deleting documents from {} where source_idx = {} and resource_id = {}", resourceSharingIndex, sourceIdx, resourceId); + + try { + DeleteByQueryRequest dbq = new DeleteByQueryRequest(resourceSharingIndex).setQuery( + QueryBuilders.boolQuery() + .must(QueryBuilders.termQuery("source_idx", sourceIdx)) + .must(QueryBuilders.termQuery("resource_id", resourceId)) + ).setRefresh(true); + + BulkByScrollResponse response = client.execute(DeleteByQueryAction.INSTANCE, dbq).actionGet(); + + if (response.getDeleted() > 0) { + LOGGER.info("Successfully deleted {} documents from {}", response.getDeleted(), resourceSharingIndex); + return true; + } else { + LOGGER.info( + "No documents found to delete in {} for source_idx: {} and resource_id: {}", + resourceSharingIndex, + sourceIdx, + resourceId + ); + return false; + } + + } catch (Exception e) { + LOGGER.error("Failed to delete documents from {}", resourceSharingIndex, e); + return false; + } } + /** + * Deletes all resource sharing records that were created by a specific user. + * This method performs a delete-by-query operation to remove all documents where + * the created_by.user field matches the specified username. + * + *

The method executes the following steps: + *

    + *
  1. Validates the input username parameter
  2. + *
  3. Creates a delete-by-query request with term query matching
  4. + *
  5. Executes the delete operation with immediate refresh
  6. + *
  7. Returns the operation status based on number of deleted documents
  8. + *
+ * + *

Example query structure: + *

+        * {
+        *   "query": {
+        *     "term": {
+        *       "created_by.user": "username"
+        *     }
+        *   }
+        * }
+        * 
+ * + * @param name The username to match against the created_by.user field + * @return boolean indicating whether the deletion was successful: + *
    + *
  • true - if one or more documents were deleted
  • + *
  • false - if no documents were found
  • + *
  • false - if the operation failed due to an error
  • + *
+ * + * @throws IllegalArgumentException if name is null or empty + * + * + * @implNote Implementation details: + *
    + *
  • Uses DeleteByQueryRequest for efficient bulk deletion
  • + *
  • Sets refresh=true for immediate consistency
  • + *
  • Uses term query for exact username matching
  • + *
  • Implements comprehensive error handling and logging
  • + *
+ * + * Example usage: + *
+        * boolean success = deleteAllRecordsForUser("john.doe");
+        * if (success) {
+        *     // Records were successfully deleted
+        * } else {
+        *     // No matching records found or operation failed
+        * }
+        * 
+ */ public boolean deleteAllRecordsForUser(String name) { - return false; + // Input validation + if (StringUtils.isBlank(name)) { + throw new IllegalArgumentException("Username must not be null or empty"); + } + + LOGGER.info("Deleting all records for user {}", name); + + try { + DeleteByQueryRequest deleteRequest = new DeleteByQueryRequest(resourceSharingIndex).setQuery( + QueryBuilders.termQuery("created_by.user", name) + ).setRefresh(true); + + BulkByScrollResponse response = client.execute(DeleteByQueryAction.INSTANCE, deleteRequest).actionGet(); + + long deletedDocs = response.getDeleted(); + + if (deletedDocs > 0) { + LOGGER.info("Successfully deleted {} documents created by user {}", deletedDocs, name); + return true; + } else { + LOGGER.info("No documents found for user {}", name); + return false; + } + + } catch (Exception e) { + LOGGER.error("Failed to delete documents for user {}", name, e); + return false; + } } + } diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java index d6b1180d46..d7b149a2fb 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java @@ -14,11 +14,13 @@ import org.apache.logging.log4j.Logger; import org.opensearch.accesscontrol.resources.CreatedBy; +import org.opensearch.accesscontrol.resources.ResourceSharing; 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.security.support.ConfigConstants; +import org.opensearch.security.user.User; import org.opensearch.threadpool.ThreadPool; /** @@ -36,8 +38,6 @@ public class ResourceSharingIndexListener implements IndexingOperationListener { private ThreadPool threadPool; - private Client client; - private ResourceSharingIndexListener() {} public static ResourceSharingIndexListener getInstance() { @@ -53,16 +53,12 @@ public void initialize(ThreadPool threadPool, Client client) { } initialized = true; - this.threadPool = threadPool; - - this.client = client; this.resourceSharingIndexHandler = new ResourceSharingIndexHandler( ConfigConstants.OPENSEARCH_RESOURCE_SHARING_INDEX, client, threadPool ); - ; } @@ -73,27 +69,41 @@ public boolean isInitialized() { @Override public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult result) { - // implement a check to see if a resource was updated - log.info("postIndex called on {}", shardId.getIndexName()); + String resourceIndex = shardId.getIndexName(); + log.info("postIndex called on {}", resourceIndex); String resourceId = index.id(); - String resourceIndex = shardId.getIndexName(); + User user = threadPool.getThreadContext().getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); try { - this.resourceSharingIndexHandler.indexResourceSharing(resourceId, resourceIndex, new CreatedBy("bleh", ""), null); - log.info("successfully indexed resource {}", resourceId); + ResourceSharing sharing = this.resourceSharingIndexHandler.indexResourceSharing( + resourceId, + resourceIndex, + new CreatedBy(user.getName()), + null + ); + log.info("Successfully created a resource sharing entry {}", sharing); } catch (IOException e) { - log.info("failed to index resource {}", resourceId); - throw new RuntimeException(e); + log.info("Failed to create a resource sharing entry for resource: {}", resourceId); } } @Override public void postDelete(ShardId shardId, Engine.Delete delete, Engine.DeleteResult result) { - // implement a check to see if a resource was deleted - log.warn("postDelete called on " + shardId.getIndexName()); + String resourceIndex = shardId.getIndexName(); + log.info("postDelete called on {}", resourceIndex); + + String resourceId = delete.id(); + + boolean success = this.resourceSharingIndexHandler.deleteResourceSharingRecord(resourceId, resourceIndex); + if (success) { + log.info("Successfully deleted resource sharing entries for resource {}", resourceId); + } else { + log.info("Failed to delete resource sharing entry for resource {}", resourceId); + } + } } From 58003f6881d0ede85eb624617dfb48f7e3c13daa Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 27 Nov 2024 14:50:18 -0500 Subject: [PATCH 24/42] Fixes spotless errors Signed-off-by: Darshit Chanpura --- .../ResourceSharingIndexHandler.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 5568ee06d6..f4e2c134c1 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -10,7 +10,11 @@ package org.opensearch.security.resources; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Callable; import org.apache.commons.lang3.StringUtils; @@ -18,12 +22,20 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.search.join.ScoreMode; -import org.opensearch.accesscontrol.resources.*; +import org.opensearch.accesscontrol.resources.CreatedBy; +import org.opensearch.accesscontrol.resources.EntityType; +import org.opensearch.accesscontrol.resources.ResourceSharing; +import org.opensearch.accesscontrol.resources.ShareWith; +import org.opensearch.accesscontrol.resources.SharedWithScope; 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.search.*; +import org.opensearch.action.search.ClearScrollRequest; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.SearchScrollAction; +import org.opensearch.action.search.SearchScrollRequest; import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; import org.opensearch.common.unit.TimeValue; @@ -36,7 +48,11 @@ import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.index.reindex.*; +import org.opensearch.index.reindex.BulkByScrollResponse; +import org.opensearch.index.reindex.DeleteByQueryAction; +import org.opensearch.index.reindex.DeleteByQueryRequest; +import org.opensearch.index.reindex.UpdateByQueryAction; +import org.opensearch.index.reindex.UpdateByQueryRequest; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.Scroll; From 078a976edcdca9095b52d88fa6aadb9d95fd8f46 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 27 Nov 2024 14:53:13 -0500 Subject: [PATCH 25/42] Fixes log statement Signed-off-by: Darshit Chanpura --- .../org/opensearch/security/OpenSearchSecurityPlugin.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index ccee464e01..24f146a033 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -724,7 +724,6 @@ public void onIndexModule(IndexModule indexModule) { ) ); - log.info("Indices to listen to: {}", this.indicesToListen); if (this.indicesToListen.contains(indexModule.getIndex().getName())) { ResourceSharingIndexListener resourceSharingIndexListener = ResourceSharingIndexListener.getInstance(); resourceSharingIndexListener.initialize(threadPool, localClient); @@ -2099,12 +2098,11 @@ public void onNodeStarted(DiscoveryNode localNode) { // create resource sharing index if absent rmr.createResourceSharingIndexIfAbsent(); - log.info("Loading resource plugins"); for (ResourcePlugin resourcePlugin : OpenSearchSecurityPlugin.GuiceHolder.getResourceService().listResourcePlugins()) { String resourceIndex = resourcePlugin.getResourceIndex(); this.indicesToListen.add(resourceIndex); - log.info("Loaded resource plugin: {}, index: {}", resourcePlugin, resourceIndex); + log.info("Preparing to listen to index: {} of plugin: {}", resourceIndex, resourcePlugin); } final Set securityModules = ReflectionHelper.getModulesLoaded(); From 8e44cf333e67f07acf07141210e869262ab1dedf Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 4 Dec 2024 14:34:35 -0500 Subject: [PATCH 26/42] Renames ResourceManagement repository and add keyword to search query term Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 6 +- .../ResourceSharingIndexHandler.java | 217 +++++++++++------- ...urceSharingIndexManagementRepository.java} | 8 +- 3 files changed, 142 insertions(+), 89 deletions(-) rename src/main/java/org/opensearch/security/resources/{ResourceManagementRepository.java => ResourceSharingIndexManagementRepository.java} (72%) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 24f146a033..4297a95083 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -181,9 +181,9 @@ import org.opensearch.security.privileges.dlsfls.DlsFlsBaseContext; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resources.ResourceAccessHandler; -import org.opensearch.security.resources.ResourceManagementRepository; import org.opensearch.security.resources.ResourceSharingIndexHandler; import org.opensearch.security.resources.ResourceSharingIndexListener; +import org.opensearch.security.resources.ResourceSharingIndexManagementRepository; import org.opensearch.security.rest.DashboardsInfoAction; import org.opensearch.security.rest.SecurityConfigUpdateAction; import org.opensearch.security.rest.SecurityHealthAction; @@ -280,7 +280,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin private volatile OpensearchDynamicSetting transportPassiveAuthSetting; private volatile PasswordHasher passwordHasher; private volatile DlsFlsBaseContext dlsFlsBaseContext; - private ResourceManagementRepository rmr; + private ResourceSharingIndexManagementRepository rmr; private ResourceAccessHandler resourceAccessHandler; private final Set indicesToListen = new HashSet<>(); @@ -1218,7 +1218,7 @@ public Collection createComponents( ResourceSharingIndexHandler rsIndexHandler = new ResourceSharingIndexHandler(resourceSharingIndex, localClient, threadPool); resourceAccessHandler = new ResourceAccessHandler(threadPool, rsIndexHandler, adminDns); - rmr = ResourceManagementRepository.create(rsIndexHandler); + rmr = ResourceSharingIndexManagementRepository.create(rsIndexHandler); components.add(adminDns); components.add(cr); diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index f4e2c134c1..592162f206 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -216,7 +216,7 @@ public List fetchAllDocuments(String pluginIndex) { SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - searchSourceBuilder.query(QueryBuilders.termQuery("source_idx", pluginIndex)); + searchSourceBuilder.query(QueryBuilders.termQuery("source_idx.keyword", pluginIndex)); searchSourceBuilder.size(10000); // TODO check what size should be set here. searchSourceBuilder.fetchSource(new String[] { "resource_id" }, null); @@ -312,7 +312,84 @@ public List fetchAllDocuments(String pluginIndex) { */ public List fetchDocumentsForAllScopes(String pluginIndex, Set entities, String entityType) { - LOGGER.debug("Fetching documents from index: {}, where share_with.*.{} contains any of {}", pluginIndex, entityType, entities); + // "*" must match all scopes + return fetchDocumentsForAGivenScope(pluginIndex, entities, entityType, "*"); + } + + /** + * Fetches documents that match the specified system index and have specific access type values for a given scope. + * This method uses scroll API to handle large result sets efficiently. + * + *

The method executes the following steps: + *

    + *
  1. Validates the entityType parameter
  2. + *
  3. Creates a scrolling search request with a compound query
  4. + *
  5. Processes results in batches using scroll API
  6. + *
  7. Collects all matching resource IDs
  8. + *
  9. Cleans up scroll context
  10. + *
+ * + *

Example query structure: + *

+     * {
+     *   "query": {
+     *     "bool": {
+     *       "must": [
+     *         { "term": { "source_idx": "system_index_name" } },
+     *         {
+     *           "bool": {
+     *             "should": [
+     *               {
+     *                 "nested": {
+     *                   "path": "share_with.scope.entityType",
+     *                   "query": {
+     *                     "term": { "share_with.scope.entityType": "entity_value" }
+     *                   }
+     *                 }
+     *               }
+     *             ],
+     *             "minimum_should_match": 1
+     *           }
+     *         }
+     *       ]
+     *     }
+     *   },
+     *   "_source": ["resource_id"],
+     *   "size": 1000
+     * }
+     * 
+ * + * @param pluginIndex The source index to match against the source_idx field + * @param entities Set of values to match in the specified entityType field + * @param entityType The type of association with the resource. Must be one of: + *
    + *
  • "users" - for user-based access
  • + *
  • "roles" - for role-based access
  • + *
  • "backend_roles" - for backend role-based access
  • + *
+ * @param scope The scope of the access. Should be implementation of {@link org.opensearch.accesscontrol.resources.ResourceAccessScope} + * @return List List of resource IDs that match the criteria. The list may be empty + * if no matches are found + * + * @throws RuntimeException if the search operation fails + * + * @apiNote This method: + *
    + *
  • Uses scroll API with 1-minute timeout
  • + *
  • Processes results in batches of 1000 documents
  • + *
  • Performs source filtering for optimization
  • + *
  • Uses nested queries for accessing array elements
  • + *
  • Properly cleans up scroll context after use
  • + *
+ */ + public List fetchDocumentsForAGivenScope(String pluginIndex, Set entities, String entityType, String scope) { + LOGGER.debug( + "Fetching documents from index: {}, where share_with.{}.{} contains any of {}", + pluginIndex, + scope, + entityType, + entities + ); List resourceIds = new ArrayList<>(); final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); @@ -321,49 +398,23 @@ public List fetchDocumentsForAllScopes(String pluginIndex, Set e SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); searchRequest.scroll(scroll); - BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("source_idx", pluginIndex)); + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("source_idx.keyword", pluginIndex)); BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery(); for (String entity : entities) { shouldQuery.should( QueryBuilders.nestedQuery( - "share_with.*." + entityType, - QueryBuilders.termQuery("share_with.*." + entityType, entity), + "share_with." + scope + "." + entityType, + QueryBuilders.termQuery("share_with." + scope + "." + entityType, entity), ScoreMode.None ) ); } shouldQuery.minimumShouldMatch(1); - boolQuery.must(shouldQuery); - - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery) - .size(1000) - .fetchSource(new String[] { "resource_id" }, null); - - searchRequest.source(searchSourceBuilder); - - SearchResponse searchResponse = client.search(searchRequest).actionGet(); - String scrollId = searchResponse.getScrollId(); - SearchHit[] hits = searchResponse.getHits().getHits(); - - while (hits != null && hits.length > 0) { - for (SearchHit hit : hits) { - Map sourceAsMap = hit.getSourceAsMap(); - if (sourceAsMap != null && sourceAsMap.containsKey("resource_id")) { - resourceIds.add(sourceAsMap.get("resource_id").toString()); - } - } - SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); - scrollRequest.scroll(scroll); - searchResponse = client.execute(SearchScrollAction.INSTANCE, scrollRequest).actionGet(); - scrollId = searchResponse.getScrollId(); - hits = searchResponse.getHits().getHits(); - } + boolQuery.must(QueryBuilders.existsQuery("share_with")).must(shouldQuery); - ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); - clearScrollRequest.addScrollId(scrollId); - client.clearScroll(clearScrollRequest).actionGet(); + executeSearchRequest(resourceIds, scroll, searchRequest, boolQuery); LOGGER.debug("Found {} documents matching the criteria in {}", resourceIds.size(), resourceSharingIndex); @@ -371,9 +422,10 @@ public List fetchDocumentsForAllScopes(String pluginIndex, Set e } catch (Exception e) { LOGGER.error( - "Failed to fetch documents from {} for criteria - systemIndex: {}, shareWithType: {}, accessWays: {}", + "Failed to fetch documents from {} for criteria - systemIndex: {}, scope: {}, entityType: {}, entities: {}", resourceSharingIndex, pluginIndex, + scope, entityType, entities, e @@ -435,7 +487,6 @@ public List fetchDocumentsForAllScopes(String pluginIndex, Set e * List resources = fetchDocumentsByField("myIndex", "status", "active"); * */ - public List fetchDocumentsByField(String systemIndex, String field, String value) { if (StringUtils.isBlank(systemIndex) || StringUtils.isBlank(field) || StringUtils.isBlank(value)) { throw new IllegalArgumentException("systemIndex, field, and value must not be null or empty"); @@ -447,48 +498,16 @@ public List fetchDocumentsByField(String systemIndex, String field, Stri final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); try { - // Create initial search request SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); searchRequest.scroll(scroll); - // Build the query BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() - .must(QueryBuilders.termQuery("source_idx", systemIndex)) - .must(QueryBuilders.termQuery(field, value)); + .must(QueryBuilders.termQuery("source_idx.keyword", systemIndex)) + .must(QueryBuilders.termQuery(field + ".keyword", value)); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery) - .size(1000) - .fetchSource(new String[] { "resource_id" }, null); + executeSearchRequest(resourceIds, scroll, searchRequest, boolQuery); - searchRequest.source(searchSourceBuilder); - - // Execute initial search - SearchResponse searchResponse = client.search(searchRequest).actionGet(); - String scrollId = searchResponse.getScrollId(); - SearchHit[] hits = searchResponse.getHits().getHits(); - - // Process results in batches - while (hits != null && hits.length > 0) { - for (SearchHit hit : hits) { - Map sourceAsMap = hit.getSourceAsMap(); - if (sourceAsMap != null && sourceAsMap.containsKey("resource_id")) { - resourceIds.add(sourceAsMap.get("resource_id").toString()); - } - } - - SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); - scrollRequest.scroll(scroll); - searchResponse = client.execute(SearchScrollAction.INSTANCE, scrollRequest).actionGet(); - scrollId = searchResponse.getScrollId(); - hits = searchResponse.getHits().getHits(); - } - - // Clear scroll - ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); - clearScrollRequest.addScrollId(scrollId); - client.clearScroll(clearScrollRequest).actionGet(); - - LOGGER.debug("Found {} documents in {} where {} = {}", resourceIds.size(), resourceSharingIndex, field, value); + LOGGER.info("Found {} documents in {} where {} = {}", resourceIds.size(), resourceSharingIndex, field, value); return resourceIds; @@ -565,8 +584,8 @@ public ResourceSharing fetchDocumentById(String pluginIndex, String resourceId) SearchRequest searchRequest = new SearchRequest(resourceSharingIndex); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() - .must(QueryBuilders.termQuery("source_idx", pluginIndex)) - .must(QueryBuilders.termQuery("resource_id", resourceId)); + .must(QueryBuilders.termQuery("source_idx.keyword", pluginIndex)) + .must(QueryBuilders.termQuery("resource_id.keyword", resourceId)); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery).size(1); // We only need one document since // a resource must have only one @@ -603,6 +622,44 @@ public ResourceSharing fetchDocumentById(String pluginIndex, String resourceId) } } + /** + * Helper method to execute a search request and collect resource IDs from the results. + * @param resourceIds List to collect resource IDs + * @param scroll Search Scroll + * @param searchRequest Request to execute + * @param boolQuery Query to execute with the request + */ + private void executeSearchRequest(List resourceIds, Scroll scroll, SearchRequest searchRequest, BoolQueryBuilder boolQuery) { + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery) + .size(1000) + .fetchSource(new String[] { "resource_id" }, null); + + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = client.search(searchRequest).actionGet(); + String scrollId = searchResponse.getScrollId(); + SearchHit[] hits = searchResponse.getHits().getHits(); + + while (hits != null && hits.length > 0) { + for (SearchHit hit : hits) { + Map sourceAsMap = hit.getSourceAsMap(); + if (sourceAsMap != null && sourceAsMap.containsKey("resource_id")) { + resourceIds.add(sourceAsMap.get("resource_id").toString()); + } + } + + SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); + scrollRequest.scroll(scroll); + searchResponse = client.execute(SearchScrollAction.INSTANCE, scrollRequest).actionGet(); + scrollId = searchResponse.getScrollId(); + hits = searchResponse.getHits().getHits(); + } + + ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); + clearScrollRequest.addScrollId(scrollId); + client.clearScroll(clearScrollRequest).actionGet(); + } + /** * Updates resource sharing entries that match the specified source index and resource ID * using the provided update script. This method performs an update-by-query operation @@ -660,8 +717,8 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId try { // Create a bool query to match both fields BoolQueryBuilder query = QueryBuilders.boolQuery() - .must(QueryBuilders.termQuery("source_idx", sourceIdx)) - .must(QueryBuilders.termQuery("resource_id", resourceId)); + .must(QueryBuilders.termQuery("source_idx.keyword", sourceIdx)) + .must(QueryBuilders.termQuery("resource_id.keyword", resourceId)); UpdateByQueryRequest ubq = new UpdateByQueryRequest(resourceSharingIndex).setQuery(query).setScript(updateScript); @@ -710,7 +767,7 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc Script updateScript = new Script( ScriptType.INLINE, "painless", - "ctx._source.shareWith = params.newShareWith", + "ctx._source.share_with = params.newShareWith", Collections.singletonMap("newShareWith", shareWith) ); @@ -737,7 +794,7 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc * "source_idx": "system_index_name", * "resource_id": "resource_id", * "share_with": { - * "group_name": { + * "scope": { * "users": ["user1", "user2"], * "roles": ["role1", "role2"], * "backend_roles": ["backend_role1"] @@ -767,11 +824,9 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc * ResourceSharing updated = revokeAccess("resourceId", "systemIndex", revokeAccess); * */ - public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { // TODO; check if this needs to be done per scope rather than for all scopes - // Input validation if (StringUtils.isBlank(resourceId) || StringUtils.isBlank(systemIndexName) || revokeAccess == null || revokeAccess.isEmpty()) { throw new IllegalArgumentException("resourceId, systemIndexName, and revokeAccess must not be null or empty"); } @@ -779,7 +834,6 @@ public ResourceSharing revokeAccess(String resourceId, String systemIndexName, M LOGGER.debug("Revoking access for resource {} in {} for entities: {}", resourceId, systemIndexName, revokeAccess); try { - // First fetch the existing document ResourceSharing existingResource = fetchDocumentById(systemIndexName, resourceId); if (existingResource == null) { LOGGER.warn("No document found for resourceId: {} in index: {}", resourceId, systemIndexName); @@ -880,7 +934,7 @@ public boolean deleteResourceSharingRecord(String resourceId, String sourceIdx) try { DeleteByQueryRequest dbq = new DeleteByQueryRequest(resourceSharingIndex).setQuery( QueryBuilders.boolQuery() - .must(QueryBuilders.termQuery("source_idx", sourceIdx)) + .must(QueryBuilders.termQuery("source_idx.keyword", sourceIdx)) .must(QueryBuilders.termQuery("resource_id", resourceId)) ).setRefresh(true); @@ -959,7 +1013,6 @@ public boolean deleteResourceSharingRecord(String resourceId, String sourceIdx) * */ public boolean deleteAllRecordsForUser(String name) { - // Input validation if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("Username must not be null or empty"); } diff --git a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexManagementRepository.java similarity index 72% rename from src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java rename to src/main/java/org/opensearch/security/resources/ResourceSharingIndexManagementRepository.java index 84749153f5..60cb48145f 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceManagementRepository.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexManagementRepository.java @@ -11,17 +11,17 @@ package org.opensearch.security.resources; -public class ResourceManagementRepository { +public class ResourceSharingIndexManagementRepository { private final ResourceSharingIndexHandler resourceSharingIndexHandler; - protected ResourceManagementRepository(final ResourceSharingIndexHandler resourceSharingIndexHandler) { + protected ResourceSharingIndexManagementRepository(final ResourceSharingIndexHandler resourceSharingIndexHandler) { this.resourceSharingIndexHandler = resourceSharingIndexHandler; } - public static ResourceManagementRepository create(ResourceSharingIndexHandler resourceSharingIndexHandler) { + public static ResourceSharingIndexManagementRepository create(ResourceSharingIndexHandler resourceSharingIndexHandler) { - return new ResourceManagementRepository(resourceSharingIndexHandler); + return new ResourceSharingIndexManagementRepository(resourceSharingIndexHandler); } /** From 16a0ba69d222a1bb7b1c344edb958dd50f6aa0e1 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 4 Dec 2024 17:37:41 -0500 Subject: [PATCH 27/42] Fixes delete method Signed-off-by: Darshit Chanpura --- .../security/resources/ResourceSharingIndexHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 592162f206..7270117a1a 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -935,7 +935,7 @@ public boolean deleteResourceSharingRecord(String resourceId, String sourceIdx) DeleteByQueryRequest dbq = new DeleteByQueryRequest(resourceSharingIndex).setQuery( QueryBuilders.boolQuery() .must(QueryBuilders.termQuery("source_idx.keyword", sourceIdx)) - .must(QueryBuilders.termQuery("resource_id", resourceId)) + .must(QueryBuilders.termQuery("resource_id.keyword", resourceId)) ).setRefresh(true); BulkByScrollResponse response = client.execute(DeleteByQueryAction.INSTANCE, dbq).actionGet(); From ac53c8feb37871ceedc276786bcaab6ec8170892 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 5 Dec 2024 12:32:01 -0500 Subject: [PATCH 28/42] Fixes updateByQuery painless script Signed-off-by: Darshit Chanpura --- .../ResourceSharingIndexHandler.java | 123 +++++++++++++----- 1 file changed, 90 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 7270117a1a..46e61f372a 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -17,6 +17,7 @@ import java.util.Set; import java.util.concurrent.Callable; +import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -41,10 +42,12 @@ import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.action.ActionListener; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -58,6 +61,7 @@ import org.opensearch.search.Scroll; import org.opensearch.search.SearchHit; import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.security.DefaultObjectMapper; import org.opensearch.threadpool.ThreadPool; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; @@ -660,6 +664,88 @@ private void executeSearchRequest(List resourceIds, Scroll scroll, Searc client.clearScroll(clearScrollRequest).actionGet(); } + /** + * Updates the sharing configuration for an existing resource in the resource sharing index. + * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map)} + * This method modifies the sharing permissions for a specific resource identified by its + * resource ID and source index. + * + * @param resourceId The unique identifier of the resource whose sharing configuration needs to be updated + * @param sourceIdx The source index where the original resource is stored + * @param shareWith Updated sharing configuration object containing access control settings: + * { + * "scope": { + * "users": ["user1", "user2"], + * "roles": ["role1", "role2"], + * "backend_roles": ["backend_role1"] + * } + * } + * @return ResourceSharing Returns resourceSharing object if the update was successful, null otherwise + * @throws RuntimeException if there's an error during the update operation + */ + public ResourceSharing updateResourceSharingInfo(String resourceId, String sourceIdx, CreatedBy createdBy, ShareWith shareWith) { + XContentBuilder builder; + Map shareWithMap; + try { + builder = XContentFactory.jsonBuilder(); + shareWith.toXContent(builder, ToXContent.EMPTY_PARAMS); + String json = builder.toString(); + shareWithMap = DefaultObjectMapper.readValue(json, new TypeReference<>() { + }); + + } catch (IOException e) { + LOGGER.error("Failed to build json content", e); + return null; + } + + // Atomic operation + Script updateScript = new Script(ScriptType.INLINE, "painless", """ + if (ctx._source.share_with == null) { + ctx._source.share_with = [:]; + } + for (def entry : params.shareWith.entrySet()) { + def scopeName = entry.getKey(); + def newScope = entry.getValue(); + if (ctx._source.share_with.containsKey(scopeName)) { + def existingScope = ctx._source.share_with.get(scopeName); + if (newScope.users != null) { + if (existingScope.users == null) { + existingScope.users = new HashSet(); + } + existingScope.users.addAll(newScope.users); + } + if (newScope.roles != null) { + if (existingScope.roles == null) { + existingScope.roles = new HashSet(); + } + existingScope.roles.addAll(newScope.roles); + } + if (newScope.backend_roles != null) { + if (existingScope.backend_roles == null) { + existingScope.backend_roles = new HashSet(); + } + existingScope.backend_roles.addAll(newScope.backend_roles); + } + } else { + def newScopeEntry = [:]; + if (newScope.users != null) { + newScopeEntry.users = new HashSet(newScope.users); + } + if (newScope.roles != null) { + newScopeEntry.roles = new HashSet(newScope.roles); + } + if (newScope.backend_roles != null) { + newScopeEntry.backend_roles = new HashSet(newScope.backend_roles); + } + ctx._source.share_with.put(scopeName, newScopeEntry); + } + } + """, Collections.singletonMap("shareWith", shareWithMap)); + + boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, updateScript); + return success ? new ResourceSharing(resourceId, sourceIdx, createdBy, shareWith) : null; + } + /** * Updates resource sharing entries that match the specified source index and resource ID * using the provided update script. This method performs an update-by-query operation @@ -715,14 +801,15 @@ private void executeSearchRequest(List resourceIds, Scroll scroll, Searc */ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId, Script updateScript) { try { - // Create a bool query to match both fields BoolQueryBuilder query = QueryBuilders.boolQuery() .must(QueryBuilders.termQuery("source_idx.keyword", sourceIdx)) .must(QueryBuilders.termQuery("resource_id.keyword", resourceId)); - UpdateByQueryRequest ubq = new UpdateByQueryRequest(resourceSharingIndex).setQuery(query).setScript(updateScript); + UpdateByQueryRequest ubq = new UpdateByQueryRequest(resourceSharingIndex).setQuery(query) + .setScript(updateScript) + .setRefresh(true); - LOGGER.info("Update By Query Request: {}", ubq.toString()); + LOGGER.debug("Update By Query Request: {}", ubq.toString()); BulkByScrollResponse response = client.execute(UpdateByQueryAction.INSTANCE, ubq).actionGet(); @@ -745,36 +832,6 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId } } - /** - * Updates the sharing configuration for an existing resource in the resource sharing index. - * This method modifies the sharing permissions for a specific resource identified by its - * resource ID and source index. - * - * @param resourceId The unique identifier of the resource whose sharing configuration needs to be updated - * @param sourceIdx The source index where the original resource is stored - * @param shareWith Updated sharing configuration object containing access control settings: - * { - * "scope": { - * "users": ["user1", "user2"], - * "roles": ["role1", "role2"], - * "backend_roles": ["backend_role1"] - * } - * } - * @return ResourceSharing Returns resourceSharing object if the update was successful, null otherwise - * @throws RuntimeException if there's an error during the update operation - */ - public ResourceSharing updateResourceSharingInfo(String resourceId, String sourceIdx, CreatedBy createdBy, ShareWith shareWith) { - Script updateScript = new Script( - ScriptType.INLINE, - "painless", - "ctx._source.share_with = params.newShareWith", - Collections.singletonMap("newShareWith", shareWith) - ); - - boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, updateScript); - return success ? new ResourceSharing(resourceId, sourceIdx, createdBy, shareWith) : null; - } - /** * Revokes access for specified entities from a resource sharing document. This method removes the specified * entities (users, roles, or backend roles) from the existing sharing configuration while preserving other From 9e6ae850428211dd4d79608f9897a7cfeb0283cf Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 5 Dec 2024 16:57:16 -0500 Subject: [PATCH 29/42] Updates revoke handler to use painless script Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 9 +- .../resources/ResourceAccessHandler.java | 9 +- .../ResourceSharingIndexHandler.java | 130 +++++++++--------- 3 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 4297a95083..8b70509390 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -2223,8 +2223,13 @@ public ResourceSharing shareWith(String resourceId, String systemIndexName, Shar } @Override - public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> entities) { - return this.resourceAccessHandler.revokeAccess(resourceId, systemIndexName, entities); + public ResourceSharing revokeAccess( + String resourceId, + String systemIndexName, + Map> entities, + List scopes + ) { + return this.resourceAccessHandler.revokeAccess(resourceId, systemIndexName, entities, scopes); } @Override diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index d5e79a1fdf..143dda52a3 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -116,11 +116,16 @@ public ResourceSharing shareWith(String resourceId, String systemIndexName, Shar return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, createdBy, shareWith); } - public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { + public ResourceSharing revokeAccess( + String resourceId, + String systemIndexName, + Map> revokeAccess, + List scopes + ) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Revoking access to resource {} created by {} for {}", resourceId, user.getName(), revokeAccess); - return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess); + return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess, scopes); } public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 46e61f372a..503701127a 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -21,13 +21,11 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.lucene.search.join.ScoreMode; import org.opensearch.accesscontrol.resources.CreatedBy; import org.opensearch.accesscontrol.resources.EntityType; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; -import org.opensearch.accesscontrol.resources.SharedWithScope; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; import org.opensearch.action.index.IndexRequest; @@ -50,6 +48,7 @@ import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.BoolQueryBuilder; +import org.opensearch.index.query.MultiMatchQueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.reindex.BulkByScrollResponse; import org.opensearch.index.reindex.DeleteByQueryAction; @@ -156,8 +155,6 @@ public ResourceSharing indexResourceSharing(String resourceId, String resourceIn .setSource(entry.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) .request(); - LOGGER.info("Index Request: {}", ir.toString()); - ActionListener irListener = ActionListener.wrap(idxResponse -> { LOGGER.info("Successfully created {} entry.", resourceSharingIndex); }, (failResponse) -> { @@ -405,14 +402,19 @@ public List fetchDocumentsForAGivenScope(String pluginIndex, Set BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("source_idx.keyword", pluginIndex)); BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery(); - for (String entity : entities) { - shouldQuery.should( - QueryBuilders.nestedQuery( - "share_with." + scope + "." + entityType, - QueryBuilders.termQuery("share_with." + scope + "." + entityType, entity), - ScoreMode.None - ) - ); + if ("*".equals(scope)) { + // Wildcard behavior: Match any scope dynamically + for (String entity : entities) { + shouldQuery.should( + QueryBuilders.multiMatchQuery(entity, "share_with.*." + entityType + ".keyword") + .type(MultiMatchQueryBuilder.Type.BEST_FIELDS) + ); + } + } else { + // Match the specific scope + for (String entity : entities) { + shouldQuery.should(QueryBuilders.termQuery("share_with." + scope + "." + entityType + ".keyword", entity)); + } } shouldQuery.minimumShouldMatch(1); @@ -666,7 +668,7 @@ private void executeSearchRequest(List resourceIds, Scroll scroll, Searc /** * Updates the sharing configuration for an existing resource in the resource sharing index. - * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map)} + * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map, List)} * This method modifies the sharing permissions for a specific resource identified by its * resource ID and source index. * @@ -861,11 +863,12 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId * * * @param resourceId The ID of the resource from which to revoke access - * @param systemIndexName The name of the system index where the resource exists + * @param sourceIdx The name of the system index where the resource exists * @param revokeAccess A map containing entity types (USER, ROLE, BACKEND_ROLE) and their corresponding * values to be removed from the sharing configuration + * @param scopes A list of scopes to revoke access from. If null or empty, access is revoked from all scopes * @return The updated ResourceSharing object after revoking access, or null if the document doesn't exist - * @throws IllegalArgumentException if resourceId, systemIndexName is null/empty, or if revokeAccess is null/empty + * @throws IllegalArgumentException if resourceId, sourceIdx is null/empty, or if revokeAccess is null/empty * @throws RuntimeException if the update operation fails or encounters an error * * @see EntityType @@ -881,65 +884,58 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId * ResourceSharing updated = revokeAccess("resourceId", "systemIndex", revokeAccess); * */ - public ResourceSharing revokeAccess(String resourceId, String systemIndexName, Map> revokeAccess) { - // TODO; check if this needs to be done per scope rather than for all scopes - - if (StringUtils.isBlank(resourceId) || StringUtils.isBlank(systemIndexName) || revokeAccess == null || revokeAccess.isEmpty()) { - throw new IllegalArgumentException("resourceId, systemIndexName, and revokeAccess must not be null or empty"); + public ResourceSharing revokeAccess( + String resourceId, + String sourceIdx, + Map> revokeAccess, + List scopes + ) { + if (StringUtils.isBlank(resourceId) || StringUtils.isBlank(sourceIdx) || revokeAccess == null || revokeAccess.isEmpty()) { + throw new IllegalArgumentException("resourceId, sourceIdx, and revokeAccess must not be null or empty"); } - LOGGER.debug("Revoking access for resource {} in {} for entities: {}", resourceId, systemIndexName, revokeAccess); + LOGGER.debug("Revoking access for resource {} in {} for entities: {} and scopes: {}", resourceId, sourceIdx, revokeAccess, scopes); try { - ResourceSharing existingResource = fetchDocumentById(systemIndexName, resourceId); - if (existingResource == null) { - LOGGER.warn("No document found for resourceId: {} in index: {}", resourceId, systemIndexName); - return null; - } - - ShareWith shareWith = existingResource.getShareWith(); - boolean modified = false; - - if (shareWith != null) { - for (SharedWithScope sharedWithScope : shareWith.getSharedWithScopes()) { - SharedWithScope.SharedWithPerScope sharedWithPerScope = sharedWithScope.getSharedWithPerScope(); - - for (Map.Entry> entry : revokeAccess.entrySet()) { - EntityType entityType = entry.getKey(); - List entities = entry.getValue(); - - // Check if the entity type exists in the share_with configuration - switch (entityType) { - case USERS: - if (sharedWithPerScope.getUsers() != null) { - modified = sharedWithPerScope.getUsers().removeAll(entities) || modified; - } - break; - case ROLES: - if (sharedWithPerScope.getRoles() != null) { - modified = sharedWithPerScope.getRoles().removeAll(entities) || modified; + // Revoke resource access + Script revokeScript = new Script( + ScriptType.INLINE, + "painless", + """ + if (ctx._source.share_with != null) { + List scopesToProcess = params.scopes == null || params.scopes.isEmpty() ? new ArrayList(ctx._source.share_with.keySet()) : params.scopes; + + for (def scopeName : scopesToProcess) { + if (ctx._source.share_with.containsKey(scopeName)) { + def existingScope = ctx._source.share_with.get(scopeName); + + for (def entry : params.revokeAccess.entrySet()) { + def entityType = entry.getKey(); + def entitiesToRemove = entry.getValue(); + + if (existingScope.containsKey(entityType) && existingScope[entityType] != null) { + existingScope[entityType].removeAll(entitiesToRemove); + if (existingScope[entityType].isEmpty()) { + existingScope.remove(entityType); + } + } + } + + if (existingScope.isEmpty()) { + ctx._source.share_with.remove(scopeName); + } } - break; - case BACKEND_ROLES: - if (sharedWithPerScope.getBackendRoles() != null) { - modified = sharedWithPerScope.getBackendRoles().removeAll(entities) || modified; - } - break; + } } - } - } - } - - if (!modified) { - LOGGER.debug("No modifications needed for resource: {}", resourceId); - return existingResource; - } + """, + Map.of("revokeAccess", revokeAccess, "scopes", scopes) + ); - // Update resource sharing info - return updateResourceSharingInfo(resourceId, systemIndexName, existingResource.getCreatedBy(), shareWith); + boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, revokeScript); + return success ? fetchDocumentById(sourceIdx, resourceId) : null; } catch (Exception e) { - LOGGER.error("Failed to revoke access for resource: {} in index: {}", resourceId, systemIndexName, e); + LOGGER.error("Failed to revoke access for resource: {} in index: {}", resourceId, sourceIdx, e); throw new RuntimeException("Failed to revoke access: " + e.getMessage(), e); } } @@ -986,7 +982,7 @@ public ResourceSharing revokeAccess(String resourceId, String systemIndexName, M * */ public boolean deleteResourceSharingRecord(String resourceId, String sourceIdx) { - LOGGER.info("Deleting documents from {} where source_idx = {} and resource_id = {}", resourceSharingIndex, sourceIdx, resourceId); + LOGGER.debug("Deleting documents from {} where source_idx = {} and resource_id = {}", resourceSharingIndex, sourceIdx, resourceId); try { DeleteByQueryRequest dbq = new DeleteByQueryRequest(resourceSharingIndex).setQuery( @@ -1074,7 +1070,7 @@ public boolean deleteAllRecordsForUser(String name) { throw new IllegalArgumentException("Username must not be null or empty"); } - LOGGER.info("Deleting all records for user {}", name); + LOGGER.debug("Deleting all records for user {}", name); try { DeleteByQueryRequest deleteRequest = new DeleteByQueryRequest(resourceSharingIndex).setQuery( From 0fe9779de06e7b57ecb9358d3c517d9cf64f9add Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 5 Dec 2024 17:58:37 -0500 Subject: [PATCH 30/42] Convert sets to lists Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 6 +-- .../resources/ResourceAccessHandler.java | 17 ++++--- .../ResourceSharingIndexHandler.java | 48 +++++++++---------- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 8b70509390..0e3e612e20 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -2208,7 +2208,7 @@ private void tryAddSecurityProvider() { } @Override - public List listAccessibleResourcesInPlugin(String systemIndexName) { + public Set listAccessibleResourcesInPlugin(String systemIndexName) { return this.resourceAccessHandler.listAccessibleResourcesInPlugin(systemIndexName); } @@ -2226,8 +2226,8 @@ public ResourceSharing shareWith(String resourceId, String systemIndexName, Shar public ResourceSharing revokeAccess( String resourceId, String systemIndexName, - Map> entities, - List scopes + Map> entities, + Set scopes ) { return this.resourceAccessHandler.revokeAccess(resourceId, systemIndexName, entities, scopes); } diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 143dda52a3..7812778981 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -13,7 +13,6 @@ import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -49,11 +48,11 @@ public ResourceAccessHandler( this.adminDNs = adminDns; } - public List listAccessibleResourcesInPlugin(String pluginIndex) { + public Set listAccessibleResourcesInPlugin(String pluginIndex) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); if (user == null) { LOGGER.info("Unable to fetch user details "); - return Collections.emptyList(); + return Collections.emptySet(); } LOGGER.info("Listing accessible resource within a system index {} for : {}", pluginIndex, user.getName()); @@ -79,7 +78,7 @@ public List listAccessibleResourcesInPlugin(String pluginIndex) { Set backendRoles = user.getRoles(); result.addAll(loadSharedWithResources(pluginIndex, backendRoles, EntityType.BACKEND_ROLES.toString())); - return result.stream().toList(); + return result; } public boolean hasPermission(String resourceId, String systemIndexName, String scope) { @@ -119,8 +118,8 @@ public ResourceSharing shareWith(String resourceId, String systemIndexName, Shar public ResourceSharing revokeAccess( String resourceId, String systemIndexName, - Map> revokeAccess, - List scopes + Map> revokeAccess, + Set scopes ) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Revoking access to resource {} created by {} for {}", resourceId, user.getName(), revokeAccess); @@ -153,16 +152,16 @@ public boolean deleteAllResourceSharingRecordsForCurrentUser() { // Helper methods - private List loadAllResources(String systemIndex) { + private Set loadAllResources(String systemIndex) { return this.resourceSharingIndexHandler.fetchAllDocuments(systemIndex); } - private List loadOwnResources(String systemIndex, String username) { + private Set loadOwnResources(String systemIndex, String username) { // TODO check if this magic variable can be replaced return this.resourceSharingIndexHandler.fetchDocumentsByField(systemIndex, "created_by.user", username); } - private List loadSharedWithResources(String systemIndex, Set entities, String shareWithType) { + private Set loadSharedWithResources(String systemIndex, Set entities, String shareWithType) { return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(systemIndex, entities, shareWithType); } diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 503701127a..309daa93d6 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -10,11 +10,7 @@ package org.opensearch.security.resources; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.Callable; import com.fasterxml.jackson.core.type.TypeReference; @@ -195,7 +191,7 @@ public ResourceSharing indexResourceSharing(String resourceId, String resourceIn * * * @param pluginIndex The source index to match against the source_idx field - * @return List containing resource IDs that belong to the specified system index. + * @return Set containing resource IDs that belong to the specified system index. * Returns an empty list if: *
    *
  • No matching documents are found
  • @@ -210,7 +206,7 @@ public ResourceSharing indexResourceSharing(String resourceId, String resourceIn *
  • Returns an empty list instead of throwing exceptions
  • *
*/ - public List fetchAllDocuments(String pluginIndex) { + public Set fetchAllDocuments(String pluginIndex) { LOGGER.debug("Fetching all documents from {} where source_idx = {}", resourceSharingIndex, pluginIndex); try { @@ -226,7 +222,7 @@ public List fetchAllDocuments(String pluginIndex) { SearchResponse searchResponse = client.search(searchRequest).actionGet(); - List resourceIds = new ArrayList<>(); + Set resourceIds = new HashSet<>(); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { @@ -242,7 +238,7 @@ public List fetchAllDocuments(String pluginIndex) { } catch (Exception e) { LOGGER.error("Failed to fetch documents from {} for source_idx: {}", resourceSharingIndex, pluginIndex, e); - return List.of(); + return Set.of(); } } @@ -297,7 +293,7 @@ public List fetchAllDocuments(String pluginIndex) { *
  • "roles" - for role-based access
  • *
  • "backend_roles" - for backend role-based access
  • * - * @return List List of resource IDs that match the criteria. The list may be empty + * @return Set List of resource IDs that match the criteria. The list may be empty * if no matches are found * * @throws RuntimeException if the search operation fails @@ -312,7 +308,7 @@ public List fetchAllDocuments(String pluginIndex) { * */ - public List fetchDocumentsForAllScopes(String pluginIndex, Set entities, String entityType) { + public Set fetchDocumentsForAllScopes(String pluginIndex, Set entities, String entityType) { // "*" must match all scopes return fetchDocumentsForAGivenScope(pluginIndex, entities, entityType, "*"); } @@ -369,7 +365,7 @@ public List fetchDocumentsForAllScopes(String pluginIndex, Set e *
  • "backend_roles" - for backend role-based access
  • * * @param scope The scope of the access. Should be implementation of {@link org.opensearch.accesscontrol.resources.ResourceAccessScope} - * @return List List of resource IDs that match the criteria. The list may be empty + * @return Set List of resource IDs that match the criteria. The list may be empty * if no matches are found * * @throws RuntimeException if the search operation fails @@ -383,7 +379,7 @@ public List fetchDocumentsForAllScopes(String pluginIndex, Set e *
  • Properly cleans up scroll context after use
  • * */ - public List fetchDocumentsForAGivenScope(String pluginIndex, Set entities, String entityType, String scope) { + public Set fetchDocumentsForAGivenScope(String pluginIndex, Set entities, String entityType, String scope) { LOGGER.debug( "Fetching documents from index: {}, where share_with.{}.{} contains any of {}", pluginIndex, @@ -392,7 +388,7 @@ public List fetchDocumentsForAGivenScope(String pluginIndex, Set entities ); - List resourceIds = new ArrayList<>(); + Set resourceIds = new HashSet<>(); final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); try { @@ -473,7 +469,7 @@ public List fetchDocumentsForAGivenScope(String pluginIndex, Set * @param systemIndex The source index to match against the source_idx field * @param field The field name to search in. Must be a valid field in the index mapping * @param value The value to match for the specified field. Performs exact term matching - * @return List List of resource IDs that match the criteria. Returns an empty list + * @return Set List of resource IDs that match the criteria. Returns an empty list * if no matches are found * * @throws IllegalArgumentException if any parameter is null or empty @@ -490,17 +486,17 @@ public List fetchDocumentsForAGivenScope(String pluginIndex, Set * * Example usage: *
    -     * List resources = fetchDocumentsByField("myIndex", "status", "active");
    +     * Set resources = fetchDocumentsByField("myIndex", "status", "active");
          * 
    */ - public List fetchDocumentsByField(String systemIndex, String field, String value) { + public Set fetchDocumentsByField(String systemIndex, String field, String value) { if (StringUtils.isBlank(systemIndex) || StringUtils.isBlank(field) || StringUtils.isBlank(value)) { throw new IllegalArgumentException("systemIndex, field, and value must not be null or empty"); } LOGGER.debug("Fetching documents from index: {}, where {} = {}", systemIndex, field, value); - List resourceIds = new ArrayList<>(); + Set resourceIds = new HashSet<>(); final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); try { @@ -635,7 +631,7 @@ public ResourceSharing fetchDocumentById(String pluginIndex, String resourceId) * @param searchRequest Request to execute * @param boolQuery Query to execute with the request */ - private void executeSearchRequest(List resourceIds, Scroll scroll, SearchRequest searchRequest, BoolQueryBuilder boolQuery) { + private void executeSearchRequest(Set resourceIds, Scroll scroll, SearchRequest searchRequest, BoolQueryBuilder boolQuery) { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery) .size(1000) .fetchSource(new String[] { "resource_id" }, null); @@ -668,7 +664,7 @@ private void executeSearchRequest(List resourceIds, Scroll scroll, Searc /** * Updates the sharing configuration for an existing resource in the resource sharing index. - * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map, List)} + * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map, Set)} * This method modifies the sharing permissions for a specific resource identified by its * resource ID and source index. * @@ -878,17 +874,17 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId * entities don't exist in the current configuration), the original document is returned unchanged. * @example *
    -     * Map> revokeAccess = new HashMap<>();
    -     * revokeAccess.put(EntityType.USER, Arrays.asList("user1", "user2"));
    -     * revokeAccess.put(EntityType.ROLE, Arrays.asList("role1"));
    +     * Map> revokeAccess = new HashMap<>();
    +     * revokeAccess.put(EntityType.USER, Set.of("user1", "user2"));
    +     * revokeAccess.put(EntityType.ROLE, Set.of("role1"));
          * ResourceSharing updated = revokeAccess("resourceId", "systemIndex", revokeAccess);
          * 
    */ public ResourceSharing revokeAccess( String resourceId, String sourceIdx, - Map> revokeAccess, - List scopes + Map> revokeAccess, + Set scopes ) { if (StringUtils.isBlank(resourceId) || StringUtils.isBlank(sourceIdx) || revokeAccess == null || revokeAccess.isEmpty()) { throw new IllegalArgumentException("resourceId, sourceIdx, and revokeAccess must not be null or empty"); @@ -903,7 +899,7 @@ public ResourceSharing revokeAccess( "painless", """ if (ctx._source.share_with != null) { - List scopesToProcess = params.scopes == null || params.scopes.isEmpty() ? new ArrayList(ctx._source.share_with.keySet()) : params.scopes; + Set scopesToProcess = params.scopes == null || params.scopes.isEmpty() ? ctx._source.share_with.keySet() : params.scopes; for (def scopeName : scopesToProcess) { if (ctx._source.share_with.containsKey(scopeName)) { From 6d7f4c0e25cefef3986e52d712aea3ba7a9c60eb Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 5 Dec 2024 19:48:35 -0500 Subject: [PATCH 31/42] Explicitly casts painless entries to set to avoid duplicates Signed-off-by: Darshit Chanpura --- .../resources/ResourceSharingIndexHandler.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 309daa93d6..3c1dd9771f 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -10,7 +10,10 @@ package org.opensearch.security.resources; import java.io.IOException; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Callable; import com.fasterxml.jackson.core.type.TypeReference; @@ -697,7 +700,9 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc } // Atomic operation - Script updateScript = new Script(ScriptType.INLINE, "painless", """ + // TODO check if this script can be updated to replace magic identifiers (i.e. users, roles and backend_roles) with the ones + // supplied in shareWith + Script updateScript = new Script(ScriptType.INLINE, """ if (ctx._source.share_with == null) { ctx._source.share_with = [:]; } @@ -710,18 +715,20 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc if (existingScope.users == null) { existingScope.users = new HashSet(); } + existingScope.users = new HashSet<>(existingScope.users); existingScope.users.addAll(newScope.users); } - if (newScope.roles != null) { if (existingScope.roles == null) { existingScope.roles = new HashSet(); } + existingScope.roles = new HashSet<>(existingScope.roles); existingScope.roles.addAll(newScope.roles); } if (newScope.backend_roles != null) { if (existingScope.backend_roles == null) { existingScope.backend_roles = new HashSet(); } + existingScope.backend_roles = new HashSet<>(existingScope.backend_roles); existingScope.backend_roles.addAll(newScope.backend_roles); } } else { @@ -738,7 +745,7 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc ctx._source.share_with.put(scopeName, newScopeEntry); } } - """, Collections.singletonMap("shareWith", shareWithMap)); + """, "painless", Collections.singletonMap("shareWith", shareWithMap)); boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, updateScript); return success ? new ResourceSharing(resourceId, sourceIdx, createdBy, shareWith) : null; @@ -899,7 +906,7 @@ public ResourceSharing revokeAccess( "painless", """ if (ctx._source.share_with != null) { - Set scopesToProcess = params.scopes == null || params.scopes.isEmpty() ? ctx._source.share_with.keySet() : params.scopes; + Set scopesToProcess = new HashSet(params.scopes == null || params.scopes.isEmpty() ? ctx._source.share_with.keySet() : params.scopes); for (def scopeName : scopesToProcess) { if (ctx._source.share_with.containsKey(scopeName)) { From 9d4ca1e2c607126bd27f9add0ec90e1b3e269df0 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 5 Dec 2024 19:51:30 -0500 Subject: [PATCH 32/42] Fixes revoke access script Signed-off-by: Darshit Chanpura --- .../security/resources/ResourceSharingIndexHandler.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 3c1dd9771f..b460fdc964 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -918,15 +918,8 @@ public ResourceSharing revokeAccess( if (existingScope.containsKey(entityType) && existingScope[entityType] != null) { existingScope[entityType].removeAll(entitiesToRemove); - if (existingScope[entityType].isEmpty()) { - existingScope.remove(entityType); - } } } - - if (existingScope.isEmpty()) { - ctx._source.share_with.remove(scopeName); - } } } } From b4b22d6ff6357c2d345d40c99a2eb98452d87d17 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 6 Dec 2024 15:11:30 -0500 Subject: [PATCH 33/42] Fixes revoke script to handle duplicates Signed-off-by: Darshit Chanpura --- .../ResourceSharingIndexHandler.java | 121 ++++++++++-------- 1 file changed, 67 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index b460fdc964..cce026b8be 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -10,8 +10,11 @@ package org.opensearch.security.resources; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; @@ -702,50 +705,45 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc // Atomic operation // TODO check if this script can be updated to replace magic identifiers (i.e. users, roles and backend_roles) with the ones // supplied in shareWith - Script updateScript = new Script(ScriptType.INLINE, """ + Script updateScript = new Script(ScriptType.INLINE, "painless", """ if (ctx._source.share_with == null) { ctx._source.share_with = [:]; } + for (def entry : params.shareWith.entrySet()) { def scopeName = entry.getKey(); def newScope = entry.getValue(); - if (ctx._source.share_with.containsKey(scopeName)) { - def existingScope = ctx._source.share_with.get(scopeName); - if (newScope.users != null) { - if (existingScope.users == null) { - existingScope.users = new HashSet(); - } - existingScope.users = new HashSet<>(existingScope.users); - existingScope.users.addAll(newScope.users); - } - if (existingScope.roles == null) { - existingScope.roles = new HashSet(); - } - existingScope.roles = new HashSet<>(existingScope.roles); - existingScope.roles.addAll(newScope.roles); - } - if (newScope.backend_roles != null) { - if (existingScope.backend_roles == null) { - existingScope.backend_roles = new HashSet(); + + if (!ctx._source.share_with.containsKey(scopeName)) { + def newScopeEntry = [:]; + for (def field : newScope.entrySet()) { + if (field.getValue() != null && !field.getValue().isEmpty()) { + newScopeEntry[field.getKey()] = new HashSet(field.getValue()); } - existingScope.backend_roles = new HashSet<>(existingScope.backend_roles); - existingScope.backend_roles.addAll(newScope.backend_roles); } + ctx._source.share_with[scopeName] = newScopeEntry; } else { - def newScopeEntry = [:]; - if (newScope.users != null) { - newScopeEntry.users = new HashSet(newScope.users); - } - if (newScope.roles != null) { - newScopeEntry.roles = new HashSet(newScope.roles); - } - if (newScope.backend_roles != null) { - newScopeEntry.backend_roles = new HashSet(newScope.backend_roles); + def existingScope = ctx._source.share_with[scopeName]; + + for (def field : newScope.entrySet()) { + def fieldName = field.getKey(); + def newValues = field.getValue(); + + if (newValues != null && !newValues.isEmpty()) { + if (!existingScope.containsKey(fieldName)) { + existingScope[fieldName] = new HashSet(); + } + + for (def value : newValues) { + if (!existingScope[fieldName].contains(value)) { + existingScope[fieldName].add(value); + } + } + } } - ctx._source.share_with.put(scopeName, newScopeEntry); } } - """, "painless", Collections.singletonMap("shareWith", shareWithMap)); + """, Collections.singletonMap("shareWith", shareWithMap)); boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, updateScript); return success ? new ResourceSharing(resourceId, sourceIdx, createdBy, shareWith) : null; @@ -900,34 +898,49 @@ public ResourceSharing revokeAccess( LOGGER.debug("Revoking access for resource {} in {} for entities: {} and scopes: {}", resourceId, sourceIdx, revokeAccess, scopes); try { - // Revoke resource access - Script revokeScript = new Script( - ScriptType.INLINE, - "painless", - """ - if (ctx._source.share_with != null) { - Set scopesToProcess = new HashSet(params.scopes == null || params.scopes.isEmpty() ? ctx._source.share_with.keySet() : params.scopes); - - for (def scopeName : scopesToProcess) { - if (ctx._source.share_with.containsKey(scopeName)) { - def existingScope = ctx._source.share_with.get(scopeName); - - for (def entry : params.revokeAccess.entrySet()) { - def entityType = entry.getKey(); - def entitiesToRemove = entry.getValue(); - - if (existingScope.containsKey(entityType) && existingScope[entityType] != null) { - existingScope[entityType].removeAll(entitiesToRemove); - } + Map revoke = new HashMap<>(); + for (Map.Entry> entry : revokeAccess.entrySet()) { + revoke.put(entry.getKey().name().toLowerCase(), new ArrayList<>(entry.getValue())); + } + + List scopesToUse = scopes != null ? new ArrayList<>(scopes) : new ArrayList<>(); + + Script revokeScript = new Script(ScriptType.INLINE, "painless", """ + if (ctx._source.share_with != null) { + Set scopesToProcess = new HashSet(params.scopes.isEmpty() ? ctx._source.share_with.keySet() : params.scopes); + + for (def scopeName : scopesToProcess) { + if (ctx._source.share_with.containsKey(scopeName)) { + def existingScope = ctx._source.share_with.get(scopeName); + + for (def entry : params.revokeAccess.entrySet()) { + def entityType = entry.getKey(); + def entitiesToRemove = entry.getValue(); + + if (existingScope.containsKey(entityType) && existingScope[entityType] != null) { + if (!(existingScope[entityType] instanceof HashSet)) { + existingScope[entityType] = new HashSet(existingScope[entityType]); + } + + existingScope[entityType].removeAll(entitiesToRemove); + + if (existingScope[entityType].isEmpty()) { + existingScope.remove(entityType); } } } + + if (existingScope.isEmpty()) { + ctx._source.share_with.remove(scopeName); + } } - """, - Map.of("revokeAccess", revokeAccess, "scopes", scopes) - ); + } + } + """, Map.of("revokeAccess", revoke, "scopes", scopesToUse)); + // Execute updateByQuery boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, revokeScript); + return success ? fetchDocumentById(sourceIdx, resourceId) : null; } catch (Exception e) { From e87bb80d9e899e0eae8dbb98c6e18a15355226ed Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Sat, 7 Dec 2024 00:47:15 -0500 Subject: [PATCH 34/42] Updates logger statement Signed-off-by: Darshit Chanpura --- .../resources/ResourceAccessHandler.java | 27 ++++++++----------- .../ResourceSharingIndexHandler.java | 3 --- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 7812778981..74d83db8c1 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -96,9 +96,9 @@ public boolean hasPermission(String resourceId, String systemIndexName, String s if (isSharedWithEveryone(document) || isOwnerOfResource(document, user.getName()) - || isSharedWithUser(document, user.getName(), scope) - || isSharedWithGroup(document, userRoles, scope) - || isSharedWithGroup(document, userBackendRoles, scope)) { + || isSharedWithEntity(document, EntityType.USERS, Set.of(user.getName()), scope) + || isSharedWithEntity(document, EntityType.ROLES, userRoles, scope) + || isSharedWithEntity(document, EntityType.BACKEND_ROLES, userBackendRoles, scope)) { LOGGER.info("User {} has {} access to {}", user.getName(), scope, resourceId); return true; } @@ -122,7 +122,7 @@ public ResourceSharing revokeAccess( Set scopes ) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); - LOGGER.info("Revoking access to resource {} created by {} for {}", resourceId, user.getName(), revokeAccess); + LOGGER.info("User {} revoking access to resource {} for {} for scopes {} ", user.getName(), resourceId, revokeAccess, scopes); return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess, scopes); } @@ -169,13 +169,9 @@ private boolean isOwnerOfResource(ResourceSharing document, String userName) { return document.getCreatedBy() != null && document.getCreatedBy().getUser().equals(userName); } - private boolean isSharedWithUser(ResourceSharing document, String userName, String scope) { - return checkSharing(document, "users", userName, scope); - } - - private boolean isSharedWithGroup(ResourceSharing document, Set roles, String scope) { + private boolean isSharedWithEntity(ResourceSharing document, EntityType entityType, Set roles, String scope) { for (String role : roles) { - if (checkSharing(document, "roles", role, scope)) { + if (checkSharing(document, entityType, role, scope)) { return true; } } @@ -187,7 +183,7 @@ private boolean isSharedWithEveryone(ResourceSharing document) { && document.getShareWith().getSharedWithScopes().stream().anyMatch(sharedWithScope -> sharedWithScope.getScope().equals("*")); } - private boolean checkSharing(ResourceSharing document, String sharingType, String identifier, String scope) { + private boolean checkSharing(ResourceSharing document, EntityType entityType, String identifier, String scope) { if (document.getShareWith() == null) { return false; } @@ -200,11 +196,10 @@ private boolean checkSharing(ResourceSharing document, String sharingType, Strin .map(sharedWithScope -> { SharedWithScope.SharedWithPerScope scopePermissions = sharedWithScope.getSharedWithPerScope(); - return switch (sharingType) { - case "users" -> scopePermissions.getUsers().contains(identifier); - case "roles" -> scopePermissions.getRoles().contains(identifier); - case "backend_roles" -> scopePermissions.getBackendRoles().contains(identifier); - default -> false; + return switch (entityType) { + case EntityType.USERS -> scopePermissions.getUsers().contains(identifier); + case EntityType.ROLES -> scopePermissions.getRoles().contains(identifier); + case EntityType.BACKEND_ROLES -> scopePermissions.getBackendRoles().contains(identifier); }; }) .orElse(false); // Return false if no matching scope is found diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index cce026b8be..bfc47a907e 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -405,7 +405,6 @@ public Set fetchDocumentsForAGivenScope(String pluginIndex, Set BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery(); if ("*".equals(scope)) { - // Wildcard behavior: Match any scope dynamically for (String entity : entities) { shouldQuery.should( QueryBuilders.multiMatchQuery(entity, "share_with.*." + entityType + ".keyword") @@ -413,7 +412,6 @@ public Set fetchDocumentsForAGivenScope(String pluginIndex, Set ); } } else { - // Match the specific scope for (String entity : entities) { shouldQuery.should(QueryBuilders.termQuery("share_with." + scope + "." + entityType + ".keyword", entity)); } @@ -938,7 +936,6 @@ public ResourceSharing revokeAccess( } """, Map.of("revokeAccess", revoke, "scopes", scopesToUse)); - // Execute updateByQuery boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, revokeScript); return success ? fetchDocumentById(sourceIdx, resourceId) : null; From 5ad813bcb0d4b7e3516a5fdd29f6340db6e42bf4 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Sat, 7 Dec 2024 18:27:11 -0500 Subject: [PATCH 35/42] Adds validation for resource ownership when granting and revoking access Signed-off-by: Darshit Chanpura --- .../resources/ResourceAccessHandler.java | 8 +- .../ResourceSharingIndexHandler.java | 146 ++++++++++-------- 2 files changed, 85 insertions(+), 69 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 74d83db8c1..d060ce6f38 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -19,7 +19,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.accesscontrol.resources.CreatedBy; import org.opensearch.accesscontrol.resources.EntityType; import org.opensearch.accesscontrol.resources.ResourceSharing; import org.opensearch.accesscontrol.resources.ShareWith; @@ -109,10 +108,9 @@ public boolean hasPermission(String resourceId, String systemIndexName, String s public ResourceSharing shareWith(String resourceId, String systemIndexName, ShareWith shareWith) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); - LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user, shareWith.toString()); + LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user.getName(), shareWith.toString()); - CreatedBy createdBy = new CreatedBy(user.getName()); - return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, createdBy, shareWith); + return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, user.getName(), shareWith); } public ResourceSharing revokeAccess( @@ -124,7 +122,7 @@ public ResourceSharing revokeAccess( final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("User {} revoking access to resource {} for {} for scopes {} ", user.getName(), resourceId, revokeAccess, scopes); - return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess, scopes); + return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess, scopes, user.getName()); } public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index bfc47a907e..6f07f608c9 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -148,7 +148,6 @@ public void createResourceSharingIndexIfAbsent(Callable callable) { public ResourceSharing indexResourceSharing(String resourceId, String resourceIndex, CreatedBy createdBy, ShareWith shareWith) throws IOException { - try { ResourceSharing entry = new ResourceSharing(resourceIndex, resourceId, createdBy, shareWith); @@ -516,7 +515,6 @@ public Set fetchDocumentsByField(String systemIndex, String field, Strin LOGGER.info("Found {} documents in {} where {} = {}", resourceIds.size(), resourceSharingIndex, field, value); return resourceIds; - } catch (Exception e) { LOGGER.error("Failed to fetch documents from {} where {} = {}", resourceSharingIndex, field, value, e); throw new RuntimeException("Failed to fetch documents: " + e.getMessage(), e); @@ -579,7 +577,6 @@ public Set fetchDocumentsByField(String systemIndex, String field, Strin */ public ResourceSharing fetchDocumentById(String pluginIndex, String resourceId) { - // Input validation if (StringUtils.isBlank(pluginIndex) || StringUtils.isBlank(resourceId)) { throw new IllegalArgumentException("systemIndexName and resourceId must not be null or empty"); } @@ -668,12 +665,13 @@ private void executeSearchRequest(Set resourceIds, Scroll scroll, Search /** * Updates the sharing configuration for an existing resource in the resource sharing index. - * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map, Set)} + * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map, Set, String)} * This method modifies the sharing permissions for a specific resource identified by its * resource ID and source index. * * @param resourceId The unique identifier of the resource whose sharing configuration needs to be updated * @param sourceIdx The source index where the original resource is stored + * @param requestUserName The user requesting to share the resource * @param shareWith Updated sharing configuration object containing access control settings: * { * "scope": { @@ -685,7 +683,7 @@ private void executeSearchRequest(Set resourceIds, Scroll scroll, Search * @return ResourceSharing Returns resourceSharing object if the update was successful, null otherwise * @throws RuntimeException if there's an error during the update operation */ - public ResourceSharing updateResourceSharingInfo(String resourceId, String sourceIdx, CreatedBy createdBy, ShareWith shareWith) { + public ResourceSharing updateResourceSharingInfo(String resourceId, String sourceIdx, String requestUserName, ShareWith shareWith) { XContentBuilder builder; Map shareWithMap; try { @@ -700,9 +698,22 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc return null; } + // Check if the user requesting to share is the owner of the resource + // TODO Add a way for users who are not creators to be able to share the resource + ResourceSharing currentSharingInfo = fetchDocumentById(sourceIdx, resourceId); + if (currentSharingInfo != null && !currentSharingInfo.getCreatedBy().getUser().equals(requestUserName)) { + LOGGER.error("User {} is not authorized to share resource {}", requestUserName, resourceId); + return null; + } + + CreatedBy createdBy; + if (currentSharingInfo == null) { + createdBy = new CreatedBy(requestUserName); + } else { + createdBy = currentSharingInfo.getCreatedBy(); + } + // Atomic operation - // TODO check if this script can be updated to replace magic identifiers (i.e. users, roles and backend_roles) with the ones - // supplied in shareWith Script updateScript = new Script(ScriptType.INLINE, "painless", """ if (ctx._source.share_with == null) { ctx._source.share_with = [:]; @@ -792,8 +803,8 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc * "query": { * "bool": { * "must": [ - * { "term": { "source_idx": sourceIdx } }, - * { "term": { "resource_id": resourceId } } + * { "term": { "source_idx.keyword": sourceIdx } }, + * { "term": { "resource_id.keyword": resourceId } } * ] * } * } @@ -810,8 +821,6 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId .setScript(updateScript) .setRefresh(true); - LOGGER.debug("Update By Query Request: {}", ubq.toString()); - BulkByScrollResponse response = client.execute(UpdateByQueryAction.INSTANCE, ubq).actionGet(); if (response.getUpdated() > 0) { @@ -866,6 +875,7 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId * @param revokeAccess A map containing entity types (USER, ROLE, BACKEND_ROLE) and their corresponding * values to be removed from the sharing configuration * @param scopes A list of scopes to revoke access from. If null or empty, access is revoked from all scopes + * @param requestUserName The user trying to revoke the accesses * @return The updated ResourceSharing object after revoking access, or null if the document doesn't exist * @throws IllegalArgumentException if resourceId, sourceIdx is null/empty, or if revokeAccess is null/empty * @throws RuntimeException if the update operation fails or encounters an error @@ -887,12 +897,20 @@ public ResourceSharing revokeAccess( String resourceId, String sourceIdx, Map> revokeAccess, - Set scopes + Set scopes, + String requestUserName ) { if (StringUtils.isBlank(resourceId) || StringUtils.isBlank(sourceIdx) || revokeAccess == null || revokeAccess.isEmpty()) { throw new IllegalArgumentException("resourceId, sourceIdx, and revokeAccess must not be null or empty"); } + // TODO Check if access can be revoked by non-creator + ResourceSharing currentSharingInfo = fetchDocumentById(sourceIdx, resourceId); + if (currentSharingInfo != null && !currentSharingInfo.getCreatedBy().getUser().equals(requestUserName)) { + LOGGER.error("User {} is not authorized to revoke access to resource {}", requestUserName, resourceId); + return null; + } + LOGGER.debug("Revoking access for resource {} in {} for entities: {} and scopes: {}", resourceId, sourceIdx, revokeAccess, scopes); try { @@ -1019,58 +1037,58 @@ public boolean deleteResourceSharingRecord(String resourceId, String sourceIdx) } /** - * Deletes all resource sharing records that were created by a specific user. - * This method performs a delete-by-query operation to remove all documents where - * the created_by.user field matches the specified username. - * - *

    The method executes the following steps: - *

      - *
    1. Validates the input username parameter
    2. - *
    3. Creates a delete-by-query request with term query matching
    4. - *
    5. Executes the delete operation with immediate refresh
    6. - *
    7. Returns the operation status based on number of deleted documents
    8. - *
    - * - *

    Example query structure: - *

    -        * {
    -        *   "query": {
    -        *     "term": {
    -        *       "created_by.user": "username"
    -        *     }
    -        *   }
    -        * }
    -        * 
    - * - * @param name The username to match against the created_by.user field - * @return boolean indicating whether the deletion was successful: - *
      - *
    • true - if one or more documents were deleted
    • - *
    • false - if no documents were found
    • - *
    • false - if the operation failed due to an error
    • - *
    - * - * @throws IllegalArgumentException if name is null or empty - * - * - * @implNote Implementation details: - *
      - *
    • Uses DeleteByQueryRequest for efficient bulk deletion
    • - *
    • Sets refresh=true for immediate consistency
    • - *
    • Uses term query for exact username matching
    • - *
    • Implements comprehensive error handling and logging
    • - *
    - * - * Example usage: - *
    -        * boolean success = deleteAllRecordsForUser("john.doe");
    -        * if (success) {
    -        *     // Records were successfully deleted
    -        * } else {
    -        *     // No matching records found or operation failed
    -        * }
    -        * 
    - */ + * Deletes all resource sharing records that were created by a specific user. + * This method performs a delete-by-query operation to remove all documents where + * the created_by.user field matches the specified username. + * + *

    The method executes the following steps: + *

      + *
    1. Validates the input username parameter
    2. + *
    3. Creates a delete-by-query request with term query matching
    4. + *
    5. Executes the delete operation with immediate refresh
    6. + *
    7. Returns the operation status based on number of deleted documents
    8. + *
    + * + *

    Example query structure: + *

    +    * {
    +    *   "query": {
    +    *     "term": {
    +    *       "created_by.user": "username"
    +    *     }
    +    *   }
    +    * }
    +    * 
    + * + * @param name The username to match against the created_by.user field + * @return boolean indicating whether the deletion was successful: + *
      + *
    • true - if one or more documents were deleted
    • + *
    • false - if no documents were found
    • + *
    • false - if the operation failed due to an error
    • + *
    + * + * @throws IllegalArgumentException if name is null or empty + * + * + * @implNote Implementation details: + *
      + *
    • Uses DeleteByQueryRequest for efficient bulk deletion
    • + *
    • Sets refresh=true for immediate consistency
    • + *
    • Uses term query for exact username matching
    • + *
    • Implements comprehensive error handling and logging
    • + *
    + * + * Example usage: + *
    +    * boolean success = deleteAllRecordsForUser("john.doe");
    +    * if (success) {
    +    *     // Records were successfully deleted
    +    * } else {
    +    *     // No matching records found or operation failed
    +    * }
    +    * 
    + */ public boolean deleteAllRecordsForUser(String name) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("Username must not be null or empty"); From c08a99273ccf3cd88d26f3421ba24c56599277c8 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Mon, 9 Dec 2024 21:45:06 -0500 Subject: [PATCH 36/42] Adds super-admin bypass Signed-off-by: Darshit Chanpura --- .../resources/ResourceAccessHandler.java | 16 +++++++++++++-- .../ResourceSharingIndexHandler.java | 20 +++++++++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index d060ce6f38..721692c85a 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -82,8 +82,14 @@ public Set listAccessibleResourcesInPlugin(String pluginIndex) { public boolean hasPermission(String resourceId, String systemIndexName, String scope) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); + LOGGER.info("Checking if {} has {} permission to resource {}", user.getName(), scope, resourceId); + // check if user is admin, if yes the user has permission + if (adminDNs.isAdmin(user)) { + return true; + } + Set userRoles = user.getSecurityRoles(); Set userBackendRoles = user.getRoles(); @@ -110,7 +116,10 @@ public ResourceSharing shareWith(String resourceId, String systemIndexName, Shar final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user.getName(), shareWith.toString()); - return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, user.getName(), shareWith); + // check if user is admin, if yes the user has permission + boolean isAdmin = adminDNs.isAdmin(user); + + return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, user.getName(), shareWith, isAdmin); } public ResourceSharing revokeAccess( @@ -122,7 +131,10 @@ public ResourceSharing revokeAccess( final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("User {} revoking access to resource {} for {} for scopes {} ", user.getName(), resourceId, revokeAccess, scopes); - return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess, scopes, user.getName()); + // check if user is admin, if yes the user has permission + boolean isAdmin = adminDNs.isAdmin(user); + + return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess, scopes, user.getName(), isAdmin); } public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 6f07f608c9..a4ef90e492 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -145,7 +145,6 @@ public void createResourceSharingIndexIfAbsent(Callable callable) { * @return ResourceSharing Returns resourceSharing object if the operation was successful, null otherwise * @throws IOException if there are issues with index operations or JSON processing */ - public ResourceSharing indexResourceSharing(String resourceId, String resourceIndex, CreatedBy createdBy, ShareWith shareWith) throws IOException { try { @@ -665,7 +664,7 @@ private void executeSearchRequest(Set resourceIds, Scroll scroll, Search /** * Updates the sharing configuration for an existing resource in the resource sharing index. - * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map, Set, String)} + * NOTE: This method only grants new access. To remove access use {@link #revokeAccess(String, String, Map, Set, String, boolean)} * This method modifies the sharing permissions for a specific resource identified by its * resource ID and source index. * @@ -680,10 +679,17 @@ private void executeSearchRequest(Set resourceIds, Scroll scroll, Search * "backend_roles": ["backend_role1"] * } * } + * @param isAdmin Boolean indicating whether the user requesting to share is an admin or not * @return ResourceSharing Returns resourceSharing object if the update was successful, null otherwise * @throws RuntimeException if there's an error during the update operation */ - public ResourceSharing updateResourceSharingInfo(String resourceId, String sourceIdx, String requestUserName, ShareWith shareWith) { + public ResourceSharing updateResourceSharingInfo( + String resourceId, + String sourceIdx, + String requestUserName, + ShareWith shareWith, + boolean isAdmin + ) { XContentBuilder builder; Map shareWithMap; try { @@ -701,7 +707,7 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc // Check if the user requesting to share is the owner of the resource // TODO Add a way for users who are not creators to be able to share the resource ResourceSharing currentSharingInfo = fetchDocumentById(sourceIdx, resourceId); - if (currentSharingInfo != null && !currentSharingInfo.getCreatedBy().getUser().equals(requestUserName)) { + if (!isAdmin && currentSharingInfo != null && !currentSharingInfo.getCreatedBy().getUser().equals(requestUserName)) { LOGGER.error("User {} is not authorized to share resource {}", requestUserName, resourceId); return null; } @@ -876,6 +882,7 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId * values to be removed from the sharing configuration * @param scopes A list of scopes to revoke access from. If null or empty, access is revoked from all scopes * @param requestUserName The user trying to revoke the accesses + * @param isAdmin Boolean indicating whether the user is an admin or not * @return The updated ResourceSharing object after revoking access, or null if the document doesn't exist * @throws IllegalArgumentException if resourceId, sourceIdx is null/empty, or if revokeAccess is null/empty * @throws RuntimeException if the update operation fails or encounters an error @@ -898,7 +905,8 @@ public ResourceSharing revokeAccess( String sourceIdx, Map> revokeAccess, Set scopes, - String requestUserName + String requestUserName, + boolean isAdmin ) { if (StringUtils.isBlank(resourceId) || StringUtils.isBlank(sourceIdx) || revokeAccess == null || revokeAccess.isEmpty()) { throw new IllegalArgumentException("resourceId, sourceIdx, and revokeAccess must not be null or empty"); @@ -906,7 +914,7 @@ public ResourceSharing revokeAccess( // TODO Check if access can be revoked by non-creator ResourceSharing currentSharingInfo = fetchDocumentById(sourceIdx, resourceId); - if (currentSharingInfo != null && !currentSharingInfo.getCreatedBy().getUser().equals(requestUserName)) { + if (!isAdmin && currentSharingInfo != null && !currentSharingInfo.getCreatedBy().getUser().equals(requestUserName)) { LOGGER.error("User {} is not authorized to revoke access to resource {}", requestUserName, resourceId); return null; } From 8e3d41c2f24df3155cf02c474c2c2955f3ba70d6 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 11 Dec 2024 15:18:02 -0500 Subject: [PATCH 37/42] Updates method names and adds missing java doc Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 4 +- .../resources/ResourceAccessHandler.java | 155 ++++++++++++++---- .../ResourceSharingIndexHandler.java | 4 + .../ResourceSharingIndexListener.java | 21 +++ 4 files changed, 151 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 0e3e612e20..e0293a7abf 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -2208,8 +2208,8 @@ private void tryAddSecurityProvider() { } @Override - public Set listAccessibleResourcesInPlugin(String systemIndexName) { - return this.resourceAccessHandler.listAccessibleResourcesInPlugin(systemIndexName); + public Set getAccessibleResourcesForCurrentUser(String systemIndexName) { + return this.resourceAccessHandler.getAccessibleResourcesForCurrentUser(systemIndexName); } @Override diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 721692c85a..5d5b39b697 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -29,6 +29,11 @@ import org.opensearch.security.user.User; import org.opensearch.threadpool.ThreadPool; +/** + * This class handles resource access permissions for users and roles. + * It provides methods to check if a user has permission to access a resource + * based on the resource sharing configuration. + */ public class ResourceAccessHandler { private static final Logger LOGGER = LogManager.getLogger(ResourceAccessHandler.class); @@ -47,40 +52,54 @@ public ResourceAccessHandler( this.adminDNs = adminDns; } - public Set listAccessibleResourcesInPlugin(String pluginIndex) { + /** + * Returns a set of accessible resources for the current user within the specified resource index. + * + * @param resourceIndex The resource index to check for accessible resources. + * @return A set of accessible resource IDs. + */ + public Set getAccessibleResourcesForCurrentUser(String resourceIndex) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); if (user == null) { LOGGER.info("Unable to fetch user details "); return Collections.emptySet(); } - LOGGER.info("Listing accessible resource within a system index {} for : {}", pluginIndex, user.getName()); + LOGGER.info("Listing accessible resources within a resource index {} for : {}", resourceIndex, user.getName()); // check if user is admin, if yes all resources should be accessible if (adminDNs.isAdmin(user)) { - return loadAllResources(pluginIndex); + return loadAllResources(resourceIndex); } Set result = new HashSet<>(); // 0. Own resources - result.addAll(loadOwnResources(pluginIndex, user.getName())); + result.addAll(loadOwnResources(resourceIndex, user.getName())); // 1. By username - result.addAll(loadSharedWithResources(pluginIndex, Set.of(user.getName()), EntityType.USERS.toString())); + result.addAll(loadSharedWithResources(resourceIndex, Set.of(user.getName()), EntityType.USERS.toString())); // 2. By roles Set roles = user.getSecurityRoles(); - result.addAll(loadSharedWithResources(pluginIndex, roles, EntityType.ROLES.toString())); + result.addAll(loadSharedWithResources(resourceIndex, roles, EntityType.ROLES.toString())); // 3. By backend_roles Set backendRoles = user.getRoles(); - result.addAll(loadSharedWithResources(pluginIndex, backendRoles, EntityType.BACKEND_ROLES.toString())); + result.addAll(loadSharedWithResources(resourceIndex, backendRoles, EntityType.BACKEND_ROLES.toString())); return result; } - public boolean hasPermission(String resourceId, String systemIndexName, String scope) { + /** + * Checks whether current user has given permission (scope) to access given resource. + * + * @param resourceId The resource ID to check access for. + * @param resourceIndex The resource index containing the resource. + * @param scope The permission scope to check. + * @return True if the user has the specified permission, false otherwise. + */ + public boolean hasPermission(String resourceId, String resourceIndex, String scope) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Checking if {} has {} permission to resource {}", user.getName(), scope, resourceId); @@ -93,9 +112,9 @@ public boolean hasPermission(String resourceId, String systemIndexName, String s Set userRoles = user.getSecurityRoles(); Set userBackendRoles = user.getRoles(); - ResourceSharing document = this.resourceSharingIndexHandler.fetchDocumentById(systemIndexName, resourceId); + ResourceSharing document = this.resourceSharingIndexHandler.fetchDocumentById(resourceIndex, resourceId); if (document == null) { - LOGGER.warn("Resource {} not found in index {}", resourceId, systemIndexName); + LOGGER.warn("Resource {} not found in index {}", resourceId, resourceIndex); return false; // If the document doesn't exist, no permissions can be granted } @@ -112,19 +131,34 @@ public boolean hasPermission(String resourceId, String systemIndexName, String s return false; } - public ResourceSharing shareWith(String resourceId, String systemIndexName, ShareWith shareWith) { + /** + * Shares a resource with the specified users, roles, and backend roles. + * @param resourceId The resource ID to share. + * @param resourceIndex The index where resource is store + * @param shareWith The users, roles, and backend roles as well as scope to share the resource with. + * @return The updated ResourceSharing document. + */ + public ResourceSharing shareWith(String resourceId, String resourceIndex, ShareWith shareWith) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user.getName(), shareWith.toString()); // check if user is admin, if yes the user has permission boolean isAdmin = adminDNs.isAdmin(user); - return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, systemIndexName, user.getName(), shareWith, isAdmin); + return this.resourceSharingIndexHandler.updateResourceSharingInfo(resourceId, resourceIndex, user.getName(), shareWith, isAdmin); } + /** + * Revokes access to a resource for the specified users, roles, and backend roles. + * @param resourceId The resource ID to revoke access from. + * @param resourceIndex The index where resource is store + * @param revokeAccess The users, roles, and backend roles to revoke access for. + * @param scopes The permission scopes to revoke access for. + * @return The updated ResourceSharing document. + */ public ResourceSharing revokeAccess( String resourceId, - String systemIndexName, + String resourceIndex, Map> revokeAccess, Set scopes ) { @@ -134,25 +168,35 @@ public ResourceSharing revokeAccess( // check if user is admin, if yes the user has permission boolean isAdmin = adminDNs.isAdmin(user); - return this.resourceSharingIndexHandler.revokeAccess(resourceId, systemIndexName, revokeAccess, scopes, user.getName(), isAdmin); + return this.resourceSharingIndexHandler.revokeAccess(resourceId, resourceIndex, revokeAccess, scopes, user.getName(), isAdmin); } - public boolean deleteResourceSharingRecord(String resourceId, String systemIndexName) { + /** + * Deletes a resource sharing record by its ID and the resource index it belongs to. + * @param resourceId The resource ID to delete. + * @param resourceIndex The resource index containing the resource. + * @return True if the record was successfully deleted, false otherwise. + */ + public boolean deleteResourceSharingRecord(String resourceId, String resourceIndex) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); - LOGGER.info("Deleting resource sharing record for resource {} in {} created by {}", resourceId, systemIndexName, user.getName()); + LOGGER.info("Deleting resource sharing record for resource {} in {} created by {}", resourceId, resourceIndex, user.getName()); - ResourceSharing document = this.resourceSharingIndexHandler.fetchDocumentById(systemIndexName, resourceId); + ResourceSharing document = this.resourceSharingIndexHandler.fetchDocumentById(resourceIndex, resourceId); if (document == null) { - LOGGER.info("Document {} does not exist in index {}", resourceId, systemIndexName); + LOGGER.info("Document {} does not exist in index {}", resourceId, resourceIndex); return false; } if (!(adminDNs.isAdmin(user) || isOwnerOfResource(document, user.getName()))) { LOGGER.info("User {} does not have access to delete the record {} ", user.getName(), resourceId); return false; } - return this.resourceSharingIndexHandler.deleteResourceSharingRecord(resourceId, systemIndexName); + return this.resourceSharingIndexHandler.deleteResourceSharingRecord(resourceId, resourceIndex); } + /** + * Deletes all resource sharing records for the current user. + * @return True if all records were successfully deleted, false otherwise. + */ public boolean deleteAllResourceSharingRecordsForCurrentUser() { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Deleting all resource sharing records for resource {}", user.getName()); @@ -160,39 +204,88 @@ public boolean deleteAllResourceSharingRecordsForCurrentUser() { return this.resourceSharingIndexHandler.deleteAllRecordsForUser(user.getName()); } - // Helper methods - - private Set loadAllResources(String systemIndex) { - return this.resourceSharingIndexHandler.fetchAllDocuments(systemIndex); + /** + * Loads all resources within the specified resource index. + * + * @param resourceIndex The resource index to load resources from. + * @return A set of resource IDs. + */ + private Set loadAllResources(String resourceIndex) { + return this.resourceSharingIndexHandler.fetchAllDocuments(resourceIndex); } - private Set loadOwnResources(String systemIndex, String username) { - // TODO check if this magic variable can be replaced - return this.resourceSharingIndexHandler.fetchDocumentsByField(systemIndex, "created_by.user", username); + /** + * Loads resources owned by the specified user within the given resource index. + * + * @param resourceIndex The resource index to load resources from. + * @param userName The username of the owner. + * @return A set of resource IDs owned by the user. + */ + private Set loadOwnResources(String resourceIndex, String userName) { + return this.resourceSharingIndexHandler.fetchDocumentsByField(resourceIndex, "created_by.user", userName); } - private Set loadSharedWithResources(String systemIndex, Set entities, String shareWithType) { - return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(systemIndex, entities, shareWithType); + /** + * Loads resources shared with the specified entities within the given resource index. + * + * @param resourceIndex The resource index to load resources from. + * @param entities The set of entities to check for shared resources. + * @param entityType The type of entity (e.g., users, roles, backend_roles). + * @return A set of resource IDs shared with the specified entities. + */ + private Set loadSharedWithResources(String resourceIndex, Set entities, String entityType) { + return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(resourceIndex, entities, entityType); } + /** + * Checks if the given resource is owned by the specified user. + * + * @param document The ResourceSharing document to check. + * @param userName The username to check ownership against. + * @return True if the resource is owned by the user, false otherwise. + */ private boolean isOwnerOfResource(ResourceSharing document, String userName) { return document.getCreatedBy() != null && document.getCreatedBy().getUser().equals(userName); } - private boolean isSharedWithEntity(ResourceSharing document, EntityType entityType, Set roles, String scope) { - for (String role : roles) { - if (checkSharing(document, entityType, role, scope)) { + /** + * Checks if the given resource is shared with the specified entities and scope. + * + * @param document The ResourceSharing document to check. + * @param entityType The type of entity (e.g., users, roles, backend_roles). + * @param entities The set of entities to check for sharing. + * @param scope The permission scope to check. + * @return True if the resource is shared with the entities and scope, false otherwise. + */ + private boolean isSharedWithEntity(ResourceSharing document, EntityType entityType, Set entities, String scope) { + for (String entity : entities) { + if (checkSharing(document, entityType, entity, scope)) { return true; } } return false; } + /** + * Checks if the given resource is shared with everyone. + * + * @param document The ResourceSharing document to check. + * @return True if the resource is shared with everyone, false otherwise. + */ private boolean isSharedWithEveryone(ResourceSharing document) { return document.getShareWith() != null && document.getShareWith().getSharedWithScopes().stream().anyMatch(sharedWithScope -> sharedWithScope.getScope().equals("*")); } + /** + * Checks if the given resource is shared with the specified entity and scope. + * + * @param document The ResourceSharing document to check. + * @param entityType The type of entity (e.g., users, roles, backend_roles). + * @param identifier The identifier of the entity to check for sharing. + * @param scope The permission scope to check. + * @return True if the resource is shared with the entity and scope, false otherwise. + */ private boolean checkSharing(ResourceSharing document, EntityType entityType, String identifier, String scope) { if (document.getShareWith() == null) { return false; diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index a4ef90e492..ec75515985 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -67,6 +67,10 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +/** + * This class handles the creation and management of the resource sharing index. + * It provides methods to create the index, index resource sharing entries along with updates and deletion, retrieve shared resources. + */ public class ResourceSharingIndexHandler { private static final Logger LOGGER = LogManager.getLogger(ResourceSharingIndexHandler.class); diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java index d7b149a2fb..2fad56fc1b 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexListener.java @@ -46,6 +46,13 @@ public static ResourceSharingIndexListener getInstance() { } + /** + * Initializes the ResourceSharingIndexListener with the provided ThreadPool and Client. + * This method is called during the plugin's initialization process. + * + * @param threadPool The ThreadPool instance to be used for executing operations. + * @param client The Client instance to be used for interacting with OpenSearch. + */ public void initialize(ThreadPool threadPool, Client client) { if (initialized) { @@ -66,6 +73,13 @@ public boolean isInitialized() { return initialized; } + /** + * This method is called after an index operation is performed. + * It creates a resource sharing entry in the dedicated resource sharing index. + * @param shardId The shard ID of the index where the operation was performed. + * @param index The index where the operation was performed. + * @param result The result of the index operation. + */ @Override public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult result) { @@ -89,6 +103,13 @@ public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult re } } + /** + * This method is called after a delete operation is performed. + * It deletes the corresponding resource sharing entry from the dedicated resource sharing index. + * @param shardId The shard ID of the index where the delete operation was performed. + * @param delete The delete operation that was performed. + * @param result The result of the delete operation. + */ @Override public void postDelete(ShardId shardId, Engine.Delete delete, Engine.DeleteResult result) { From cabbcd60d557103c263f56412c79600a4773a423 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 12 Dec 2024 16:48:28 -0500 Subject: [PATCH 38/42] Updates methods to return actual resources instead of resource ids Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 4 +- .../resources/ResourceAccessHandler.java | 26 +++---- .../ResourceSharingIndexHandler.java | 74 ++++++++++++++----- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index e0293a7abf..118b5bc88b 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -2208,8 +2208,8 @@ private void tryAddSecurityProvider() { } @Override - public Set getAccessibleResourcesForCurrentUser(String systemIndexName) { - return this.resourceAccessHandler.getAccessibleResourcesForCurrentUser(systemIndexName); + public Set getAccessibleResourcesForCurrentUser(String systemIndexName, Class clazz) { + return this.resourceAccessHandler.getAccessibleResourcesForCurrentUser(systemIndexName, clazz); } @Override diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 5d5b39b697..6837f88cbf 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -58,7 +58,7 @@ public ResourceAccessHandler( * @param resourceIndex The resource index to check for accessible resources. * @return A set of accessible resource IDs. */ - public Set getAccessibleResourcesForCurrentUser(String resourceIndex) { + public Set getAccessibleResourcesForCurrentUser(String resourceIndex, Class clazz) { final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); if (user == null) { LOGGER.info("Unable to fetch user details "); @@ -69,24 +69,24 @@ public Set getAccessibleResourcesForCurrentUser(String resourceIndex) { // check if user is admin, if yes all resources should be accessible if (adminDNs.isAdmin(user)) { - return loadAllResources(resourceIndex); + return loadAllResources(resourceIndex, clazz); } - Set result = new HashSet<>(); + Set result = new HashSet<>(); // 0. Own resources - result.addAll(loadOwnResources(resourceIndex, user.getName())); + result.addAll(loadOwnResources(resourceIndex, user.getName(), clazz)); // 1. By username - result.addAll(loadSharedWithResources(resourceIndex, Set.of(user.getName()), EntityType.USERS.toString())); + result.addAll(loadSharedWithResources(resourceIndex, Set.of(user.getName()), EntityType.USERS.toString(), clazz)); // 2. By roles Set roles = user.getSecurityRoles(); - result.addAll(loadSharedWithResources(resourceIndex, roles, EntityType.ROLES.toString())); + result.addAll(loadSharedWithResources(resourceIndex, roles, EntityType.ROLES.toString(), clazz)); // 3. By backend_roles Set backendRoles = user.getRoles(); - result.addAll(loadSharedWithResources(resourceIndex, backendRoles, EntityType.BACKEND_ROLES.toString())); + result.addAll(loadSharedWithResources(resourceIndex, backendRoles, EntityType.BACKEND_ROLES.toString(), clazz)); return result; } @@ -210,8 +210,8 @@ public boolean deleteAllResourceSharingRecordsForCurrentUser() { * @param resourceIndex The resource index to load resources from. * @return A set of resource IDs. */ - private Set loadAllResources(String resourceIndex) { - return this.resourceSharingIndexHandler.fetchAllDocuments(resourceIndex); + private Set loadAllResources(String resourceIndex, Class clazz) { + return this.resourceSharingIndexHandler.fetchAllDocuments(resourceIndex, clazz); } /** @@ -221,8 +221,8 @@ private Set loadAllResources(String resourceIndex) { * @param userName The username of the owner. * @return A set of resource IDs owned by the user. */ - private Set loadOwnResources(String resourceIndex, String userName) { - return this.resourceSharingIndexHandler.fetchDocumentsByField(resourceIndex, "created_by.user", userName); + private Set loadOwnResources(String resourceIndex, String userName, Class clazz) { + return this.resourceSharingIndexHandler.fetchDocumentsByField(resourceIndex, "created_by.user", userName, clazz); } /** @@ -233,8 +233,8 @@ private Set loadOwnResources(String resourceIndex, String userName) { * @param entityType The type of entity (e.g., users, roles, backend_roles). * @return A set of resource IDs shared with the specified entities. */ - private Set loadSharedWithResources(String resourceIndex, Set entities, String entityType) { - return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(resourceIndex, entities, entityType); + private Set loadSharedWithResources(String resourceIndex, Set entities, String entityType, Class clazz) { + return this.resourceSharingIndexHandler.fetchDocumentsForAllScopes(resourceIndex, entities, entityType, clazz); } /** diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index ec75515985..e53c1f1a56 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -30,6 +30,9 @@ import org.opensearch.accesscontrol.resources.ShareWith; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; +import org.opensearch.action.get.MultiGetItemResponse; +import org.opensearch.action.get.MultiGetRequest; +import org.opensearch.action.get.MultiGetResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.search.ClearScrollRequest; @@ -214,7 +217,7 @@ public ResourceSharing indexResourceSharing(String resourceId, String resourceIn *
  • Returns an empty list instead of throwing exceptions
  • * */ - public Set fetchAllDocuments(String pluginIndex) { + public Set fetchAllDocuments(String pluginIndex, Class clazz) { LOGGER.debug("Fetching all documents from {} where source_idx = {}", resourceSharingIndex, pluginIndex); try { @@ -242,7 +245,7 @@ public Set fetchAllDocuments(String pluginIndex) { LOGGER.debug("Found {} documents in {} for source_idx: {}", resourceIds.size(), resourceSharingIndex, pluginIndex); - return resourceIds; + return getResourcesFromIds(resourceIds, pluginIndex, clazz); } catch (Exception e) { LOGGER.error("Failed to fetch documents from {} for source_idx: {}", resourceSharingIndex, pluginIndex, e); @@ -316,9 +319,9 @@ public Set fetchAllDocuments(String pluginIndex) { * */ - public Set fetchDocumentsForAllScopes(String pluginIndex, Set entities, String entityType) { + public Set fetchDocumentsForAllScopes(String pluginIndex, Set entities, String entityType, Class clazz) { // "*" must match all scopes - return fetchDocumentsForAGivenScope(pluginIndex, entities, entityType, "*"); + return fetchDocumentsForAGivenScope(pluginIndex, entities, entityType, "*", clazz); } /** @@ -387,7 +390,13 @@ public Set fetchDocumentsForAllScopes(String pluginIndex, Set en *
  • Properly cleans up scroll context after use
  • * */ - public Set fetchDocumentsForAGivenScope(String pluginIndex, Set entities, String entityType, String scope) { + public Set fetchDocumentsForAGivenScope( + String pluginIndex, + Set entities, + String entityType, + String scope, + Class clazz + ) { LOGGER.debug( "Fetching documents from index: {}, where share_with.{}.{} contains any of {}", pluginIndex, @@ -426,11 +435,11 @@ public Set fetchDocumentsForAGivenScope(String pluginIndex, Set LOGGER.debug("Found {} documents matching the criteria in {}", resourceIds.size(), resourceSharingIndex); - return resourceIds; + return getResourcesFromIds(resourceIds, pluginIndex, clazz); } catch (Exception e) { LOGGER.error( - "Failed to fetch documents from {} for criteria - systemIndex: {}, scope: {}, entityType: {}, entities: {}", + "Failed to fetch documents from {} for criteria - pluginIndex: {}, scope: {}, entityType: {}, entities: {}", resourceSharingIndex, pluginIndex, scope, @@ -472,7 +481,7 @@ public Set fetchDocumentsForAGivenScope(String pluginIndex, Set * } * * - * @param systemIndex The source index to match against the source_idx field + * @param pluginIndex The source index to match against the source_idx field * @param field The field name to search in. Must be a valid field in the index mapping * @param value The value to match for the specified field. Performs exact term matching * @return Set List of resource IDs that match the criteria. Returns an empty list @@ -495,12 +504,12 @@ public Set fetchDocumentsForAGivenScope(String pluginIndex, Set * Set resources = fetchDocumentsByField("myIndex", "status", "active"); * */ - public Set fetchDocumentsByField(String systemIndex, String field, String value) { - if (StringUtils.isBlank(systemIndex) || StringUtils.isBlank(field) || StringUtils.isBlank(value)) { - throw new IllegalArgumentException("systemIndex, field, and value must not be null or empty"); + public Set fetchDocumentsByField(String pluginIndex, String field, String value, Class clazz) { + if (StringUtils.isBlank(pluginIndex) || StringUtils.isBlank(field) || StringUtils.isBlank(value)) { + throw new IllegalArgumentException("pluginIndex, field, and value must not be null or empty"); } - LOGGER.debug("Fetching documents from index: {}, where {} = {}", systemIndex, field, value); + LOGGER.debug("Fetching documents from index: {}, where {} = {}", pluginIndex, field, value); Set resourceIds = new HashSet<>(); final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); @@ -510,14 +519,14 @@ public Set fetchDocumentsByField(String systemIndex, String field, Strin searchRequest.scroll(scroll); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() - .must(QueryBuilders.termQuery("source_idx.keyword", systemIndex)) + .must(QueryBuilders.termQuery("source_idx.keyword", pluginIndex)) .must(QueryBuilders.termQuery(field + ".keyword", value)); executeSearchRequest(resourceIds, scroll, searchRequest, boolQuery); LOGGER.info("Found {} documents in {} where {} = {}", resourceIds.size(), resourceSharingIndex, field, value); - return resourceIds; + return getResourcesFromIds(resourceIds, pluginIndex, clazz); } catch (Exception e) { LOGGER.error("Failed to fetch documents from {} where {} = {}", resourceSharingIndex, field, value, e); throw new RuntimeException("Failed to fetch documents: " + e.getMessage(), e); @@ -557,7 +566,7 @@ public Set fetchDocumentsByField(String systemIndex, String field, Strin * @return ResourceSharing object if a matching document is found, null if no document * matches the criteria * - * @throws IllegalArgumentException if systemIndexName or resourceId is null or empty + * @throws IllegalArgumentException if pluginIndexName or resourceId is null or empty * @throws RuntimeException if the search operation fails or parsing errors occur, * wrapping the underlying exception * @@ -581,7 +590,7 @@ public Set fetchDocumentsByField(String systemIndex, String field, Strin public ResourceSharing fetchDocumentById(String pluginIndex, String resourceId) { if (StringUtils.isBlank(pluginIndex) || StringUtils.isBlank(resourceId)) { - throw new IllegalArgumentException("systemIndexName and resourceId must not be null or empty"); + throw new IllegalArgumentException("pluginIndexName and resourceId must not be null or empty"); } LOGGER.debug("Fetching document from index: {}, with resourceId: {}", pluginIndex, resourceId); @@ -901,7 +910,7 @@ private boolean updateByQueryResourceSharing(String sourceIdx, String resourceId * Map> revokeAccess = new HashMap<>(); * revokeAccess.put(EntityType.USER, Set.of("user1", "user2")); * revokeAccess.put(EntityType.ROLE, Set.of("role1")); - * ResourceSharing updated = revokeAccess("resourceId", "systemIndex", revokeAccess); + * ResourceSharing updated = revokeAccess("resourceId", "pluginIndex", revokeAccess); * */ public ResourceSharing revokeAccess( @@ -1131,4 +1140,35 @@ public boolean deleteAllRecordsForUser(String name) { } } + /** + * Fetches all documents from the specified resource index and deserializes them into the specified class. + * + * @param resourceIndex The resource index to fetch documents from. + * @param clazz The class to deserialize the documents into. + * @return A set of deserialized documents. + */ + private Set getResourcesFromIds(Set resourceIds, String resourceIndex, Class clazz) { + + Set result = new HashSet<>(); + try { + MultiGetRequest request = new MultiGetRequest(); + for (String id : resourceIds) { + request.add(new MultiGetRequest.Item(resourceIndex, id)); + } + + MultiGetResponse response = client.multiGet(request).actionGet(); + + for (MultiGetItemResponse itemResponse : response.getResponses()) { + if (!itemResponse.isFailed() && itemResponse.getResponse().isExists()) { + String sourceAsString = itemResponse.getResponse().getSourceAsString(); + T resource = DefaultObjectMapper.readValue(sourceAsString, clazz); + result.add(resource); + } + } + } catch (Exception e) { + LOGGER.error("Failed to fetch resources with ids {} from index {}", resourceIds, resourceIndex, e); + } + + return result; + } } From dc964aca7eaa375377b04c6d7354557a31202264 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 12 Dec 2024 20:33:27 -0500 Subject: [PATCH 39/42] Stash context to fetch resources from a system index Signed-off-by: Darshit Chanpura --- .../security/resources/ResourceSharingIndexHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index e53c1f1a56..92ef31402a 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -1150,7 +1150,8 @@ public boolean deleteAllRecordsForUser(String name) { private Set getResourcesFromIds(Set resourceIds, String resourceIndex, Class clazz) { Set result = new HashSet<>(); - try { + // stashing Context to avoid permission issues in-case resourceIndex is a system index + try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) { MultiGetRequest request = new MultiGetRequest(); for (String id : resourceIds) { request.add(new MultiGetRequest.Item(resourceIndex, id)); From cc973c6864be903f5acba05c28834cce42d6a248 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 12 Dec 2024 20:55:43 -0500 Subject: [PATCH 40/42] Optimize call to fetch resources Signed-off-by: Darshit Chanpura --- .../security/resources/ResourceSharingIndexHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index 92ef31402a..cf622b46a1 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -245,7 +245,7 @@ public Set fetchAllDocuments(String pluginIndex, Class clazz) { LOGGER.debug("Found {} documents in {} for source_idx: {}", resourceIds.size(), resourceSharingIndex, pluginIndex); - return getResourcesFromIds(resourceIds, pluginIndex, clazz); + return resourceIds.isEmpty() ? Set.of() : getResourcesFromIds(resourceIds, pluginIndex, clazz); } catch (Exception e) { LOGGER.error("Failed to fetch documents from {} for source_idx: {}", resourceSharingIndex, pluginIndex, e); @@ -435,7 +435,7 @@ public Set fetchDocumentsForAGivenScope( LOGGER.debug("Found {} documents matching the criteria in {}", resourceIds.size(), resourceSharingIndex); - return getResourcesFromIds(resourceIds, pluginIndex, clazz); + return resourceIds.isEmpty() ? Set.of() : getResourcesFromIds(resourceIds, pluginIndex, clazz); } catch (Exception e) { LOGGER.error( @@ -526,7 +526,7 @@ public Set fetchDocumentsByField(String pluginIndex, String field, String LOGGER.info("Found {} documents in {} where {} = {}", resourceIds.size(), resourceSharingIndex, field, value); - return getResourcesFromIds(resourceIds, pluginIndex, clazz); + return resourceIds.isEmpty() ? Set.of() : getResourcesFromIds(resourceIds, pluginIndex, clazz); } catch (Exception e) { LOGGER.error("Failed to fetch documents from {} where {} = {}", resourceSharingIndex, field, value, e); throw new RuntimeException("Failed to fetch documents: " + e.getMessage(), e); From 3ce3d92735a927b78e641b155a1c6104a61e781b Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 13 Dec 2024 01:28:44 -0500 Subject: [PATCH 41/42] Updates javadoc Signed-off-by: Darshit Chanpura --- .../security/resources/ResourceSharingIndexHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index cf622b46a1..c0f6ea2bd0 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -304,6 +304,7 @@ public Set fetchAllDocuments(String pluginIndex, Class clazz) { *
  • "roles" - for role-based access
  • *
  • "backend_roles" - for backend role-based access
  • * + * @param clazz Class to deserialize each document from Response into * @return Set List of resource IDs that match the criteria. The list may be empty * if no matches are found * @@ -376,6 +377,7 @@ public Set fetchDocumentsForAllScopes(String pluginIndex, Set ent *
  • "backend_roles" - for backend role-based access
  • * * @param scope The scope of the access. Should be implementation of {@link org.opensearch.accesscontrol.resources.ResourceAccessScope} + * @param clazz Class to deserialize each document from Response into * @return Set List of resource IDs that match the criteria. The list may be empty * if no matches are found * @@ -484,6 +486,7 @@ public Set fetchDocumentsForAGivenScope( * @param pluginIndex The source index to match against the source_idx field * @param field The field name to search in. Must be a valid field in the index mapping * @param value The value to match for the specified field. Performs exact term matching + * @param clazz Class to deserialize each document from Response into * @return Set List of resource IDs that match the criteria. Returns an empty list * if no matches are found * From 428e11e204492d91c91161ee5b6aa7838abb28f3 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 13 Dec 2024 01:43:22 -0500 Subject: [PATCH 42/42] Adds input validation Signed-off-by: Darshit Chanpura --- .../resources/ResourceAccessHandler.java | 32 +++++++++++++++++++ .../ResourceSharingIndexHandler.java | 1 - 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java index 6837f88cbf..41b999c009 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceAccessHandler.java @@ -59,6 +59,9 @@ public ResourceAccessHandler( * @return A set of accessible resource IDs. */ public Set getAccessibleResourcesForCurrentUser(String resourceIndex, Class clazz) { + if (areArgumentsInvalid(resourceIndex, clazz)) { + return Collections.emptySet(); + } final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); if (user == null) { LOGGER.info("Unable to fetch user details "); @@ -100,6 +103,9 @@ public Set getAccessibleResourcesForCurrentUser(String resourceIndex, Cla * @return True if the user has the specified permission, false otherwise. */ public boolean hasPermission(String resourceId, String resourceIndex, String scope) { + if (areArgumentsInvalid(resourceId, resourceIndex, scope)) { + return false; + } final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Checking if {} has {} permission to resource {}", user.getName(), scope, resourceId); @@ -139,6 +145,9 @@ public boolean hasPermission(String resourceId, String resourceIndex, String sco * @return The updated ResourceSharing document. */ public ResourceSharing shareWith(String resourceId, String resourceIndex, ShareWith shareWith) { + if (areArgumentsInvalid(resourceId, resourceIndex, shareWith)) { + return null; + } final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Sharing resource {} created by {} with {}", resourceId, user.getName(), shareWith.toString()); @@ -162,6 +171,9 @@ public ResourceSharing revokeAccess( Map> revokeAccess, Set scopes ) { + if (areArgumentsInvalid(resourceId, resourceIndex, revokeAccess, scopes)) { + return null; + } final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("User {} revoking access to resource {} for {} for scopes {} ", user.getName(), resourceId, revokeAccess, scopes); @@ -178,6 +190,9 @@ public ResourceSharing revokeAccess( * @return True if the record was successfully deleted, false otherwise. */ public boolean deleteResourceSharingRecord(String resourceId, String resourceIndex) { + if (areArgumentsInvalid(resourceId, resourceIndex)) { + return false; + } final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Deleting resource sharing record for resource {} in {} created by {}", resourceId, resourceIndex, user.getName()); @@ -198,6 +213,7 @@ public boolean deleteResourceSharingRecord(String resourceId, String resourceInd * @return True if all records were successfully deleted, false otherwise. */ public boolean deleteAllResourceSharingRecordsForCurrentUser() { + final User user = threadContext.getPersistent(ConfigConstants.OPENDISTRO_SECURITY_USER); LOGGER.info("Deleting all resource sharing records for resource {}", user.getName()); @@ -308,4 +324,20 @@ private boolean checkSharing(ResourceSharing document, EntityType entityType, St .orElse(false); // Return false if no matching scope is found } + private boolean areArgumentsInvalid(Object... args) { + if (args == null) { + return true; + } + for (Object arg : args) { + if (arg == null) { + return true; + } + // Additional check for String type arguments + if (arg instanceof String && ((String) arg).trim().isEmpty()) { + return true; + } + } + return false; + } + } diff --git a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java index c0f6ea2bd0..839af57f9c 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java +++ b/src/main/java/org/opensearch/security/resources/ResourceSharingIndexHandler.java @@ -1151,7 +1151,6 @@ public boolean deleteAllRecordsForUser(String name) { * @return A set of deserialized documents. */ private Set getResourcesFromIds(Set resourceIds, String resourceIndex, Class clazz) { - Set result = new HashSet<>(); // stashing Context to avoid permission issues in-case resourceIndex is a system index try (ThreadContext.StoredContext ctx = this.threadPool.getThreadContext().stashContext()) {