From 3d5703b4f75e8bae74c1ccb730c8d95a5815cf7b Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 4 Nov 2025 17:16:51 -0500 Subject: [PATCH 1/4] WIP on adding resource type to sharing info Signed-off-by: Craig Perkins --- .../sample/SampleResourceExtension.java | 5 + .../sample/SampleResourceGroupExtension.java | 5 + .../spi/resources/ResourceProvider.java | 9 ++ .../resources/ResourceIndexListener.java | 6 ++ .../resources/ResourcePluginInfo.java | 102 ++++++++++-------- .../resources/sharing/ResourceSharing.java | 54 +++++++--- 6 files changed, 125 insertions(+), 56 deletions(-) diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceExtension.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceExtension.java index aa3a03c4f5..6f47436787 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceExtension.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceExtension.java @@ -38,6 +38,11 @@ public String resourceType() { public String resourceIndexName() { return RESOURCE_INDEX_NAME; } + + @Override + public String typeField() { + return "resource_type"; + } }); } diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceGroupExtension.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceGroupExtension.java index 2fc0f77ae9..77fb9753f0 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceGroupExtension.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResourceGroupExtension.java @@ -27,6 +27,11 @@ public String resourceType() { public String resourceIndexName() { return RESOURCE_INDEX_NAME; } + + @Override + public String typeField() { + return "resource_type"; + } }); } diff --git a/spi/src/main/java/org/opensearch/security/spi/resources/ResourceProvider.java b/spi/src/main/java/org/opensearch/security/spi/resources/ResourceProvider.java index 82e050244e..e5b7fc39cd 100644 --- a/spi/src/main/java/org/opensearch/security/spi/resources/ResourceProvider.java +++ b/spi/src/main/java/org/opensearch/security/spi/resources/ResourceProvider.java @@ -20,4 +20,13 @@ public interface ResourceProvider { String resourceIndexName(); + /** + * Returns the name of the field representing the resource type in the resource document. + * + * @return the field name containing the resource type + */ + default String typeField() { + return null; + } + } diff --git a/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java b/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java index 8382a6463c..19414bfbed 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java +++ b/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java @@ -22,6 +22,7 @@ import org.opensearch.security.resources.sharing.CreatedBy; import org.opensearch.security.resources.sharing.ResourceSharing; import org.opensearch.security.setting.OpensearchDynamicSetting; +import org.opensearch.security.spi.resources.ResourceProvider; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.User; import org.opensearch.threadpool.ThreadPool; @@ -73,6 +74,9 @@ public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult re log.debug("postIndex called on {}", resourceIndex); + String resourceType = resourcePluginInfo.getResourceTypeForIndexOp(resourceIndex, index); + ResourceProvider provider = resourcePluginInfo.getResourceProvider(resourceType); + String resourceId = index.id(); // Only proceed if this was a create operation and for primary shard @@ -107,8 +111,10 @@ public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult re resourceIndex ); }, e -> { log.debug(e.getMessage()); }); + // User.getRequestedTenant() is null if multi-tenancy is disabled ResourceSharing.Builder builder = ResourceSharing.builder() .resourceId(resourceId) + .resourceType(resourceType) .createdBy(new CreatedBy(user.getName(), user.getRequestedTenant())); ResourceSharing sharingInfo = builder.build(); // User.getRequestedTenant() is null if multi-tenancy is disabled diff --git a/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java b/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java index a08c6025cd..e2c5d6a1b4 100644 --- a/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java +++ b/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java @@ -15,20 +15,22 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import com.google.common.collect.ImmutableSet; +import org.apache.lucene.index.IndexableField; import org.opensearch.OpenSearchSecurityException; import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.index.engine.Engine; import org.opensearch.security.securityconf.FlattenedActionGroups; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; import org.opensearch.security.setting.OpensearchDynamicSetting; +import org.opensearch.security.spi.resources.ResourceProvider; import org.opensearch.security.spi.resources.ResourceSharingExtension; import org.opensearch.security.spi.resources.client.ResourceSharingClient; @@ -46,8 +48,8 @@ public class ResourcePluginInfo { private final Set resourceSharingExtensions = new HashSet<>(); - // type <-> index - private final Map typeToIndex = new HashMap<>(); + // type <-> resource provider + private final Map typeToProvider = new HashMap<>(); // UI: access-level *names* per type private final Map> typeToAccessLevels = new HashMap<>(); @@ -57,8 +59,6 @@ public class ResourcePluginInfo { // cache current protected types and their indices private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); // make the updates/reads thread-safe - private Set currentProtectedTypes = Collections.emptySet(); // snapshot of last set - private Set cachedProtectedTypeIndices = Collections.emptySet(); // precomputed indices public void setProtectedTypesSetting(OpensearchDynamicSetting> protectedTypesSetting) { this.protectedTypesSetting = protectedTypesSetting; @@ -68,7 +68,7 @@ public void setResourceSharingExtensions(Set extension lock.writeLock().lock(); try { resourceSharingExtensions.clear(); - typeToIndex.clear(); + typeToProvider.clear(); // Enforce resource-type unique-ness Set resourceTypes = new HashSet<>(); @@ -78,7 +78,7 @@ public void setResourceSharingExtensions(Set extension // add name seen so far to the resource-types set resourceTypes.add(rp.resourceType()); // also cache type->index and index->type mapping - typeToIndex.put(rp.resourceType(), rp.resourceIndexName()); + typeToProvider.put(rp.resourceType(), rp); } else { throw new OpenSearchSecurityException( String.format( @@ -91,10 +91,6 @@ public void setResourceSharingExtensions(Set extension } } resourceSharingExtensions.addAll(extensions); - - // Whenever providers change, invalidate protected caches so next update refreshes them - currentProtectedTypes = Collections.emptySet(); - cachedProtectedTypeIndices = Collections.emptySet(); } finally { lock.writeLock().unlock(); } @@ -104,35 +100,63 @@ public void updateProtectedTypes(List protectedTypes) { lock.writeLock().lock(); try { // Rebuild mappings based on the current allowlist - typeToIndex.clear(); + typeToProvider.clear(); if (protectedTypes == null || protectedTypes.isEmpty()) { - // No protected types -> leave maps empty - currentProtectedTypes = Collections.emptySet(); - cachedProtectedTypeIndices = Collections.emptySet(); return; } - // Cache current protected set as an unmodifiable snapshot - currentProtectedTypes = Collections.unmodifiableSet(new LinkedHashSet<>(protectedTypes)); - for (ResourceSharingExtension extension : resourceSharingExtensions) { for (var rp : extension.getResourceProviders()) { final String type = rp.resourceType(); - if (!currentProtectedTypes.contains(type)) continue; - - final String index = rp.resourceIndexName(); - typeToIndex.put(type, index); + if (!protectedTypes.contains(type)) continue; + typeToProvider.put(rp.resourceType(), rp); } } - - // pre-compute indices for current protected set - cachedProtectedTypeIndices = Collections.unmodifiableSet(new LinkedHashSet<>(typeToIndex.values())); } finally { lock.writeLock().unlock(); } } + public static String extractFieldFromIndexOp(String fieldName, Engine.Index indexOp) { + String fieldValue = null; + for (IndexableField f : indexOp.parsedDoc().rootDoc().getFields(fieldName)) { + if (f.stringValue() != null) { + fieldValue = f.stringValue(); + break; + } + if (f.binaryValue() != null) { // e.g., BytesRef-backed + fieldValue = f.binaryValue().utf8ToString(); + break; + } + } + return fieldValue; + } + + public String getResourceTypeForIndexOp(String resourceIndex, Engine.Index indexOp) { + lock.readLock().lock(); + try { + // Eagerly use type field from first matching provider of same index as the indexOp + // If typeField is not present, assume single resource type per index and return type from provider + var provider = typeToProvider.values() + .stream() + .filter(p -> p.resourceIndexName().equals(resourceIndex)) + .findFirst() + .orElse(null); + if (provider == null) { + // should not happen + return null; + } + if (provider.typeField() != null) { + return extractFieldFromIndexOp(provider.typeField(), indexOp); + } + // If `typeField` is not defined, assume single type to index and return type from provider + return provider.resourceType(); + } finally { + lock.readLock().unlock(); + } + } + public Set getResourceSharingExtensions() { return ImmutableSet.copyOf(resourceSharingExtensions); } @@ -179,23 +203,22 @@ public FlattenedActionGroups flattenedForType(String resourceType) { } } - public String indexByType(String type) { + public ResourceProvider getResourceProvider(String type) { lock.readLock().lock(); try { - return typeToIndex.get(type); + return typeToProvider.get(type); } finally { lock.readLock().unlock(); } } - public Set typesByIndex(String index) { + public String indexByType(String type) { lock.readLock().lock(); try { - return typeToIndex.entrySet() - .stream() - .filter(entry -> Objects.equals(entry.getValue(), index)) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()); + if (!typeToProvider.containsKey(type)) { + return null; + } + return typeToProvider.get(type).resourceIndexName(); } finally { lock.readLock().unlock(); } @@ -204,7 +227,7 @@ public Set typesByIndex(String index) { public Set getResourceTypes() { lock.readLock().lock(); try { - return typeToIndex.keySet() + return typeToProvider.keySet() .stream() .map( s -> new ResourceDashboardInfo( @@ -221,7 +244,7 @@ public Set getResourceTypes() { public Set getResourceIndices() { lock.readLock().lock(); try { - return new HashSet<>(typeToIndex.values()); + return typeToProvider.values().stream().map(ResourceProvider::resourceIndexName).collect(Collectors.toSet()); } finally { lock.readLock().unlock(); } @@ -235,15 +258,10 @@ public Set getResourceIndicesForProtectedTypes() { lock.readLock().lock(); try { - // If caller is asking for the current protected set, return the cache - if (new LinkedHashSet<>(resourceTypes).equals(currentProtectedTypes)) { - return cachedProtectedTypeIndices; - } - - return typeToIndex.entrySet() + return typeToProvider.entrySet() .stream() .filter(e -> resourceTypes.contains(e.getKey())) - .map(Map.Entry::getValue) + .map(e -> e.getValue().resourceIndexName()) .collect(Collectors.toSet()); } finally { lock.readLock().unlock(); diff --git a/src/main/java/org/opensearch/security/resources/sharing/ResourceSharing.java b/src/main/java/org/opensearch/security/resources/sharing/ResourceSharing.java index 318c960d38..4e58d784f5 100644 --- a/src/main/java/org/opensearch/security/resources/sharing/ResourceSharing.java +++ b/src/main/java/org/opensearch/security/resources/sharing/ResourceSharing.java @@ -51,6 +51,11 @@ public class ResourceSharing implements ToXContentFragment, NamedWriteable { */ private String resourceId; + /** + * The type of the resource + */ + private String resourceType; + /** * Information about who created the resource */ @@ -61,14 +66,9 @@ public class ResourceSharing implements ToXContentFragment, NamedWriteable { */ private ShareWith shareWith; - public ResourceSharing(String resourceId, CreatedBy createdBy, ShareWith shareWith) { - this.resourceId = resourceId; - this.createdBy = createdBy; - this.shareWith = shareWith; - } - private ResourceSharing(Builder b) { this.resourceId = b.resourceId; + this.resourceType = b.resourceType; this.createdBy = b.createdBy; this.shareWith = b.shareWith; } @@ -137,21 +137,33 @@ public void revoke(String accessLevel, Recipients target) { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ResourceSharing resourceSharing = (ResourceSharing) o; - return Objects.equals(getResourceId(), resourceSharing.getResourceId()) - && Objects.equals(getCreatedBy(), resourceSharing.getCreatedBy()) - && Objects.equals(getShareWith(), resourceSharing.getShareWith()); + if (!(o instanceof ResourceSharing)) return false; + ResourceSharing that = (ResourceSharing) o; + return Objects.equals(resourceId, that.resourceId) + && Objects.equals(resourceType, that.resourceType) + && Objects.equals(createdBy, that.createdBy) + && Objects.equals(shareWith, that.shareWith); } @Override public int hashCode() { - return Objects.hash(getResourceId(), getCreatedBy(), getShareWith()); + return Objects.hash(resourceId, resourceType, createdBy, shareWith); } @Override public String toString() { - return "ResourceSharing {" + "resourceId='" + resourceId + '\'' + ", createdBy=" + createdBy + ", sharedWith=" + shareWith + '}'; + return "ResourceSharing{" + + "resourceId='" + + resourceId + + '\'' + + ", resourceType='" + + resourceType + + '\'' + + ", createdBy=" + + createdBy + + ", shareWith=" + + shareWith + + '}'; } @Override @@ -162,6 +174,7 @@ public String getWriteableName() { @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(resourceId); + out.writeString(resourceType); createdBy.writeTo(out); if (shareWith != null) { out.writeBoolean(true); @@ -173,7 +186,7 @@ public void writeTo(StreamOutput out) throws IOException { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject().field("resource_id", resourceId).field("created_by"); + builder.startObject().field("resource_id", resourceId).field("resource_type", resourceType).field("created_by"); createdBy.toXContent(builder, params); if (shareWith != null) { builder.field("share_with"); @@ -196,6 +209,13 @@ public static ResourceSharing fromXContent(XContentParser parser) throws IOExcep case "resource_id": b.resourceId(parser.text()); break; + case "resource_type": + if (token == XContentParser.Token.VALUE_NULL) { + b.resourceType(null); + } else { + b.resourceType(parser.text()); + } + break; case "created_by": b.createdBy(CreatedBy.fromXContent(parser)); break; @@ -359,6 +379,7 @@ public List getAllPrincipals() { public static final class Builder { private String resourceId; + private String resourceType; private CreatedBy createdBy; private ShareWith shareWith; @@ -367,6 +388,11 @@ public Builder resourceId(String resourceId) { return this; } + public Builder resourceType(String resourceType) { + this.resourceType = resourceType; + return this; + } + public Builder createdBy(CreatedBy createdBy) { this.createdBy = createdBy; return this; From f94ab9d8049178e1dec863a0fd37592594f357ec Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 4 Nov 2025 20:44:28 -0500 Subject: [PATCH 2/4] Modify tests Signed-off-by: Craig Perkins --- .../org/opensearch/sample/resource/TestUtils.java | 8 +++++--- .../resource/securityapis/MigrateApiTests.java | 1 + .../java/org/opensearch/sample/SampleResource.java | 7 +++++-- .../CreateResourceGroupTransportAction.java | 4 ++-- .../src/main/resources/mappings.json | 3 +++ .../security/resources/ResourceIndexListener.java | 2 -- .../security/resources/ResourcePluginInfo.java | 13 ++++--------- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/TestUtils.java b/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/TestUtils.java index f61440b288..57dc6ad270 100644 --- a/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/TestUtils.java +++ b/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/TestUtils.java @@ -344,7 +344,7 @@ public void wipeOutResourceEntries() { // Helper to create a sample resource and return its ID public String createSampleResourceAs(TestSecurityConfig.User user, Header... headers) { try (TestRestClient client = cluster.getRestClient(user)) { - String sample = "{\"name\":\"sample\"}"; + String sample = "{\"name\":\"sample\",\"resource_type\":\"" + RESOURCE_TYPE + "\"}"; TestRestClient.HttpResponse resp = client.putJson(SAMPLE_RESOURCE_CREATE_ENDPOINT, sample, headers); resp.assertStatusCode(HttpStatus.SC_OK); return resp.getTextFromJsonBody("/message").split(":")[1].trim(); @@ -353,7 +353,7 @@ public String createSampleResourceAs(TestSecurityConfig.User user, Header... hea public String createSampleResourceGroupAs(TestSecurityConfig.User user, Header... headers) { try (TestRestClient client = cluster.getRestClient(user)) { - String sample = "{\"name\":\"samplegroup\"}"; + String sample = "{\"name\":\"samplegroup\",\"resource_type\":\"" + RESOURCE_GROUP_TYPE + "\"}"; TestRestClient.HttpResponse resp = client.putJson(SAMPLE_RESOURCE_GROUP_CREATE_ENDPOINT, sample, headers); resp.assertStatusCode(HttpStatus.SC_OK); return resp.getTextFromJsonBody("/message").split(":")[1].trim(); @@ -362,7 +362,7 @@ public String createSampleResourceGroupAs(TestSecurityConfig.User user, Header.. public String createRawResourceAs(CertificateData adminCert) { try (TestRestClient client = cluster.getRestClient(adminCert)) { - String sample = "{\"name\":\"sample\"}"; + String sample = "{\"name\":\"sample\",\"resource_type\":\"" + RESOURCE_TYPE + "\"}"; TestRestClient.HttpResponse resp = client.postJson(RESOURCE_INDEX_NAME + "/_doc", sample); resp.assertStatusCode(HttpStatus.SC_CREATED); return resp.getTextFromJsonBody("/_id"); @@ -680,6 +680,8 @@ public void awaitSharingEntry(String resourceId, String expectedString) { TestRestClient.HttpResponse response = client.get(RESOURCE_SHARING_INDEX + "/_doc/" + resourceId); response.assertStatusCode(200); String body = response.getBody(); + String resourceType = response.getTextFromJsonBody("/_source/resource_type"); + assert resourceType != null : "resource_type cannot be null"; assertThat(body, containsString(expectedString)); assertThat(body, containsString(resourceId)); }); diff --git a/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/securityapis/MigrateApiTests.java b/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/securityapis/MigrateApiTests.java index 372f27a2a8..e09a0bce7b 100644 --- a/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/securityapis/MigrateApiTests.java +++ b/sample-resource-plugin/src/integrationTest/java/org/opensearch/sample/resource/securityapis/MigrateApiTests.java @@ -301,6 +301,7 @@ private ArrayNode expectedHits(String resourceId, String accessLevel) { // 3) Build the _source sub-object ObjectNode source = hit.putObject("_source"); source.put("resource_id", resourceId); + source.put("resource_type", RESOURCE_TYPE); ObjectNode createdBy = source.putObject("created_by"); createdBy.put("user", MIGRATION_USER.getName()); diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResource.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResource.java index 63f1a20474..370b6acd0b 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResource.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/SampleResource.java @@ -60,14 +60,16 @@ public SampleResource(StreamInput in) throws IOException { } s.setName((String) a[0]); s.setDescription((String) a[1]); - s.setAttributes((Map) a[2]); - s.setUser((User) a[3]); + // ignore a[2] as we know the type + s.setAttributes((Map) a[3]); + s.setUser((User) a[4]); return s; }); static { PARSER.declareString(constructorArg(), new ParseField("name")); PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("description")); + PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("resource_type")); PARSER.declareObjectOrNull(optionalConstructorArg(), (p, c) -> p.mapStrings(), null, new ParseField("attributes")); PARSER.declareObjectOrNull(optionalConstructorArg(), (p, c) -> User.parse(p), null, new ParseField("user")); } @@ -80,6 +82,7 @@ public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params par return builder.startObject() .field("name", name) .field("description", description) + .field("resource_type", RESOURCE_TYPE) .field("attributes", attributes) .field("user", user) .endObject(); diff --git a/sample-resource-plugin/src/main/java/org/opensearch/sample/resourcegroup/actions/transport/CreateResourceGroupTransportAction.java b/sample-resource-plugin/src/main/java/org/opensearch/sample/resourcegroup/actions/transport/CreateResourceGroupTransportAction.java index c761577f20..e9d2eaf450 100644 --- a/sample-resource-plugin/src/main/java/org/opensearch/sample/resourcegroup/actions/transport/CreateResourceGroupTransportAction.java +++ b/sample-resource-plugin/src/main/java/org/opensearch/sample/resourcegroup/actions/transport/CreateResourceGroupTransportAction.java @@ -53,10 +53,10 @@ public CreateResourceGroupTransportAction(TransportService transportService, Act @Override protected void doExecute(Task task, CreateResourceGroupRequest request, ActionListener listener) { - createResource(request, listener); + createResourceGroup(request, listener); } - private void createResource(CreateResourceGroupRequest request, ActionListener listener) { + private void createResourceGroup(CreateResourceGroupRequest request, ActionListener listener) { SampleResourceGroup sampleGroup = request.getResourceGroup(); // 1. Read mapping JSON from the config file diff --git a/sample-resource-plugin/src/main/resources/mappings.json b/sample-resource-plugin/src/main/resources/mappings.json index d0141e153f..b163ee8c11 100644 --- a/sample-resource-plugin/src/main/resources/mappings.json +++ b/sample-resource-plugin/src/main/resources/mappings.json @@ -4,6 +4,9 @@ "schema_version": 1 }, "properties": { + "resource_type": { + "type": "keyword" + }, "all_shared_principals": { "type": "keyword" } diff --git a/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java b/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java index 19414bfbed..1d32259ee4 100644 --- a/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java +++ b/src/main/java/org/opensearch/security/resources/ResourceIndexListener.java @@ -22,7 +22,6 @@ import org.opensearch.security.resources.sharing.CreatedBy; import org.opensearch.security.resources.sharing.ResourceSharing; import org.opensearch.security.setting.OpensearchDynamicSetting; -import org.opensearch.security.spi.resources.ResourceProvider; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.User; import org.opensearch.threadpool.ThreadPool; @@ -75,7 +74,6 @@ public void postIndex(ShardId shardId, Engine.Index index, Engine.IndexResult re log.debug("postIndex called on {}", resourceIndex); String resourceType = resourcePluginInfo.getResourceTypeForIndexOp(resourceIndex, index); - ResourceProvider provider = resourcePluginInfo.getResourceProvider(resourceType); String resourceId = index.id(); diff --git a/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java b/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java index e2c5d6a1b4..92249aa4d1 100644 --- a/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java +++ b/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java @@ -20,6 +20,8 @@ import java.util.stream.Collectors; import com.google.common.collect.ImmutableSet; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.lucene.index.IndexableField; import org.opensearch.OpenSearchSecurityException; @@ -42,6 +44,8 @@ */ public class ResourcePluginInfo { + private static final Logger log = LogManager.getLogger(ResourcePluginInfo.class); + private ResourceSharingClient resourceAccessControlClient; private OpensearchDynamicSetting> protectedTypesSetting; @@ -203,15 +207,6 @@ public FlattenedActionGroups flattenedForType(String resourceType) { } } - public ResourceProvider getResourceProvider(String type) { - lock.readLock().lock(); - try { - return typeToProvider.get(type); - } finally { - lock.readLock().unlock(); - } - } - public String indexByType(String type) { lock.readLock().lock(); try { From f6b6cf390e9d34e2b2f86cd260a4e271102ac69b Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 4 Nov 2025 21:23:54 -0500 Subject: [PATCH 3/4] Fix migrate API Signed-off-by: Craig Perkins --- .../security/resources/ResourcePluginInfo.java | 9 +++++++++ .../api/migrate/MigrateResourceSharingInfoApiAction.java | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java b/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java index 92249aa4d1..c72e683f15 100644 --- a/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java +++ b/src/main/java/org/opensearch/security/resources/ResourcePluginInfo.java @@ -207,6 +207,15 @@ public FlattenedActionGroups flattenedForType(String resourceType) { } } + public ResourceProvider getResourceProvider(String type) { + lock.readLock().lock(); + try { + return typeToProvider.get(type); + } finally { + lock.readLock().unlock(); + } + } + public String indexByType(String type) { lock.readLock().lock(); try { diff --git a/src/main/java/org/opensearch/security/resources/api/migrate/MigrateResourceSharingInfoApiAction.java b/src/main/java/org/opensearch/security/resources/api/migrate/MigrateResourceSharingInfoApiAction.java index 06c0a1a895..4f0b444fa2 100644 --- a/src/main/java/org/opensearch/security/resources/api/migrate/MigrateResourceSharingInfoApiAction.java +++ b/src/main/java/org/opensearch/security/resources/api/migrate/MigrateResourceSharingInfoApiAction.java @@ -60,6 +60,7 @@ import org.opensearch.security.resources.sharing.ResourceSharing; import org.opensearch.security.resources.sharing.ShareWith; import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.security.spi.resources.ResourceProvider; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.client.Client; @@ -292,6 +293,8 @@ private ValidationResult createNewSharingRecords(Triple createNewSharingRecords(Triple Date: Wed, 5 Nov 2025 07:48:37 -0500 Subject: [PATCH 4/4] Add to CHANGELOG Signed-off-by: Craig Perkins --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1228880cf9..d64ba46185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Adding Alerting V2 roles to roles.yml ([#5747](https://github.com/opensearch-project/security/pull/5747)) - add suggest api to ad read access role ([#5754](https://github.com/opensearch-project/security/pull/5754)) - Get list of headersToCopy from core and use getHeader(String headerName) instead of getHeaders() ([#5769](https://github.com/opensearch-project/security/pull/5769)) +- [Resource Sharing] Keep track of resource_type on resource sharing document ([#5772](https://github.com/opensearch-project/security/pull/5772)) ### Bug Fixes - Create a WildcardMatcher.NONE when creating a WildcardMatcher with an empty string ([#5694](https://github.com/opensearch-project/security/pull/5694))