Skip to content

Commit

Permalink
Implement simple functionality for get and delete APIs as well
Browse files Browse the repository at this point in the history
Signed-off-by: Derek Ho <[email protected]>
  • Loading branch information
derek-ho committed Nov 20, 2024
1 parent 0b1da6d commit 77e18d9
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,25 @@
import org.opensearch.rest.RestRequest;
import org.opensearch.threadpool.ThreadPool;

import static org.opensearch.rest.RestRequest.Method.DELETE;
import static org.opensearch.rest.RestRequest.Method.GET;
import static org.opensearch.rest.RestRequest.Method.POST;
import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix;

public class ApiTokenAction extends BaseRestHandler {

private ClusterService clusterService;
private ThreadPool threadpool;
private ApiTokenRepository apiTokenRepository;
private final ApiTokenRepository apiTokenRepository;

public static final String NAME_JSON_PROPERTY = "name";

private static final List<RestHandler.Route> ROUTES = addRoutesPrefix(ImmutableList.of(new RestHandler.Route(POST, "/apitokens")));
private static final List<RestHandler.Route> ROUTES = addRoutesPrefix(
ImmutableList.of(
new RestHandler.Route(POST, "/apitokens"),
new RestHandler.Route(DELETE, "/apitokens"),
new RestHandler.Route(GET, "/apitokens")
)
);

public ApiTokenAction(ClusterService clusterService, ThreadPool threadPool, Client client) {
this.clusterService = clusterService;
this.threadpool = threadPool;
this.apiTokenRepository = new ApiTokenRepository(client, clusterService);
}

Expand All @@ -59,15 +62,49 @@ public List<RestHandler.Route> routes() {

@Override
protected RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
// TODO: Authorize this API properly
switch (request.method()) {
case POST:
return handlePost(request, client);

Check warning on line 68 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L68

Added line #L68 was not covered by tests
case DELETE:
return handleDelete(request, client);

Check warning on line 70 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L70

Added line #L70 was not covered by tests
case GET:
return handleGet(request, client);

Check warning on line 72 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L72

Added line #L72 was not covered by tests
default:
throw new IllegalArgumentException(request.method() + " not supported");

Check warning on line 74 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L74

Added line #L74 was not covered by tests
}
}

private RestChannelConsumer handleGet(RestRequest request, NodeClient client) {
return channel -> {
final XContentBuilder builder = channel.newBuilder();

Check warning on line 80 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L79-L80

Added lines #L79 - L80 were not covered by tests
BytesRestResponse response;
try {
List<Map<String, Object>> token = apiTokenRepository.getApiTokens();

Check warning on line 83 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L83

Added line #L83 was not covered by tests

builder.startArray();

Check warning on line 85 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L85

Added line #L85 was not covered by tests
for (int i = 0; i < token.toArray().length; i++) {
// TODO: refactor this to the helper function
builder.startObject();
builder.field("name", token.get(i).get("description"));
builder.field("creation_time", token.get(i).get("creation_time"));
builder.endObject();

Check warning on line 91 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L88-L91

Added lines #L88 - L91 were not covered by tests
}
builder.endArray();

Check warning on line 93 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L93

Added line #L93 was not covered by tests

response = new BytesRestResponse(RestStatus.OK, builder);
} catch (final Exception exception) {
builder.startObject().field("error", "An unexpected error occurred. Please check the input and try again.").endObject();
response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder);
}
builder.close();
channel.sendResponse(response);
};

Check warning on line 102 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L95-L102

Added lines #L95 - L102 were not covered by tests

}

private RestChannelConsumer handlePost(RestRequest request, NodeClient client) {
// TODO: Enforce unique token description
return channel -> {
final XContentBuilder builder = channel.newBuilder();

Check warning on line 109 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L108-L109

Added lines #L108 - L109 were not covered by tests
BytesRestResponse response;
Expand All @@ -92,6 +129,34 @@ private RestChannelConsumer handlePost(RestRequest request, NodeClient client) {

}

private RestChannelConsumer handleDelete(RestRequest request, NodeClient client) {
return channel -> {
final XContentBuilder builder = channel.newBuilder();

Check warning on line 134 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L133-L134

Added lines #L133 - L134 were not covered by tests
BytesRestResponse response;
try {
final Map<String, Object> requestBody = request.contentOrSourceParamParser().map();

Check warning on line 137 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L137

Added line #L137 was not covered by tests

validateRequestParameters(requestBody);
apiTokenRepository.deleteApiToken((String) requestBody.get(NAME_JSON_PROPERTY));

Check warning on line 140 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L139-L140

Added lines #L139 - L140 were not covered by tests

builder.startObject();
builder.field("message", "token " + requestBody.get(NAME_JSON_PROPERTY) + " deleted successfully.");
builder.endObject();

Check warning on line 144 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L142-L144

Added lines #L142 - L144 were not covered by tests

response = new BytesRestResponse(RestStatus.OK, builder);
} catch (final ApiTokenException exception) {
builder.startObject().field("error", exception.getMessage()).endObject();
response = new BytesRestResponse(RestStatus.NOT_FOUND, builder);
} catch (final Exception exception) {
builder.startObject().field("error", "An unexpected error occurred. Please check the input and try again.").endObject();
response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder);
}
builder.close();
channel.sendResponse(response);
};

Check warning on line 156 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L146-L156

Added lines #L146 - L156 were not covered by tests

}

private void validateRequestParameters(Map<String, Object> requestBody) {
if (!requestBody.containsKey(NAME_JSON_PROPERTY)) {
throw new IllegalArgumentException("Name parameter is required and cannot be empty.");

Check warning on line 162 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java#L162

Added line #L162 was not covered by tests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.action.apitokens;

import org.opensearch.OpenSearchException;

public class ApiTokenException extends OpenSearchException {
public ApiTokenException(String message) {
super(message);
}

Check warning on line 19 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenException.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenException.java#L18-L19

Added lines #L18 - L19 were not covered by tests

public ApiTokenException(String message, Throwable cause) {
super(message, cause);
}

Check warning on line 23 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenException.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenException.java#L22-L23

Added lines #L22 - L23 were not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
package org.opensearch.security.action.apitokens;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.google.common.collect.ImmutableMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.opensearch.action.admin.indices.create.CreateIndexRequest;
import org.opensearch.action.get.GetRequest;
import org.opensearch.action.index.IndexRequest;
import org.opensearch.action.index.IndexResponse;
import org.opensearch.action.search.SearchRequest;
import org.opensearch.action.search.SearchResponse;
import org.opensearch.client.Client;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.common.util.concurrent.ThreadContext;
Expand All @@ -30,6 +33,11 @@
import org.opensearch.core.action.ActionListener;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.index.reindex.BulkByScrollResponse;
import org.opensearch.index.reindex.DeleteByQueryAction;
import org.opensearch.index.reindex.DeleteByQueryRequest;
import org.opensearch.search.SearchHit;
import org.opensearch.security.support.ConfigConstants;

public class ApiTokenIndexHandler {
Expand Down Expand Up @@ -67,11 +75,36 @@ public String indexToken(ApiToken token) {

}

public Object getTokens() {
public void deleteToken(String name) throws ApiTokenException {
try (final ThreadContext.StoredContext ctx = client.threadPool().getThreadContext().stashContext()) {
DeleteByQueryRequest request = new DeleteByQueryRequest(ConfigConstants.OPENSEARCH_API_TOKENS_INDEX).setQuery(
QueryBuilders.matchQuery("description", name)
).setRefresh(true); // This will refresh the index after deletion

Check warning on line 82 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L79-L82

Added lines #L79 - L82 were not covered by tests

return client.get(new GetRequest(ConfigConstants.OPENSEARCH_API_TOKENS_INDEX));
BulkByScrollResponse response = client.execute(DeleteByQueryAction.INSTANCE, request).actionGet();

Check warning on line 84 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L84

Added line #L84 was not covered by tests

long deletedDocs = response.getDeleted();

Check warning on line 86 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L86

Added line #L86 was not covered by tests

if (deletedDocs == 0) {
throw new ApiTokenException("No token found with name " + name);

Check warning on line 89 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L89

Added line #L89 was not covered by tests
}
LOGGER.info("Deleted " + deletedDocs + " documents");

Check warning on line 91 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L91

Added line #L91 was not covered by tests
}
}

Check warning on line 93 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L93

Added line #L93 was not covered by tests

public List<Map<String, Object>> getApiTokens() {
try (final ThreadContext.StoredContext ctx = client.threadPool().getThreadContext().stashContext()) {

Check warning on line 96 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L96

Added line #L96 was not covered by tests

SearchRequest searchRequest = new SearchRequest(ConfigConstants.OPENSEARCH_API_TOKENS_INDEX);

Check warning on line 98 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L98

Added line #L98 was not covered by tests

SearchResponse response = client.search(searchRequest).actionGet();

Check warning on line 100 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L100

Added line #L100 was not covered by tests

List<Map<String, Object>> tokens = new ArrayList<>();

Check warning on line 102 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L102

Added line #L102 was not covered by tests
for (SearchHit hit : response.getHits().getHits()) {
tokens.add(hit.getSourceAsMap());

Check warning on line 104 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L104

Added line #L104 was not covered by tests
}

return tokens;

Check warning on line 107 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexHandler.java#L107

Added line #L107 was not covered by tests
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
package org.opensearch.security.action.apitokens;

import java.util.List;
import java.util.Map;

import org.opensearch.client.Client;
import org.opensearch.cluster.service.ClusterService;
Expand All @@ -25,8 +26,17 @@ public ApiTokenRepository(Client client, ClusterService clusterService) {

public String createApiToken(String name) {
apiTokenIndexHandler.createApiTokenIndexIfAbsent();
String token = apiTokenIndexHandler.indexToken(new ApiToken(name, "test-token", List.of()));
return token;
return apiTokenIndexHandler.indexToken(new ApiToken(name, "test-token", List.of()));

Check warning on line 29 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenRepository.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenRepository.java#L28-L29

Added lines #L28 - L29 were not covered by tests
}

public void deleteApiToken(String name) throws ApiTokenException {
apiTokenIndexHandler.createApiTokenIndexIfAbsent();
apiTokenIndexHandler.deleteToken(name);
}

Check warning on line 35 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenRepository.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenRepository.java#L33-L35

Added lines #L33 - L35 were not covered by tests

public List<Map<String, Object>> getApiTokens() {
apiTokenIndexHandler.createApiTokenIndexIfAbsent();
return apiTokenIndexHandler.getApiTokens();

Check warning on line 39 in src/main/java/org/opensearch/security/action/apitokens/ApiTokenRepository.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/action/apitokens/ApiTokenRepository.java#L38-L39

Added lines #L38 - L39 were not covered by tests
}

}

0 comments on commit 77e18d9

Please sign in to comment.