Skip to content

Commit

Permalink
Consolidates rest actions to a single place
Browse files Browse the repository at this point in the history
Signed-off-by: Darshit Chanpura <[email protected]>
  • Loading branch information
DarshitChanpura committed Jan 19, 2025
1 parent 456418e commit ba6928a
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import static org.opensearch.sample.utils.Constants.RESOURCE_INDEX_NAME;
import static org.opensearch.sample.utils.Constants.SAMPLE_RESOURCE_PLUGIN_PREFIX;
import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX;
import static org.opensearch.security.dlic.rest.support.Utils.PLUGIN_RESOURCE_ROUTE_PREFIX;

/**
* These tests run with security enabled
Expand All @@ -24,10 +24,11 @@ public class AbstractSampleResourcePluginTests {
static final String SAMPLE_RESOURCE_CREATE_ENDPOINT = SAMPLE_RESOURCE_PLUGIN_PREFIX + "/create";
static final String SAMPLE_RESOURCE_UPDATE_ENDPOINT = SAMPLE_RESOURCE_PLUGIN_PREFIX + "/update";
static final String SAMPLE_RESOURCE_DELETE_ENDPOINT = SAMPLE_RESOURCE_PLUGIN_PREFIX + "/delete";
static final String SECURITY_RESOURCE_LIST_ENDPOINT = PLUGINS_PREFIX + "/resources/list";
static final String SECURITY_RESOURCE_SHARE_ENDPOINT = PLUGINS_PREFIX + "/resources/share";
static final String SECURITY_RESOURCE_VERIFY_ENDPOINT = PLUGINS_PREFIX + "/resources/verify_access";
static final String SECURITY_RESOURCE_REVOKE_ENDPOINT = PLUGINS_PREFIX + "/resources/revoke";
private static final String PLUGIN_RESOURCE_ROUTE_PREFIX_NO_LEADING_SLASH = PLUGIN_RESOURCE_ROUTE_PREFIX.replaceFirst("/", "");
static final String SECURITY_RESOURCE_LIST_ENDPOINT = PLUGIN_RESOURCE_ROUTE_PREFIX_NO_LEADING_SLASH + "/list";
static final String SECURITY_RESOURCE_SHARE_ENDPOINT = PLUGIN_RESOURCE_ROUTE_PREFIX_NO_LEADING_SLASH + "/share";
static final String SECURITY_RESOURCE_VERIFY_ENDPOINT = PLUGIN_RESOURCE_ROUTE_PREFIX_NO_LEADING_SLASH + "/verify_access";
static final String SECURITY_RESOURCE_REVOKE_ENDPOINT = PLUGIN_RESOURCE_ROUTE_PREFIX_NO_LEADING_SLASH + "/revoke";

static String shareWithPayload(String resourceId) {
return "{"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import org.apache.http.HttpStatus;
import org.junit.*;
import org.junit.After;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.opensearch.painless.PainlessModulePlugin;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.opensearch.sample.AbstractSampleResourcePluginTests.*;
import static org.opensearch.sample.utils.Constants.RESOURCE_INDEX_NAME;
import static org.opensearch.security.resources.ResourceSharingConstants.OPENSEARCH_RESOURCE_SHARING_INDEX;
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,10 @@
import org.opensearch.security.rest.SecurityInfoAction;
import org.opensearch.security.rest.SecurityWhoAmIAction;
import org.opensearch.security.rest.TenantInfoAction;
import org.opensearch.security.rest.resources.access.RestResourceAccessAction;
import org.opensearch.security.rest.resources.access.list.ListAccessibleResourcesAction;
import org.opensearch.security.rest.resources.access.list.RestListAccessibleResourcesAction;
import org.opensearch.security.rest.resources.access.revoke.RestRevokeResourceAccessAction;
import org.opensearch.security.rest.resources.access.revoke.RevokeResourceAccessAction;
import org.opensearch.security.rest.resources.access.share.RestShareResourceAction;
import org.opensearch.security.rest.resources.access.share.ShareResourceAction;
import org.opensearch.security.rest.resources.access.verify.RestVerifyResourceAccessAction;
import org.opensearch.security.rest.resources.access.verify.VerifyResourceAccessAction;
import org.opensearch.security.securityconf.DynamicConfigFactory;
import org.opensearch.security.securityconf.impl.CType;
Expand Down Expand Up @@ -700,14 +697,7 @@ public List<RestHandler> getRestHandlers(
ConfigConstants.OPENSEARCH_RESOURCE_SHARING_ENABLED,
ConfigConstants.OPENSEARCH_RESOURCE_SHARING_ENABLED_DEFAULT
)) {
handlers.addAll(
List.of(
new RestShareResourceAction(),
new RestRevokeResourceAccessAction(),
new RestListAccessibleResourcesAction(),
new RestVerifyResourceAccessAction()
)
);
handlers.add(new RestResourceAccessAction());
}
log.debug("Added {} rest handler(s)", handlers.size());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class Utils {

public final static String LEGACY_PLUGIN_API_ROUTE_PREFIX = LEGACY_PLUGIN_ROUTE_PREFIX + "/api";

public final static String PLUGIN_RESOURCE_ROUTE_PREFIX = PLUGIN_ROUTE_PREFIX + "/resources";

private static final ObjectMapper internalMapper = new ObjectMapper();

public static Map<String, Object> convertJsonToxToStructuredMap(ToXContent jsonContent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* 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.rest.resources.access;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.google.common.collect.ImmutableList;

import org.opensearch.client.node.NodeClient;
import org.opensearch.common.xcontent.LoggingDeprecationHandler;
import org.opensearch.common.xcontent.XContentFactory;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.rest.BaseRestHandler;
import org.opensearch.rest.RestChannel;
import org.opensearch.rest.RestRequest;
import org.opensearch.rest.action.RestToXContentListener;
import org.opensearch.security.resources.RecipientType;
import org.opensearch.security.resources.RecipientTypeRegistry;
import org.opensearch.security.resources.ShareWith;
import org.opensearch.security.rest.resources.access.list.ListAccessibleResourcesAction;
import org.opensearch.security.rest.resources.access.list.ListAccessibleResourcesRequest;
import org.opensearch.security.rest.resources.access.revoke.RevokeResourceAccessAction;
import org.opensearch.security.rest.resources.access.revoke.RevokeResourceAccessRequest;
import org.opensearch.security.rest.resources.access.share.ShareResourceAction;
import org.opensearch.security.rest.resources.access.share.ShareResourceRequest;
import org.opensearch.security.rest.resources.access.verify.VerifyResourceAccessAction;
import org.opensearch.security.rest.resources.access.verify.VerifyResourceAccessRequest;

import static org.opensearch.rest.RestRequest.Method.GET;
import static org.opensearch.rest.RestRequest.Method.POST;
import static org.opensearch.security.dlic.rest.api.Responses.badRequest;
import static org.opensearch.security.dlic.rest.support.Utils.PLUGIN_RESOURCE_ROUTE_PREFIX;
import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix;

public class RestResourceAccessAction extends BaseRestHandler {

public RestResourceAccessAction() {}

@Override
public List<Route> routes() {
return addRoutesPrefix(
ImmutableList.of(
new Route(GET, "/list/{resourceIndex}"),
new Route(POST, "/revoke"),
new Route(POST, "/share"),
new Route(POST, "/verify_access")
),
PLUGIN_RESOURCE_ROUTE_PREFIX
);
}

@Override
public String getName() {
return "resource_access_action";
}

@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
consumeParams(request); // to avoid 400s
String path = request.path().split(PLUGIN_RESOURCE_ROUTE_PREFIX)[1].split("/")[1];
return switch (path) {
case "list" -> channel -> handleListRequest(request, client, channel);
case "revoke" -> channel -> handleRevokeRequest(request, client, channel);
case "share" -> channel -> handleShareRequest(request, client, channel);
case "verify_access" -> channel -> handleVerifyRequest(request, client, channel);
default -> channel -> badRequest(channel, "Unknown route: " + path);
};
}

private void consumeParams(RestRequest request) {
request.param("resourceIndex", "");
}

public void handleListRequest(RestRequest request, NodeClient client, RestChannel channel) {
String resourceIndex = request.param("resourceIndex", "");
final ListAccessibleResourcesRequest listAccessibleResourcesRequest = new ListAccessibleResourcesRequest(resourceIndex);
client.executeLocally(
ListAccessibleResourcesAction.INSTANCE,
listAccessibleResourcesRequest,
new RestToXContentListener<>(channel)
);

}

public void handleRevokeRequest(RestRequest request, NodeClient client, RestChannel channel) throws IOException {
Map<String, Object> source;
try (XContentParser parser = request.contentParser()) {
source = parser.map();
}

String resourceId = (String) source.get("resource_id");
String resourceIndex = (String) source.get("resource_index");
@SuppressWarnings("unchecked")
Map<String, Set<String>> revokeSource = (Map<String, Set<String>>) source.get("entities");
Map<RecipientType, Set<String>> revoke = revokeSource.entrySet()
.stream()
.collect(Collectors.toMap(entry -> RecipientTypeRegistry.fromValue(entry.getKey()), Map.Entry::getValue));
@SuppressWarnings("unchecked")
Set<String> scopes = new HashSet<>(source.containsKey("scopes") ? (List<String>) source.get("scopes") : List.of());
final RevokeResourceAccessRequest revokeResourceAccessRequest = new RevokeResourceAccessRequest(
resourceId,
resourceIndex,
revoke,
scopes
);
client.executeLocally(RevokeResourceAccessAction.INSTANCE, revokeResourceAccessRequest, new RestToXContentListener<>(channel));
}

public void handleShareRequest(RestRequest request, NodeClient client, RestChannel channel) throws IOException {
Map<String, Object> source;
try (XContentParser parser = request.contentParser()) {
source = parser.map();
}

String resourceId = (String) source.get("resource_id");
String resourceIndex = (String) source.get("resource_index");

ShareWith shareWith = parseShareWith(source);
final ShareResourceRequest shareResourceRequest = new ShareResourceRequest(resourceId, resourceIndex, shareWith);
client.executeLocally(ShareResourceAction.INSTANCE, shareResourceRequest, new RestToXContentListener<>(channel));
}

public void handleVerifyRequest(RestRequest request, NodeClient client, RestChannel channel) throws IOException {
Map<String, Object> source;
try (XContentParser parser = request.contentParser()) {
source = parser.map();
}

String resourceId = (String) source.get("resource_id");
String resourceIndex = (String) source.get("resource_index");
String scope = (String) source.get("scope");

final VerifyResourceAccessRequest verifyResourceAccessRequest = new VerifyResourceAccessRequest(resourceId, resourceIndex, scope);
client.executeLocally(VerifyResourceAccessAction.INSTANCE, verifyResourceAccessRequest, new RestToXContentListener<>(channel));
}

private ShareWith parseShareWith(Map<String, Object> source) throws IOException {
@SuppressWarnings("unchecked")
Map<String, Object> shareWithMap = (Map<String, Object>) source.get("share_with");
if (shareWithMap == null || shareWithMap.isEmpty()) {
throw new IllegalArgumentException("share_with is required and cannot be empty");
}

String jsonString = XContentFactory.jsonBuilder().map(shareWithMap).toString();

try (
XContentParser parser = XContentType.JSON.xContent()
.createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, jsonString)
) {
return ShareWith.fromXContent(parser);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid share_with structure: " + e.getMessage(), e);
}
}
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit ba6928a

Please sign in to comment.