Skip to content

Commit

Permalink
GetAllRuleCategories API (opensearch-project#327)
Browse files Browse the repository at this point in the history
Signed-off-by: Petar Dzepina <[email protected]>
  • Loading branch information
petardz committed Feb 22, 2023
1 parent ab6c28d commit 38175a5
Show file tree
Hide file tree
Showing 9 changed files with 327 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.opensearch.securityanalytics.action.CreateIndexMappingsAction;
import org.opensearch.securityanalytics.action.DeleteDetectorAction;
import org.opensearch.securityanalytics.action.GetAlertsAction;
import org.opensearch.securityanalytics.action.GetAllRuleCategoriesAction;
import org.opensearch.securityanalytics.action.GetDetectorAction;
import org.opensearch.securityanalytics.action.GetFindingsAction;
import org.opensearch.securityanalytics.action.GetIndexMappingsAction;
Expand All @@ -45,10 +46,12 @@
import org.opensearch.securityanalytics.action.ValidateRulesAction;
import org.opensearch.securityanalytics.mapper.MapperService;
import org.opensearch.securityanalytics.resthandler.RestAcknowledgeAlertsAction;
import org.opensearch.securityanalytics.resthandler.RestGetAllRuleCategoriesAction;
import org.opensearch.securityanalytics.resthandler.RestGetFindingsAction;
import org.opensearch.securityanalytics.resthandler.RestValidateRulesAction;
import org.opensearch.securityanalytics.transport.TransportAcknowledgeAlertsAction;
import org.opensearch.securityanalytics.transport.TransportCreateIndexMappingsAction;
import org.opensearch.securityanalytics.transport.TransportGetAllRuleCategoriesAction;
import org.opensearch.securityanalytics.transport.TransportGetFindingsAction;
import org.opensearch.securityanalytics.action.DeleteRuleAction;
import org.opensearch.securityanalytics.action.IndexRuleAction;
Expand Down Expand Up @@ -154,7 +157,8 @@ public List<RestHandler> getRestHandlers(Settings settings,
new RestIndexRuleAction(),
new RestSearchRuleAction(),
new RestDeleteRuleAction(),
new RestValidateRulesAction()
new RestValidateRulesAction(),
new RestGetAllRuleCategoriesAction()
);
}

Expand Down Expand Up @@ -204,7 +208,8 @@ public List<Setting<?>> getSettings() {
new ActionPlugin.ActionHandler<>(IndexRuleAction.INSTANCE, TransportIndexRuleAction.class),
new ActionPlugin.ActionHandler<>(SearchRuleAction.INSTANCE, TransportSearchRuleAction.class),
new ActionPlugin.ActionHandler<>(DeleteRuleAction.INSTANCE, TransportDeleteRuleAction.class),
new ActionPlugin.ActionHandler<>(ValidateRulesAction.INSTANCE, TransportValidateRulesAction.class)
new ActionPlugin.ActionHandler<>(ValidateRulesAction.INSTANCE, TransportValidateRulesAction.class),
new ActionPlugin.ActionHandler<>(GetAllRuleCategoriesAction.INSTANCE, TransportGetAllRuleCategoriesAction.class)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
package org.opensearch.securityanalytics.action;

import org.opensearch.action.ActionType;

public class GetAllRuleCategoriesAction extends ActionType<GetAllRuleCategoriesResponse> {

public static final GetAllRuleCategoriesAction INSTANCE = new GetAllRuleCategoriesAction();
public static final String NAME = "cluster:admin/opensearch/securityanalytics/rules/categories";

public GetAllRuleCategoriesAction() {
super(NAME, GetAllRuleCategoriesResponse::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
package org.opensearch.securityanalytics.action;

import java.io.IOException;
import org.opensearch.action.ActionRequest;
import org.opensearch.action.ActionRequestValidationException;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;

public class GetAllRuleCategoriesRequest extends ActionRequest {

public GetAllRuleCategoriesRequest() {
super();
}
public GetAllRuleCategoriesRequest(StreamInput sin) throws IOException {
this();
}

@Override
public ActionRequestValidationException validate() {
return null;
}

@Override
public void writeTo(StreamOutput out) throws IOException {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
package org.opensearch.securityanalytics.action;

import java.io.IOException;
import java.util.List;
import org.opensearch.action.ActionResponse;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
import org.opensearch.common.xcontent.ToXContentObject;
import org.opensearch.common.xcontent.XContentBuilder;
import org.opensearch.securityanalytics.model.RuleCategory;

public class GetAllRuleCategoriesResponse extends ActionResponse implements ToXContentObject {

private static final String RULE_CATEGORIES = "rule_categories";

private List<RuleCategory> ruleCategories;

public GetAllRuleCategoriesResponse(List<RuleCategory> ruleCategories) {
super();
this.ruleCategories = ruleCategories;
}

public GetAllRuleCategoriesResponse(StreamInput sin) throws IOException {
this(sin.readList(RuleCategory::new));
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeList(ruleCategories);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startArray(RULE_CATEGORIES);
for (RuleCategory c : ruleCategories) {
c.toXContent(builder, null);
}
builder.endArray();
return builder.endObject();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.securityanalytics.model;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.opensearch.OpenSearchParseException;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
import org.opensearch.common.io.stream.Writeable;
import org.opensearch.common.settings.SettingsException;
import org.opensearch.common.xcontent.ToXContentObject;
import org.opensearch.common.xcontent.XContentBuilder;
import org.opensearch.common.xcontent.XContentHelper;
import org.opensearch.common.xcontent.json.JsonXContent;

public class RuleCategory implements Writeable, ToXContentObject {

public static final String KEY = "key";
public static final String DISPLAY_NAME = "display_name";

private String name;
private String displayName;

public RuleCategory(StreamInput sin) throws IOException {
this(sin.readString(), sin.readString());
}

public RuleCategory(String name, String displayName) {
this.name = name;
this.displayName = displayName;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeString(displayName);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field(KEY, name)
.field(DISPLAY_NAME, displayName)
.endObject();
}

public String getName() {
return name;
}


private static final String RULE_CATEGORIES_CONFIG_FILE = "rules/rule_categories.json";

// Rule category is the same as detector type
public static final List<RuleCategory> ALL_RULE_CATEGORIES;

static {
List<RuleCategory> ruleCategories = new ArrayList<>();
String ruleCategoriesJson;
try (
InputStream is = RuleCategory.class.getClassLoader().getResourceAsStream(RULE_CATEGORIES_CONFIG_FILE)
) {
ruleCategoriesJson = new String(Objects.requireNonNull(is).readAllBytes(), StandardCharsets.UTF_8);

if (ruleCategoriesJson != null) {
Map<String, Object> configMap =
XContentHelper.convertToMap(JsonXContent.jsonXContent, ruleCategoriesJson, false);
List<Map<String, Object>> categories = (List<Map<String, Object>>) configMap.get("rule_categories");
for (Map<String, Object> c : categories) {
ruleCategories.add(new RuleCategory(
(String) c.get(KEY),
(String) c.get(DISPLAY_NAME)
));
}
}
} catch (OpenSearchParseException e) {
throw e;
} catch (Exception e) {
throw new SettingsException("Failed to load settings from [" + RULE_CATEGORIES_CONFIG_FILE + "]", e);
}
ALL_RULE_CATEGORIES = Collections.unmodifiableList(ruleCategories);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
package org.opensearch.securityanalytics.resthandler;

import java.io.IOException;
import java.util.List;
import org.opensearch.client.node.NodeClient;
import org.opensearch.rest.BaseRestHandler;
import org.opensearch.rest.RestRequest;
import org.opensearch.rest.action.RestToXContentListener;
import org.opensearch.securityanalytics.SecurityAnalyticsPlugin;
import org.opensearch.securityanalytics.action.GetAllRuleCategoriesAction;
import org.opensearch.securityanalytics.action.GetAllRuleCategoriesRequest;


import static org.opensearch.rest.RestRequest.Method.GET;

public class RestGetAllRuleCategoriesAction extends BaseRestHandler {

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

@Override
public List<Route> routes() {
return List.of(new Route(GET, SecurityAnalyticsPlugin.RULE_BASE_URI + "/categories"));
}

@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {

return channel -> client.execute(
GetAllRuleCategoriesAction.INSTANCE,
new GetAllRuleCategoriesRequest(),
new RestToXContentListener<>(channel)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
package org.opensearch.securityanalytics.transport;

import org.opensearch.action.ActionListener;
import org.opensearch.action.support.ActionFilters;
import org.opensearch.action.support.HandledTransportAction;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.common.inject.Inject;
import org.opensearch.securityanalytics.action.GetAllRuleCategoriesAction;
import org.opensearch.securityanalytics.action.GetAllRuleCategoriesRequest;
import org.opensearch.securityanalytics.action.GetAllRuleCategoriesResponse;
import org.opensearch.securityanalytics.model.RuleCategory;
import org.opensearch.tasks.Task;
import org.opensearch.threadpool.ThreadPool;
import org.opensearch.transport.TransportService;

public class TransportGetAllRuleCategoriesAction extends HandledTransportAction<GetAllRuleCategoriesRequest, GetAllRuleCategoriesResponse> {

private final ThreadPool threadPool;

@Inject
public TransportGetAllRuleCategoriesAction(
TransportService transportService,
ActionFilters actionFilters,
GetAllRuleCategoriesAction getAllRuleCategoriesAction,
ClusterService clusterService,
ThreadPool threadPool
) {
super(getAllRuleCategoriesAction.NAME, transportService, actionFilters, GetAllRuleCategoriesRequest::new);
this.threadPool = threadPool;
}

@Override
protected void doExecute(Task task, GetAllRuleCategoriesRequest request, ActionListener<GetAllRuleCategoriesResponse> actionListener) {
this.threadPool.getThreadContext().stashContext();
actionListener.onResponse(new GetAllRuleCategoriesResponse(RuleCategory.ALL_RULE_CATEGORIES));
}
}
36 changes: 36 additions & 0 deletions src/main/resources/rules/rule_categories.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"rule_categories": [
{
"key": "ad_ldap",
"display_name": "AD/LDAP"
},
{
"key": "dns",
"display_name": "DNS logs"
},
{
"key": "network",
"display_name": "Network"
},
{
"key": "apache_access",
"display_name": "Apache access logs"
},
{
"key": "cloudtrail",
"display_name": "Cloud Trail logs"
},
{
"key": "s3",
"display_name": "S3 access logs"
},
{
"key": "windows",
"display_name": "Windows logs"
},
{
"key": "linux",
"display_name": "System logs"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -765,4 +765,17 @@ public void testCustomRuleValidation() throws IOException {
assertEquals(rule2createdId, ((List)responseBody.get("nonapplicable_fields")).get(0));
}

public void testGetAllRuleCategories() throws IOException {
Response response = makeRequest(client(), "GET", SecurityAnalyticsPlugin.RULE_BASE_URI + "/categories", Collections.emptyMap(), null);
List<Object> categories = (List<Object>) asMap(response).get("rule_categories");
assertEquals(8, categories.size());
assertTrue(((Map<String, Object>)categories.get(0)).get("key").equals("ad_ldap"));
assertTrue(((Map<String, Object>)categories.get(1)).get("key").equals("dns"));
assertTrue(((Map<String, Object>)categories.get(2)).get("key").equals("network"));
assertTrue(((Map<String, Object>)categories.get(3)).get("key").equals("apache_access"));
assertTrue(((Map<String, Object>)categories.get(4)).get("key").equals("cloudtrail"));
assertTrue(((Map<String, Object>)categories.get(5)).get("key").equals("s3"));
assertTrue(((Map<String, Object>)categories.get(6)).get("key").equals("windows"));
assertTrue(((Map<String, Object>)categories.get(7)).get("key").equals("linux"));
}
}

0 comments on commit 38175a5

Please sign in to comment.