Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -289,19 +289,12 @@ public void testRawAccess_allAccessUser() {
api.assertDirectPostSearch(searchByNamePayload("sample"), FULL_ACCESS_USER, HttpStatus.SC_OK, 1, "sample");
api.assertDirectPostSearch(searchByNamePayload("sampleUser"), FULL_ACCESS_USER, HttpStatus.SC_OK, 1, "sampleUser");

// cannot update or delete resource
api.assertDirectUpdate(adminResId, FULL_ACCESS_USER, "sampleUpdateAdmin", HttpStatus.SC_OK);
api.assertDirectDelete(adminResId, FULL_ACCESS_USER, HttpStatus.SC_OK);
// can update and delete own resource
api.assertDirectUpdate(userResId, FULL_ACCESS_USER, "sampleUpdateUser", HttpStatus.SC_OK);
api.assertDirectDelete(userResId, FULL_ACCESS_USER, HttpStatus.SC_OK);

// can view, share, revoke and delete resource sharing record(s) directly
api.assertDirectViewSharingRecord(adminResId, FULL_ACCESS_USER, HttpStatus.SC_OK);
api.assertDirectShare(adminResId, FULL_ACCESS_USER, NO_ACCESS_USER, SAMPLE_FULL_ACCESS_RESOURCE_AG, HttpStatus.SC_OK);
api.assertDirectRevoke(adminResId, FULL_ACCESS_USER, NO_ACCESS_USER, SAMPLE_FULL_ACCESS_RESOURCE_AG, HttpStatus.SC_OK);
api.assertDirectDeleteResourceSharingRecord(adminResId, FULL_ACCESS_USER, HttpStatus.SC_OK);

// can update or delete admin resource, since system index protection is disabled and user has direct index access.
api.assertDirectUpdate(adminResId, FULL_ACCESS_USER, "sampleUpdateAdmin", HttpStatus.SC_OK);
api.assertDirectDelete(adminResId, FULL_ACCESS_USER, HttpStatus.SC_OK);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
import org.opensearch.sample.resource.actions.rest.create.CreateResourceAction;
import org.opensearch.sample.resource.actions.rest.create.CreateResourceRequest;
import org.opensearch.sample.resource.actions.rest.create.CreateResourceResponse;
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;

Expand All @@ -45,26 +45,21 @@ public class CreateResourceTransportAction extends HandledTransportAction<Create
private static final Logger log = LogManager.getLogger(CreateResourceTransportAction.class);

private final TransportService transportService;
private final Client nodeClient;
private final PluginClient pluginClient;

@Inject
public CreateResourceTransportAction(TransportService transportService, ActionFilters actionFilters, Client nodeClient) {
public CreateResourceTransportAction(TransportService transportService, ActionFilters actionFilters, PluginClient pluginClient) {
super(CreateResourceAction.NAME, transportService, actionFilters, CreateResourceRequest::new);
this.transportService = transportService;
this.nodeClient = nodeClient;
this.pluginClient = pluginClient;
}

@Override
protected void doExecute(Task task, CreateResourceRequest request, ActionListener<CreateResourceResponse> listener) {
ThreadContext threadContext = transportService.getThreadPool().getThreadContext();
String userStr = threadContext.getTransient(ConfigConstants.OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT);
User user = User.parse(userStr);
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
createResource(request, user, listener);
} catch (Exception e) {
log.error("Failed to create resource", e);
listener.onFailure(e);
}
createResource(request, user, listener);
}

private void createResource(CreateResourceRequest request, User user, ActionListener<CreateResourceResponse> listener) {
Expand All @@ -88,17 +83,17 @@ private void createResource(CreateResourceRequest request, User user, ActionList
}

// 2. Ensure index exists with mapping, then index the doc
ensureIndexWithMapping(nodeClient, mappingJson, ActionListener.wrap(v -> {
ensureIndexWithMapping(pluginClient, mappingJson, ActionListener.wrap(v -> {
try (XContentBuilder builder = org.opensearch.common.xcontent.XContentFactory.jsonBuilder()) {
IndexRequest ir = nodeClient.prepareIndex(RESOURCE_INDEX_NAME)
IndexRequest ir = pluginClient.prepareIndex(RESOURCE_INDEX_NAME)
.setWaitForActiveShards(1)
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.setSource(sample.toXContent(builder, ToXContent.EMPTY_PARAMS))
.request();

log.debug("Index Request: {}", ir);

nodeClient.index(ir, ActionListener.wrap(idxResponse -> {
pluginClient.index(ir, ActionListener.wrap(idxResponse -> {
log.debug("Created resource: {}", idxResponse.getId());
listener.onResponse(new CreateResourceResponse("Created resource: " + idxResponse.getId()));
}, listener::onFailure));
Expand All @@ -113,12 +108,12 @@ private void createResource(CreateResourceRequest request, User user, ActionList
* - If the index does not exist: creates it with the mapping.
* - If the index exists: updates (puts) the mapping.
*/
private void ensureIndexWithMapping(Client client, String mappingJson, ActionListener<Void> listener) {
private void ensureIndexWithMapping(PluginClient pluginClient, String mappingJson, ActionListener<Void> listener) {
String indexName = RESOURCE_INDEX_NAME;
client.admin().indices().prepareExists(indexName).execute(ActionListener.wrap(existsResp -> {
pluginClient.admin().indices().prepareExists(indexName).execute(ActionListener.wrap(existsResp -> {
if (!existsResp.isExists()) {
// Create index with mapping
client.admin().indices().prepareCreate(indexName).setMapping(mappingJson).execute(ActionListener.wrap(createResp -> {
pluginClient.admin().indices().prepareCreate(indexName).setMapping(mappingJson).execute(ActionListener.wrap(createResp -> {
if (!createResp.isAcknowledged()) {
listener.onFailure(new IllegalStateException("CreateIndex not acknowledged for " + indexName));
return;
Expand All @@ -127,7 +122,7 @@ private void ensureIndexWithMapping(Client client, String mappingJson, ActionLis
}, listener::onFailure));
} else {
// Update mapping on existing index
client.admin()
pluginClient.admin()
.indices()
.preparePutMapping(indexName)
.setSource(mappingJson, XContentType.JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
import org.opensearch.action.support.HandledTransportAction;
import org.opensearch.action.support.WriteRequest;
import org.opensearch.common.inject.Inject;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.core.action.ActionListener;
import org.opensearch.sample.resource.actions.rest.delete.DeleteResourceAction;
import org.opensearch.sample.resource.actions.rest.delete.DeleteResourceRequest;
import org.opensearch.sample.resource.actions.rest.delete.DeleteResourceResponse;
import org.opensearch.sample.utils.PluginClient;
import org.opensearch.tasks.Task;
import org.opensearch.transport.TransportService;
import org.opensearch.transport.client.node.NodeClient;

import static org.opensearch.sample.utils.Constants.RESOURCE_INDEX_NAME;

Expand All @@ -37,13 +36,13 @@ public class DeleteResourceTransportAction extends HandledTransportAction<Delete
private static final Logger log = LogManager.getLogger(DeleteResourceTransportAction.class);

private final TransportService transportService;
private final NodeClient nodeClient;
private final PluginClient pluginClient;

@Inject
public DeleteResourceTransportAction(TransportService transportService, ActionFilters actionFilters, NodeClient nodeClient) {
public DeleteResourceTransportAction(TransportService transportService, ActionFilters actionFilters, PluginClient pluginClient) {
super(DeleteResourceAction.NAME, transportService, actionFilters, DeleteResourceRequest::new);
this.transportService = transportService;
this.nodeClient = nodeClient;
this.pluginClient = pluginClient;
}

@Override
Expand All @@ -64,18 +63,15 @@ protected void doExecute(Task task, DeleteResourceRequest request, ActionListene
listener.onFailure(exception);
});

ThreadContext threadContext = transportService.getThreadPool().getThreadContext();
try (ThreadContext.StoredContext ignored = threadContext.stashContext()) {
deleteResource(resourceId, deleteResponseListener);
}
deleteResource(resourceId, deleteResponseListener);
}

private void deleteResource(String resourceId, ActionListener<DeleteResponse> listener) {
DeleteRequest deleteRequest = new DeleteRequest(RESOURCE_INDEX_NAME, resourceId).setRefreshPolicy(
WriteRequest.RefreshPolicy.IMMEDIATE
);

nodeClient.delete(deleteRequest, listener);
pluginClient.delete(deleteRequest, listener);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@
import org.opensearch.action.support.HandledTransportAction;
import org.opensearch.action.support.WriteRequest;
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.core.xcontent.XContentBuilder;
import org.opensearch.sample.SampleResource;
import org.opensearch.sample.resource.actions.rest.create.CreateResourceResponse;
import org.opensearch.sample.resource.actions.rest.create.UpdateResourceAction;
import org.opensearch.sample.resource.actions.rest.create.UpdateResourceRequest;
import org.opensearch.sample.utils.PluginClient;
import org.opensearch.tasks.Task;
import org.opensearch.transport.TransportService;
import org.opensearch.transport.client.node.NodeClient;

import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.opensearch.sample.utils.Constants.RESOURCE_INDEX_NAME;
Expand All @@ -38,13 +37,13 @@ public class UpdateResourceTransportAction extends HandledTransportAction<Update
private static final Logger log = LogManager.getLogger(UpdateResourceTransportAction.class);

private final TransportService transportService;
private final NodeClient nodeClient;
private final PluginClient pluginClient;

@Inject
public UpdateResourceTransportAction(TransportService transportService, ActionFilters actionFilters, NodeClient nodeClient) {
public UpdateResourceTransportAction(TransportService transportService, ActionFilters actionFilters, PluginClient pluginClient) {
super(UpdateResourceAction.NAME, transportService, actionFilters, UpdateResourceRequest::new);
this.transportService = transportService;
this.nodeClient = nodeClient;
this.pluginClient = pluginClient;
}

@Override
Expand All @@ -58,8 +57,7 @@ protected void doExecute(Task task, UpdateResourceRequest request, ActionListene
}

private void updateResource(UpdateResourceRequest request, ActionListener<CreateResourceResponse> listener) {
ThreadContext threadContext = this.transportService.getThreadPool().getThreadContext();
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
try {
String resourceId = request.getResourceId();
SampleResource sample = request.getResource();
try (XContentBuilder builder = jsonBuilder()) {
Expand All @@ -73,7 +71,7 @@ private void updateResource(UpdateResourceRequest request, ActionListener<Create

log.debug("Update Request: {}", ir.toString());

nodeClient.index(ir, ActionListener.wrap(updateResponse -> {
pluginClient.index(ir, ActionListener.wrap(updateResponse -> {
listener.onResponse(
new CreateResourceResponse("Resource " + request.getResource().getName() + " updated successfully.")
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.opensearch.action.ActionRequest;
import org.opensearch.action.DocRequest;
import org.opensearch.action.DocWriteRequest;
import org.opensearch.action.get.GetRequest;
import org.opensearch.common.settings.Settings;
import org.opensearch.core.action.ActionListener;
Expand Down Expand Up @@ -105,7 +106,23 @@ public boolean shouldEvaluate(ActionRequest request) {
);
if (!isResourceSharingFeatureEnabled) return false;
if (!(request instanceof DocRequest docRequest)) return false;
/**
* Authorization notes:
*
* - Treat {@link GetRequest} and all {@link DocWriteRequest} types as standard *index actions*.
* They should NOT be evaluated by {@code ResourceAccessEvaluator}.
*
* - {@code ResourceAccessEvaluator} is for higher-level transport actions that operate on a
* single shareable resource. Those actions may perform plugin/system-level index operations
* against the system (resource) index that stores resource metadata. Such accesses must be
* evaluated by {@code SystemIndexAccessEvaluator}.
*
* - {@link DocWriteRequest} is the abstract base for write requests
* ({@link IndexRequest}, {@link UpdateRequest}, {@link DeleteRequest}) and may appear as items
* in a {@code _bulk} request.
*/
if (request instanceof GetRequest) return false;
if (request instanceof DocWriteRequest<?>) return false;
if (Strings.isNullOrEmpty(docRequest.id())) {
log.debug("Request id is blank or null, request is of type {}", docRequest.getClass().getName());
return false;
Expand Down
Loading