-
Notifications
You must be signed in to change notification settings - Fork 331
[Resource Sharing] Use DLS to automatically filter sharable resources for authenticated user based on all_shared_principals
#5600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 19 commits
f25ac34
746b239
3b1f0fd
0bfc778
4aa049b
8bff6f3
6ef6d1f
3d90774
5f6e254
fee622b
373547e
9cae246
2cba16e
21ae888
2613852
3c9ceec
f532d8e
f539295
ca58fa1
956487d
8c58c37
7ceafa8
f6519f0
ddb9a7c
7e10713
a3b3511
b663a0e
a86c2a8
7a1207c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,6 @@ | |
|
||
package org.opensearch.sample.resource.actions.transport; | ||
|
||
import java.util.Set; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
|
@@ -18,99 +16,30 @@ | |
import org.opensearch.action.support.ActionFilters; | ||
import org.opensearch.action.support.HandledTransportAction; | ||
import org.opensearch.common.inject.Inject; | ||
import org.opensearch.common.util.concurrent.ThreadContext; | ||
import org.opensearch.core.action.ActionListener; | ||
import org.opensearch.index.query.BoolQueryBuilder; | ||
import org.opensearch.index.query.QueryBuilder; | ||
import org.opensearch.index.query.QueryBuilders; | ||
import org.opensearch.sample.client.ResourceSharingClientAccessor; | ||
import org.opensearch.sample.resource.actions.rest.search.SearchResourceAction; | ||
import org.opensearch.search.builder.SearchSourceBuilder; | ||
import org.opensearch.security.spi.resources.client.ResourceSharingClient; | ||
import org.opensearch.sample.utils.PluginClient; | ||
import org.opensearch.tasks.Task; | ||
import org.opensearch.transport.TransportService; | ||
import org.opensearch.transport.client.Client; | ||
|
||
import static org.opensearch.sample.utils.Constants.RESOURCE_INDEX_NAME; | ||
|
||
/** | ||
* Transport action for searching sample resources | ||
*/ | ||
public class SearchResourceTransportAction extends HandledTransportAction<SearchRequest, SearchResponse> { | ||
private static final Logger log = LogManager.getLogger(SearchResourceTransportAction.class); | ||
|
||
private final TransportService transportService; | ||
private final Client nodeClient; | ||
private final ResourceSharingClient resourceSharingClient; | ||
private final PluginClient pluginClient; | ||
|
||
@Inject | ||
public SearchResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) { | ||
public SearchResourceTransportAction(TransportService transportService, ActionFilters actionFilters, PluginClient pluginClient) { | ||
super(SearchResourceAction.NAME, transportService, actionFilters, SearchRequest::new); | ||
this.transportService = transportService; | ||
this.nodeClient = nodeClient; | ||
this.resourceSharingClient = ResourceSharingClientAccessor.getInstance().getResourceSharingClient(); | ||
this.pluginClient = pluginClient; | ||
} | ||
|
||
@Override | ||
protected void doExecute(Task task, SearchRequest request, ActionListener<SearchResponse> listener) { | ||
ThreadContext threadContext = transportService.getThreadPool().getThreadContext(); | ||
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) { | ||
if (resourceSharingClient == null) { | ||
nodeClient.search(request, listener); | ||
return; | ||
} | ||
// if the resource sharing feature is enabled, we only allow search from documents that requested user has access to | ||
searchFilteredIds(request, listener); | ||
} catch (Exception e) { | ||
log.error("Failed to search resources", e); | ||
listener.onFailure(e); | ||
} | ||
} | ||
|
||
private void searchFilteredIds(SearchRequest request, ActionListener<SearchResponse> listener) { | ||
SearchSourceBuilder src = request.source() != null ? request.source() : new SearchSourceBuilder(); | ||
ActionListener<Set<String>> idsListener = ActionListener.wrap(resourceIds -> { | ||
mergeAccessibleFilter(src, resourceIds); | ||
request.source(src); | ||
nodeClient.search(request, listener); | ||
}, e -> { | ||
mergeAccessibleFilter(src, Set.of()); | ||
request.source(src); | ||
nodeClient.search(request, listener); | ||
}); | ||
|
||
resourceSharingClient.getAccessibleResourceIds(RESOURCE_INDEX_NAME, idsListener); | ||
} | ||
|
||
private void mergeAccessibleFilter(SearchSourceBuilder src, Set<String> resourceIds) { | ||
QueryBuilder accessQB; | ||
|
||
if (resourceIds == null || resourceIds.isEmpty()) { | ||
// match nothing | ||
accessQB = QueryBuilders.boolQuery().mustNot(QueryBuilders.matchAllQuery()); | ||
} else { | ||
// match only from a provided set of resources | ||
accessQB = QueryBuilders.idsQuery().addIds(resourceIds.toArray(new String[0])); | ||
} | ||
|
||
QueryBuilder existing = src.query(); | ||
if (existing == null) { | ||
// No existing query → just the filter | ||
src.query(QueryBuilders.boolQuery().filter(accessQB)); | ||
return; | ||
} | ||
pluginClient.search(request, listener); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need to update plugin-dev documentation to declare plugin as IdentityAwarePlugin. |
||
|
||
if (existing instanceof BoolQueryBuilder) { | ||
// Reuse existing bool: just add a filter clause | ||
((BoolQueryBuilder) existing).filter(accessQB); | ||
src.query(existing); | ||
} else { | ||
// Preserve existing scoring by keeping it in MUST, add our filter | ||
BoolQueryBuilder merged = QueryBuilders.boolQuery() | ||
.must(existing) // keep original query semantics/scoring | ||
.filter(accessQB); // filter results | ||
src.query(merged); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,6 @@ | |
|
||
package org.opensearch.security.spi.resources.client; | ||
|
||
import java.util.Set; | ||
|
||
import org.opensearch.core.action.ActionListener; | ||
import org.opensearch.security.spi.resources.sharing.ResourceSharing; | ||
import org.opensearch.security.spi.resources.sharing.ShareWith; | ||
|
@@ -47,11 +45,4 @@ public interface ResourceSharingClient { | |
* @param listener The listener to be notified with the updated ResourceSharing document. | ||
*/ | ||
void revoke(String resourceId, String resourceIndex, ShareWith target, ActionListener<ResourceSharing> listener); | ||
|
||
/** | ||
* Lists resourceIds of all shareable resources accessible by the current user. | ||
* @param resourceIndex The index containing the resources. | ||
* @param listener The listener to be notified with the set of accessible resources. | ||
*/ | ||
void getAccessibleResourceIds(String resourceIndex, ActionListener<Set<String>> listener); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. I'll add this back in until hierarchy is properly supported. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ML uses modelGroupIds to filter search for models. We need to keep this here until we support hierarchy. |
||
} |
Uh oh!
There was an error while loading. Please reload this page.