From d648ed364c905cf261750ab8519f3bc3b5707d5a Mon Sep 17 00:00:00 2001 From: Megha Goyal Date: Mon, 4 Dec 2023 07:36:43 -0800 Subject: [PATCH 1/4] Allow deletion of custom log type if custom rule index is missing Signed-off-by: Megha Goyal --- .../TransportDeleteCustomLogTypeAction.java | 66 ++++++++----------- .../resthandler/CustomLogTypeRestApiIT.java | 55 ++++++++++++++++ 2 files changed, 84 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java index a972869c7..b07012699 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java @@ -27,6 +27,7 @@ import org.opensearch.commons.authuser.User; import org.opensearch.core.action.ActionListener; import org.opensearch.core.rest.RestStatus; +import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.builder.SearchSourceBuilder; @@ -195,30 +196,17 @@ public void onResponse(SearchResponse response) { onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted because active rules exist", logType.getId()), RestStatus.BAD_REQUEST)); return; } - - DeleteRequest deleteRequest = new DeleteRequest(LogTypeService.LOG_TYPE_INDEX, logType.getId()) - .setRefreshPolicy(request.getRefreshPolicy()) - .timeout(indexTimeout); - - client.delete(deleteRequest, new ActionListener<>() { - @Override - public void onResponse(DeleteResponse response) { - if (response.status() != RestStatus.OK) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted", logType.getId()), RestStatus.INTERNAL_SERVER_ERROR)); - } - onOperation(response); - } - - @Override - public void onFailure(Exception e) { - onFailures(e); - } - }); + deleteLogType(logType); } @Override public void onFailure(Exception e) { - onFailures(e); + if (e instanceof IndexNotFoundException) { + // let log type deletion to go through if the rule index is missing + deleteLogType(logType); + } else { + onFailures(e); + } } }); } @@ -229,25 +217,29 @@ public void onFailure(Exception e) { } }); } else { - DeleteRequest deleteRequest = new DeleteRequest(LogTypeService.LOG_TYPE_INDEX, logType.getId()) - .setRefreshPolicy(request.getRefreshPolicy()) - .timeout(indexTimeout); + deleteLogType(logType); + } + } - client.delete(deleteRequest, new ActionListener<>() { - @Override - public void onResponse(DeleteResponse response) { - if (response.status() != RestStatus.OK) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted", logType.getId()), RestStatus.INTERNAL_SERVER_ERROR)); - } - onOperation(response); - } + private void deleteLogType(CustomLogType logType) { + DeleteRequest deleteRequest = new DeleteRequest(LogTypeService.LOG_TYPE_INDEX, logType.getId()) + .setRefreshPolicy(request.getRefreshPolicy()) + .timeout(indexTimeout); - @Override - public void onFailure(Exception e) { - onFailures(e); + client.delete(deleteRequest, new ActionListener<>() { + @Override + public void onResponse(DeleteResponse response) { + if (response.status() != RestStatus.OK) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted", logType.getId()), RestStatus.INTERNAL_SERVER_ERROR)); } - }); - } + onOperation(response); + } + + @Override + public void onFailure(Exception e) { + onFailures(e); + } + }); } private void searchDetectors(String logTypeName, ActionListener listener) { @@ -292,7 +284,7 @@ private void onOperation(DeleteResponse response) { } private void onFailures(Exception t) { - log.error(String.format(Locale.ROOT, "Failed to delete detector")); + log.error(String.format(Locale.ROOT, "Failed to delete log type")); if (counter.compareAndSet(false, true)) { finishHim(null, t); } diff --git a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java index df3b99867..72b6a4edb 100644 --- a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java @@ -547,6 +547,61 @@ public void testDeleteCustomLogTypeFailsAsDetectorExist() throws IOException { }); } + @SuppressWarnings("unchecked") + public void testDeleteCustomLogTypeWithRuleIndexMissing() throws IOException { + String index = createTestIndex(randomIndex(), windowsIndexMapping()); + + CustomLogType customLogType = TestHelpers.randomCustomLogType(null, null, null, "Custom"); + Response createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI, Collections.emptyMap(), toHttpEntity(customLogType)); + Assert.assertEquals("Create custom log type failed", RestStatus.CREATED, restStatus(createResponse)); + + Map responseBody = asMap(createResponse); + String logTypeId = responseBody.get("_id").toString(); + Assert.assertEquals(customLogType.getDescription(), ((Map) responseBody.get("logType")).get("description")); + + // Execute CreateMappingsAction to add alias mapping for index + Request createMappingRequest = new Request("POST", SecurityAnalyticsPlugin.MAPPER_BASE_URI); + // both req params and req body are supported + createMappingRequest.setJsonEntity( + "{ \"index_name\":\"" + index + "\"," + + " \"rule_topic\":\"" + customLogType.getName() + "\", " + + " \"partial\":true, " + + " \"alias_mappings\":{}" + + "}" + ); + + Response response = client().performRequest(createMappingRequest); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + String rule = randomRule(); + + createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.RULE_BASE_URI, Collections.singletonMap("category", customLogType.getName()), + new StringEntity(rule), new BasicHeader("Content-Type", "application/json")); + Assert.assertEquals("Create rule failed", RestStatus.CREATED, restStatus(createResponse)); + + responseBody = asMap(createResponse); + String createdId = responseBody.get("_id").toString(); + + DetectorInput input = new DetectorInput("custom log type detector for security analytics", List.of(index), List.of(new DetectorRule(createdId)), + List.of()); + Detector detector = randomDetectorWithInputs(List.of(input), customLogType.getName()); + + + createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.DETECTOR_BASE_URI, Collections.emptyMap(), toHttpEntity(detector)); + Assert.assertEquals("Create detector successful", RestStatus.CREATED, restStatus(createResponse)); + + responseBody = asMap(createResponse); + createdId = responseBody.get("_id").toString(); + + Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.DETECTOR_BASE_URI + "/" + createdId, Collections.emptyMap(), null); + Assert.assertEquals("Delete detector failed", RestStatus.OK, restStatus(deleteResponse)); + + makeRequest(client(), "DELETE", "/.opensearch-sap-custom-rules-config", Collections.emptyMap(), new StringEntity("")); + + deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI + "/" + logTypeId, Collections.emptyMap(), new StringEntity("")); + Assert.assertEquals("Delete custom log type successful", RestStatus.OK, restStatus(deleteResponse)); + } + @SuppressWarnings("unchecked") public void testDeleteCustomLogTypeFailsAsRulesExist() throws IOException { String index = createTestIndex(randomIndex(), windowsIndexMapping()); From 8be2159f8913082f018b25af55ae10b54fb55cd7 Mon Sep 17 00:00:00 2001 From: Megha Goyal Date: Tue, 5 Dec 2023 14:51:30 -0800 Subject: [PATCH 2/4] Allow custom log type name to be updated when custom rule index is missing Signed-off-by: Megha Goyal --- .../TransportDeleteCustomLogTypeAction.java | 75 +++---- .../TransportIndexCustomLogTypeAction.java | 195 +++++++----------- .../securityanalytics/util/RuleIndices.java | 19 ++ .../resthandler/CustomLogTypeRestApiIT.java | 60 +++++- 4 files changed, 186 insertions(+), 163 deletions(-) diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java index b07012699..bb76c2e99 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java @@ -37,16 +37,15 @@ import org.opensearch.securityanalytics.logtype.LogTypeService; import org.opensearch.securityanalytics.model.CustomLogType; import org.opensearch.securityanalytics.model.Detector; -import org.opensearch.securityanalytics.model.Rule; import org.opensearch.securityanalytics.settings.SecurityAnalyticsSettings; import org.opensearch.securityanalytics.util.CustomLogTypeIndices; import org.opensearch.securityanalytics.util.DetectorIndices; +import org.opensearch.securityanalytics.util.RuleIndices; import org.opensearch.securityanalytics.util.SecurityAnalyticsException; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; -import java.io.IOException; import java.util.Locale; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -66,6 +65,8 @@ public class TransportDeleteCustomLogTypeAction extends HandledTransportAction() { - @Override - public void onResponse(SearchResponse response) { - if (response.isTimedOut()) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be deleted", logType.getId()), RestStatus.REQUEST_TIMEOUT)); - return; - } - - if (response.getHits().getTotalHits().value > 0) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted because active rules exist", logType.getId()), RestStatus.BAD_REQUEST)); - return; + if (ruleIndices.ruleIndexExists(false)) { + ruleIndices.searchRules(logType.getName(), new ActionListener<>() { + @Override + public void onResponse(SearchResponse response) { + if (response.isTimedOut()) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be deleted", logType.getId()), RestStatus.REQUEST_TIMEOUT)); + return; + } + + if (response.getHits().getTotalHits().value > 0) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted because active rules exist", logType.getId()), RestStatus.BAD_REQUEST)); + return; + } + deleteLogType(logType); } - deleteLogType(logType); - } - @Override - public void onFailure(Exception e) { - if (e instanceof IndexNotFoundException) { - // let log type deletion to go through if the rule index is missing - deleteLogType(logType); - } else { - onFailures(e); + @Override + public void onFailure(Exception e) { + if (e instanceof IndexNotFoundException) { + // let log type deletion to go through if the rule index is missing + deleteLogType(logType); + } else { + onFailures(e); + } } - } - }); + }); + } else { + log.warn("Custom rule index missing, allowing updation of custom log type {} to go through", logType.getId()); + deleteLogType(logType); + } } @Override @@ -259,23 +267,6 @@ private void searchDetectors(String logTypeName, ActionListener client.search(searchRequest, listener); } - private void searchRules(String logTypeName, ActionListener listener) { - QueryBuilder queryBuilder = - QueryBuilders.nestedQuery("rule", - QueryBuilders.boolQuery().must( - QueryBuilders.matchQuery("rule.category", logTypeName) - ), ScoreMode.Avg); - - SearchRequest searchRequest = new SearchRequest(Rule.CUSTOM_RULES_INDEX) - .source(new SearchSourceBuilder() - .seqNoAndPrimaryTerm(true) - .version(true) - .query(queryBuilder) - .size(0)); - - client.search(searchRequest, listener); - } - private void onOperation(DeleteResponse response) { this.response.set(response); if (counter.compareAndSet(false, true)) { @@ -284,7 +275,7 @@ private void onOperation(DeleteResponse response) { } private void onFailures(Exception t) { - log.error(String.format(Locale.ROOT, "Failed to delete log type")); + log.error(String.format(Locale.ROOT, "Failed to delete log type"), t); if (counter.compareAndSet(false, true)) { finishHim(null, t); } diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java index 137c05243..13f69e939 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java @@ -29,6 +29,7 @@ import org.opensearch.core.action.ActionListener; import org.opensearch.core.rest.RestStatus; import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -43,11 +44,11 @@ import org.opensearch.securityanalytics.logtype.LogTypeService; import org.opensearch.securityanalytics.model.CustomLogType; import org.opensearch.securityanalytics.model.Detector; -import org.opensearch.securityanalytics.model.Rule; import org.opensearch.securityanalytics.settings.SecurityAnalyticsSettings; import org.opensearch.securityanalytics.util.CustomLogTypeIndices; import org.opensearch.securityanalytics.util.DetectorIndices; import org.opensearch.securityanalytics.util.IndexUtils; +import org.opensearch.securityanalytics.util.RuleIndices; import org.opensearch.securityanalytics.util.SecurityAnalyticsException; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; @@ -72,6 +73,8 @@ public class TransportIndexCustomLogTypeAction extends HandledTransportAction sourceMap = response.getHits().getHits()[0].getSourceAsMap(); - CustomLogType existingLogType = new CustomLogType(sourceMap); - existingLogType.setId(request.getCustomLogType().getId()); - existingLogType.setVersion(request.getCustomLogType().getVersion()); + Map sourceMap = response.getHits().getHits()[0].getSourceAsMap(); + CustomLogType existingLogType = new CustomLogType(sourceMap); + existingLogType.setId(request.getCustomLogType().getId()); + existingLogType.setVersion(request.getCustomLogType().getVersion()); - if (existingLogType.getSource().equals("Sigma")) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be updated because source is sigma", logTypeId), RestStatus.BAD_REQUEST)); - } - if (!existingLogType.getName().equals(request.getCustomLogType().getName())) { - - if (detectorIndices.detectorIndexExists()) { - searchDetectors(existingLogType.getName(), new ActionListener<>() { - @Override - public void onResponse(SearchResponse response) { - if (response.isTimedOut()) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be updated", logTypeId), RestStatus.REQUEST_TIMEOUT)); - return; - } - - if (response.getHits().getTotalHits().value > 0) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Name of Log Type with id %s cannot be updated because active detectors exist", logTypeId), RestStatus.BAD_REQUEST)); - return; - } - - searchRules(existingLogType.getName(), new ActionListener<>() { + if (existingLogType.getSource().equals("Sigma")) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be updated because source is sigma", logTypeId), RestStatus.BAD_REQUEST)); + } + if (!existingLogType.getName().equals(request.getCustomLogType().getName())) { + + if (detectorIndices.detectorIndexExists()) { + searchDetectors(existingLogType.getName(), new ActionListener<>() { + @Override + public void onResponse(SearchResponse response) { + if (response.isTimedOut()) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be updated", logTypeId), RestStatus.REQUEST_TIMEOUT)); + return; + } + + if (response.getHits().getTotalHits().value > 0) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Name of Log Type with id %s cannot be updated because active detectors exist", logTypeId), RestStatus.BAD_REQUEST)); + return; + } + + if (ruleIndices.ruleIndexExists(false)) { + ruleIndices.searchRules(existingLogType.getName(), new ActionListener<>() { @Override public void onResponse(SearchResponse response) { if (response.isTimedOut()) { @@ -265,88 +270,23 @@ public void onResponse(SearchResponse response) { onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Name of Log Type with id %s cannot be updated because active rules exist", logTypeId), RestStatus.BAD_REQUEST)); return; } - - try { - request.getCustomLogType().setTags(existingLogType.getTags()); - IndexRequest indexRequest = new IndexRequest(LogTypeService.LOG_TYPE_INDEX) - .setRefreshPolicy(request.getRefreshPolicy()) - .source(request.getCustomLogType().toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS)) - .id(request.getLogTypeId()) - .timeout(indexTimeout); - - client.index(indexRequest, new ActionListener<>() { - @Override - public void onResponse(IndexResponse response) { - if (response.status() != RestStatus.OK) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be updated", logTypeId), RestStatus.INTERNAL_SERVER_ERROR)); - } - onOperation(response, request.getCustomLogType()); - } - - @Override - public void onFailure(Exception e) { - onFailures(e); - } - }); - } catch (IOException e) { - onFailures(e); - } + updateLogType(existingLogType, logTypeId); } @Override public void onFailure(Exception e) { - onFailures(e); + if (e instanceof IndexNotFoundException) { + // let log type update if the rule index is missing + updateLogType(existingLogType, logTypeId); + } else { + onFailures(e); + } } }); + } else { + log.warn("Custom rule index missing, allowing update of custom log type {} to go through", logTypeId); + updateLogType(existingLogType, logTypeId); } - - @Override - public void onFailure(Exception e) { - onFailures(e); - } - }); - } else { - request.getCustomLogType().setTags(existingLogType.getTags()); - IndexRequest indexRequest = new IndexRequest(LogTypeService.LOG_TYPE_INDEX) - .setRefreshPolicy(request.getRefreshPolicy()) - .source(request.getCustomLogType().toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS)) - .id(request.getLogTypeId()) - .timeout(indexTimeout); - - client.index(indexRequest, new ActionListener<>() { - @Override - public void onResponse(IndexResponse response) { - if (response.status() != RestStatus.OK) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be updated", logTypeId), RestStatus.INTERNAL_SERVER_ERROR)); - } - - request.getCustomLogType().setId(response.getId()); - onOperation(response, request.getCustomLogType()); - } - - @Override - public void onFailure(Exception e) { - onFailures(e); - } - }); - } - } else { - request.getCustomLogType().setTags(existingLogType.getTags()); - IndexRequest indexRequest = new IndexRequest(LogTypeService.LOG_TYPE_INDEX) - .setRefreshPolicy(request.getRefreshPolicy()) - .source(request.getCustomLogType().toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS)) - .id(request.getLogTypeId()) - .timeout(indexTimeout); - - client.index(indexRequest, new ActionListener<>() { - @Override - public void onResponse(IndexResponse response) { - if (response.status() != RestStatus.OK) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be updated", logTypeId), RestStatus.INTERNAL_SERVER_ERROR)); - } - - request.getCustomLogType().setId(response.getId()); - onOperation(response, request.getCustomLogType()); } @Override @@ -354,9 +294,11 @@ public void onFailure(Exception e) { onFailures(e); } }); + } else { + updateLogType(existingLogType, logTypeId); } - } catch (IOException e) { - onFailures(e); + } else { + updateLogType(existingLogType, logTypeId); } } @@ -455,6 +397,36 @@ public void onFailure(Exception e) { } } + private void updateLogType(CustomLogType existingLogType, String logTypeId) { + try { + request.getCustomLogType().setTags(existingLogType.getTags()); + IndexRequest indexRequest = new IndexRequest(LogTypeService.LOG_TYPE_INDEX) + .setRefreshPolicy(request.getRefreshPolicy()) + .source(request.getCustomLogType().toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS)) + .id(logTypeId) + .timeout(indexTimeout); + + client.index(indexRequest, new ActionListener<>() { + @Override + public void onResponse(IndexResponse response) { + if (response.status() != RestStatus.OK) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be updated", logTypeId), RestStatus.INTERNAL_SERVER_ERROR)); + } + + request.getCustomLogType().setId(response.getId()); + onOperation(response, request.getCustomLogType()); + } + + @Override + public void onFailure(Exception e) { + onFailures(e); + } + }); + } catch (IOException e) { + onFailures(e); + } + } + private void searchLogTypes(String logTypeId, ActionListener listener) { QueryBuilder queryBuilder = QueryBuilders.matchQuery("_id", logTypeId); SearchRequest searchRequest = new SearchRequest(LogTypeService.LOG_TYPE_INDEX) @@ -483,23 +455,6 @@ private void searchDetectors(String logTypeName, ActionListener client.search(searchRequest, listener); } - private void searchRules(String logTypeName, ActionListener listener) { - QueryBuilder queryBuilder = - QueryBuilders.nestedQuery("rule", - QueryBuilders.boolQuery().must( - QueryBuilders.matchQuery("rule.category", logTypeName) - ), ScoreMode.Avg); - - SearchRequest searchRequest = new SearchRequest(Rule.CUSTOM_RULES_INDEX) - .source(new SearchSourceBuilder() - .seqNoAndPrimaryTerm(true) - .version(true) - .query(queryBuilder) - .size(0)); - - client.search(searchRequest, listener); - } - private void onOperation(IndexResponse response, CustomLogType logType) { this.response.set(response); if (counter.compareAndSet(false, true)) { diff --git a/src/main/java/org/opensearch/securityanalytics/util/RuleIndices.java b/src/main/java/org/opensearch/securityanalytics/util/RuleIndices.java index 0cb57e97c..31aa76029 100644 --- a/src/main/java/org/opensearch/securityanalytics/util/RuleIndices.java +++ b/src/main/java/org/opensearch/securityanalytics/util/RuleIndices.java @@ -6,6 +6,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.lucene.search.join.ScoreMode; import org.opensearch.OpenSearchStatusException; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; @@ -31,6 +32,7 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.core.xcontent.ToXContent; import org.opensearch.index.query.BoolQueryBuilder; +import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.reindex.BulkByScrollResponse; import org.opensearch.index.reindex.DeleteByQueryAction; @@ -215,6 +217,23 @@ public void countRules(ActionListener listener) { client.search(request, listener); } + public void searchRules(String logTypeName, ActionListener listener) { + QueryBuilder queryBuilder = + QueryBuilders.nestedQuery("rule", + QueryBuilders.boolQuery().must( + QueryBuilders.matchQuery("rule.category", logTypeName) + ), ScoreMode.Avg); + + SearchRequest searchRequest = new SearchRequest(Rule.CUSTOM_RULES_INDEX) + .source(new SearchSourceBuilder() + .seqNoAndPrimaryTerm(true) + .version(true) + .query(queryBuilder) + .size(0)); + + client.search(searchRequest, listener); + } + private List getRules(List listOfRules) { List rules = new ArrayList<>(); diff --git a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java index 72b6a4edb..55bafc8f7 100644 --- a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java @@ -387,6 +387,64 @@ public void testEditACustomLogTypeNameFailsAsCustomRuleExist() throws IOExceptio }); } + @SuppressWarnings("unchecked") + public void testEditACustomLogTypeNameWhenCustomRuleIndexMissing() throws IOException { + String index = createTestIndex(randomIndex(), windowsIndexMapping()); + + CustomLogType customLogType = TestHelpers.randomCustomLogType(null, null, null, "Custom"); + Response createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI, Collections.emptyMap(), toHttpEntity(customLogType)); + Assert.assertEquals("Create custom log type failed", RestStatus.CREATED, restStatus(createResponse)); + + Map responseBody = asMap(createResponse); + String logTypeId = responseBody.get("_id").toString(); + Assert.assertEquals(customLogType.getDescription(), ((Map) responseBody.get("logType")).get("description")); + + // Execute CreateMappingsAction to add alias mapping for index + Request createMappingRequest = new Request("POST", SecurityAnalyticsPlugin.MAPPER_BASE_URI); + // both req params and req body are supported + createMappingRequest.setJsonEntity( + "{ \"index_name\":\"" + index + "\"," + + " \"rule_topic\":\"" + customLogType.getName() + "\", " + + " \"partial\":true, " + + " \"alias_mappings\":{}" + + "}" + ); + + Response response = client().performRequest(createMappingRequest); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + String rule = randomRule(); + + createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.RULE_BASE_URI, Collections.singletonMap("category", customLogType.getName()), + new StringEntity(rule), new BasicHeader("Content-Type", "application/json")); + Assert.assertEquals("Create rule failed", RestStatus.CREATED, restStatus(createResponse)); + + responseBody = asMap(createResponse); + String createdId = responseBody.get("_id").toString(); + + DetectorInput input = new DetectorInput("custom log type detector for security analytics", List.of(index), List.of(new DetectorRule(createdId)), + List.of()); + Detector detector = randomDetectorWithInputs(List.of(input), customLogType.getName()); + + createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.DETECTOR_BASE_URI, Collections.emptyMap(), toHttpEntity(detector)); + Assert.assertEquals("Create detector successful", RestStatus.CREATED, restStatus(createResponse)); + + responseBody = asMap(createResponse); + createdId = responseBody.get("_id").toString(); + + Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.DETECTOR_BASE_URI + "/" + createdId, Collections.emptyMap(), null); + Assert.assertEquals("Delete detector successful", RestStatus.OK, restStatus(deleteResponse)); + + makeRequest(client(), "DELETE", "/.opensearch-sap-custom-rules-config", Collections.emptyMap(), new StringEntity("")); + + customLogType = TestHelpers.randomCustomLogType("test", null, "Access Management", "Custom"); + Response updatedResponse = makeRequest(client(), "PUT", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI + "/" + logTypeId, Collections.emptyMap(), toHttpEntity(customLogType)); + Assert.assertEquals("Update custom log type successful", RestStatus.OK, restStatus(updatedResponse)); + + responseBody = asMap(updatedResponse); + Assert.assertEquals(customLogType.getCategory(), ((Map) responseBody.get("logType")).get("category")); + } + @SuppressWarnings("unchecked") public void testEditACustomLogTypeName() throws IOException, InterruptedException { String index = createTestIndex(randomIndex(), windowsIndexMapping()); @@ -594,7 +652,7 @@ public void testDeleteCustomLogTypeWithRuleIndexMissing() throws IOException { createdId = responseBody.get("_id").toString(); Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.DETECTOR_BASE_URI + "/" + createdId, Collections.emptyMap(), null); - Assert.assertEquals("Delete detector failed", RestStatus.OK, restStatus(deleteResponse)); + Assert.assertEquals("Delete detector successful", RestStatus.OK, restStatus(deleteResponse)); makeRequest(client(), "DELETE", "/.opensearch-sap-custom-rules-config", Collections.emptyMap(), new StringEntity("")); From cc5c570d43fa20d5d40b028d9b1dfd49c7168104 Mon Sep 17 00:00:00 2001 From: Megha Goyal Date: Fri, 8 Dec 2023 11:11:25 -0800 Subject: [PATCH 3/4] Adding changes for detector index missing Signed-off-by: Megha Goyal --- .../TransportDeleteCustomLogTypeAction.java | 74 ++++++++++--------- .../resthandler/CustomLogTypeRestApiIT.java | 28 +++++++ 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java index bb76c2e99..deedaa055 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteCustomLogTypeAction.java @@ -8,6 +8,7 @@ package org.opensearch.securityanalytics.transport; +import org.apache.commons.logging.Log; import org.apache.lucene.search.join.ScoreMode; import org.opensearch.OpenSearchStatusException; import org.opensearch.action.ActionRunnable; @@ -37,6 +38,7 @@ import org.opensearch.securityanalytics.logtype.LogTypeService; import org.opensearch.securityanalytics.model.CustomLogType; import org.opensearch.securityanalytics.model.Detector; +import org.opensearch.securityanalytics.model.LogType; import org.opensearch.securityanalytics.settings.SecurityAnalyticsSettings; import org.opensearch.securityanalytics.util.CustomLogTypeIndices; import org.opensearch.securityanalytics.util.DetectorIndices; @@ -170,7 +172,8 @@ public void onFailure(Exception e) { private void onGetResponse(CustomLogType logType) { if (logType.getSource().equals("Sigma")) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted because source is sigma", logType.getId()), RestStatus.BAD_REQUEST)); + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), + "Log Type with id %s cannot be deleted because source is sigma", logType.getId()), RestStatus.BAD_REQUEST)); } if (detectorIndices.detectorIndexExists()) { @@ -178,7 +181,8 @@ private void onGetResponse(CustomLogType logType) { @Override public void onResponse(SearchResponse response) { if (response.isTimedOut()) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be deleted", logType.getId()), RestStatus.REQUEST_TIMEOUT)); + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), + "Search request timed out. Log Type with id %s cannot be deleted", logType.getId()), RestStatus.REQUEST_TIMEOUT)); return; } @@ -187,36 +191,7 @@ public void onResponse(SearchResponse response) { return; } - if (ruleIndices.ruleIndexExists(false)) { - ruleIndices.searchRules(logType.getName(), new ActionListener<>() { - @Override - public void onResponse(SearchResponse response) { - if (response.isTimedOut()) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be deleted", logType.getId()), RestStatus.REQUEST_TIMEOUT)); - return; - } - - if (response.getHits().getTotalHits().value > 0) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted because active rules exist", logType.getId()), RestStatus.BAD_REQUEST)); - return; - } - deleteLogType(logType); - } - - @Override - public void onFailure(Exception e) { - if (e instanceof IndexNotFoundException) { - // let log type deletion to go through if the rule index is missing - deleteLogType(logType); - } else { - onFailures(e); - } - } - }); - } else { - log.warn("Custom rule index missing, allowing updation of custom log type {} to go through", logType.getId()); - deleteLogType(logType); - } + checkRuleIndexAndDeleteLogType(logType); } @Override @@ -225,10 +200,43 @@ public void onFailure(Exception e) { } }); } else { - deleteLogType(logType); + checkRuleIndexAndDeleteLogType(logType); } } + void checkRuleIndexAndDeleteLogType(CustomLogType logType) { + if(ruleIndices.ruleIndexExists(false)) { + ruleIndices.searchRules(logType.getName(), new ActionListener<>() { + @Override + public void onResponse(SearchResponse response) { + if (response.isTimedOut()) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be deleted", logType.getId()), RestStatus.REQUEST_TIMEOUT)); + return; + } + + if (response.getHits().getTotalHits().value > 0) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Log Type with id %s cannot be deleted because active rules exist", logType.getId()), RestStatus.BAD_REQUEST)); + return; + } + deleteLogType(logType); + } + + @Override + public void onFailure(Exception e) { + if (e instanceof IndexNotFoundException) { + // let log type deletion to go through if the rule index is missing + deleteLogType(logType); + } else { + onFailures(e); + } + } + }); + } else { + log.warn("Custom rule index missing, allowing deletion of custom log type {} to go through", logType.getId()); + deleteLogType(logType); + } + } + private void deleteLogType(CustomLogType logType) { DeleteRequest deleteRequest = new DeleteRequest(LogTypeService.LOG_TYPE_INDEX, logType.getId()) .setRefreshPolicy(request.getRefreshPolicy()) diff --git a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java index 55bafc8f7..83c1de26e 100644 --- a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java @@ -605,6 +605,34 @@ public void testDeleteCustomLogTypeFailsAsDetectorExist() throws IOException { }); } + @SuppressWarnings("unchecked") + public void testDeleteCustomLogTypeWithDetectorIndexMissing() throws IOException { + String index = createTestIndex(randomIndex(), windowsIndexMapping()); + + CustomLogType customLogType = TestHelpers.randomCustomLogType(null, null, null, "Custom"); + Response createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI, Collections.emptyMap(), toHttpEntity(customLogType)); + Assert.assertEquals("Create custom log type successful", RestStatus.CREATED, restStatus(createResponse)); + + Map responseBody = asMap(createResponse); + String logTypeId = responseBody.get("_id").toString(); + Assert.assertEquals(customLogType.getDescription(), ((Map) responseBody.get("logType")).get("description")); + + String rule = randomRule(); + + createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.RULE_BASE_URI, Collections.singletonMap("category", customLogType.getName()), + new StringEntity(rule), new BasicHeader("Content-Type", "application/json")); + Assert.assertEquals("Create rule successful", RestStatus.CREATED, restStatus(createResponse)); + + responseBody = asMap(createResponse); + String ruleId = responseBody.get("_id").toString(); + + Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.RULE_BASE_URI + "/" + ruleId, Collections.emptyMap(), null); + Assert.assertEquals("Delete rule successful", RestStatus.OK, restStatus(deleteResponse)); + + deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI + "/" + logTypeId, Collections.emptyMap(), new StringEntity("")); + Assert.assertEquals("Delete custom log type successful", RestStatus.OK, restStatus(deleteResponse)); + } + @SuppressWarnings("unchecked") public void testDeleteCustomLogTypeWithRuleIndexMissing() throws IOException { String index = createTestIndex(randomIndex(), windowsIndexMapping()); From 1e31c990ce138834cc92dcf63d0d3a8635e660de Mon Sep 17 00:00:00 2001 From: Megha Goyal Date: Fri, 8 Dec 2023 12:30:00 -0800 Subject: [PATCH 4/4] Update log type when detector index is missing Signed-off-by: Megha Goyal --- .../transport/TransportDeleteRuleAction.java | 1 + .../TransportIndexCustomLogTypeAction.java | 68 +++++++++-------- .../resthandler/CustomLogTypeRestApiIT.java | 75 ++++++++++++++++++- 3 files changed, 109 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteRuleAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteRuleAction.java index 4d9adbb7d..7e0d70e0e 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteRuleAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteRuleAction.java @@ -227,6 +227,7 @@ private void deleteRule(String ruleId) { new DeleteByQueryRequestBuilder(client, DeleteByQueryAction.INSTANCE) .source(Rule.CUSTOM_RULES_INDEX) .filter(QueryBuilders.matchQuery("_id", ruleId)) + .refresh(true) .execute(new ActionListener<>() { @Override public void onResponse(BulkByScrollResponse response) { diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java index 13f69e939..d7eaf495f 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportIndexCustomLogTypeAction.java @@ -256,37 +256,7 @@ public void onResponse(SearchResponse response) { onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Name of Log Type with id %s cannot be updated because active detectors exist", logTypeId), RestStatus.BAD_REQUEST)); return; } - - if (ruleIndices.ruleIndexExists(false)) { - ruleIndices.searchRules(existingLogType.getName(), new ActionListener<>() { - @Override - public void onResponse(SearchResponse response) { - if (response.isTimedOut()) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be updated", logTypeId), RestStatus.REQUEST_TIMEOUT)); - return; - } - - if (response.getHits().getTotalHits().value > 0) { - onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Name of Log Type with id %s cannot be updated because active rules exist", logTypeId), RestStatus.BAD_REQUEST)); - return; - } - updateLogType(existingLogType, logTypeId); - } - - @Override - public void onFailure(Exception e) { - if (e instanceof IndexNotFoundException) { - // let log type update if the rule index is missing - updateLogType(existingLogType, logTypeId); - } else { - onFailures(e); - } - } - }); - } else { - log.warn("Custom rule index missing, allowing update of custom log type {} to go through", logTypeId); - updateLogType(existingLogType, logTypeId); - } + checkRuleIndexAndUpdateLogType(existingLogType, logTypeId); } @Override @@ -295,7 +265,7 @@ public void onFailure(Exception e) { } }); } else { - updateLogType(existingLogType, logTypeId); + checkRuleIndexAndUpdateLogType(existingLogType, logTypeId); } } else { updateLogType(existingLogType, logTypeId); @@ -397,6 +367,40 @@ public void onFailure(Exception e) { } } + void checkRuleIndexAndUpdateLogType(CustomLogType existingLogType, String logTypeId) { + if (ruleIndices.ruleIndexExists(false)) { + ruleIndices.searchRules(existingLogType.getName(), new ActionListener<>() { + @Override + public void onResponse(SearchResponse response) { + if (response.isTimedOut()) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Search request timed out. Log Type with id %s cannot be updated", logTypeId), RestStatus.REQUEST_TIMEOUT)); + return; + } + + if (response.getHits().getTotalHits().value > 0) { + onFailures(new OpenSearchStatusException(String.format(Locale.getDefault(), "Name of Log Type with id %s cannot be updated because active rules exist", logTypeId), RestStatus.BAD_REQUEST)); + return; + } + updateLogType(existingLogType, logTypeId); + } + + @Override + public void onFailure(Exception e) { + if (e instanceof IndexNotFoundException) { + // let log type update if the rule index is missing + updateLogType(existingLogType, logTypeId); + } else { + onFailures(e); + } + } + }); + } else { + log.warn("Custom rule index missing, allowing update of custom log type {} to go through", logTypeId); + updateLogType(existingLogType, logTypeId); + } + + } + private void updateLogType(CustomLogType existingLogType, String logTypeId) { try { request.getCustomLogType().setTags(existingLogType.getTags()); diff --git a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java index 83c1de26e..c5e409e44 100644 --- a/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/resthandler/CustomLogTypeRestApiIT.java @@ -11,6 +11,7 @@ import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicHeader; import org.junit.Assert; +import org.opensearch.action.admin.indices.refresh.RefreshRequest; import org.opensearch.client.Request; import org.opensearch.client.Response; import org.opensearch.client.ResponseException; @@ -32,6 +33,8 @@ import java.util.Map; import static org.opensearch.securityanalytics.TestHelpers.*; +import static org.opensearch.securityanalytics.logtype.LogTypeService.LOG_TYPE_INDEX; +import static org.opensearch.securityanalytics.model.Rule.CUSTOM_RULES_INDEX; public class CustomLogTypeRestApiIT extends SecurityAnalyticsRestTestCase { @@ -445,6 +448,51 @@ public void testEditACustomLogTypeNameWhenCustomRuleIndexMissing() throws IOExce Assert.assertEquals(customLogType.getCategory(), ((Map) responseBody.get("logType")).get("category")); } + public void testEditACustomLogTypeNameWhenDetectorIndexMissing() throws IOException { + String index = createTestIndex(randomIndex(), windowsIndexMapping()); + + CustomLogType customLogType = TestHelpers.randomCustomLogType(null, null, null, "Custom"); + Response createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI, Collections.emptyMap(), toHttpEntity(customLogType)); + Assert.assertEquals("Create custom log type failed", RestStatus.CREATED, restStatus(createResponse)); + + Map responseBody = asMap(createResponse); + String logTypeId = responseBody.get("_id").toString(); + Assert.assertEquals(customLogType.getDescription(), ((Map) responseBody.get("logType")).get("description")); + + // Execute CreateMappingsAction to add alias mapping for index + Request createMappingRequest = new Request("POST", SecurityAnalyticsPlugin.MAPPER_BASE_URI); + // both req params and req body are supported + createMappingRequest.setJsonEntity( + "{ \"index_name\":\"" + index + "\"," + + " \"rule_topic\":\"" + customLogType.getName() + "\", " + + " \"partial\":true, " + + " \"alias_mappings\":{}" + + "}" + ); + + Response response = client().performRequest(createMappingRequest); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + String rule = randomRule(); + + createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.RULE_BASE_URI, Collections.singletonMap("category", customLogType.getName()), + new StringEntity(rule), new BasicHeader("Content-Type", "application/json")); + Assert.assertEquals("Create rule failed", RestStatus.CREATED, restStatus(createResponse)); + + responseBody = asMap(createResponse); + String createdId = responseBody.get("_id").toString(); + + Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.RULE_BASE_URI + "/" + createdId, Collections.emptyMap(), new StringEntity("")); + Assert.assertEquals("Delete rule successful", RestStatus.OK, restStatus(deleteResponse)); + + customLogType = TestHelpers.randomCustomLogType("test", null, "Access Management", "Custom"); + Response updatedResponse = makeRequest(client(), "PUT", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI + "/" + logTypeId, Collections.emptyMap(), toHttpEntity(customLogType)); + Assert.assertEquals("Update custom log type successful", RestStatus.OK, restStatus(updatedResponse)); + + responseBody = asMap(updatedResponse); + Assert.assertEquals(customLogType.getCategory(), ((Map) responseBody.get("logType")).get("category")); + } + @SuppressWarnings("unchecked") public void testEditACustomLogTypeName() throws IOException, InterruptedException { String index = createTestIndex(randomIndex(), windowsIndexMapping()); @@ -495,7 +543,6 @@ public void testEditACustomLogTypeName() throws IOException, InterruptedExceptio deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.RULE_BASE_URI + "/" + ruleId, Collections.emptyMap(), null); Assert.assertEquals("Delete rule failed", RestStatus.OK, restStatus(deleteResponse)); - Thread.sleep(5000); CustomLogType updatedCustomLogType = TestHelpers.randomCustomLogType("updated_name", null, null, "Custom"); Response updatedResponse = makeRequest(client(), "PUT", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI + "/" + logTypeId, Collections.emptyMap(), toHttpEntity(updatedCustomLogType)); @@ -626,9 +673,31 @@ public void testDeleteCustomLogTypeWithDetectorIndexMissing() throws IOException responseBody = asMap(createResponse); String ruleId = responseBody.get("_id").toString(); - Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.RULE_BASE_URI + "/" + ruleId, Collections.emptyMap(), null); + Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.RULE_BASE_URI + "/" + ruleId, Collections.emptyMap(), new StringEntity("")); Assert.assertEquals("Delete rule successful", RestStatus.OK, restStatus(deleteResponse)); - + + String request = "{\n" + + " \"query\": {\n" + + " \"nested\": {\n" + + " \"path\": \"rule\",\n" + + " \"query\": {\n" + + " \"bool\": {\n" + + " \"must\": [\n" + + " { \"match\": {\"rule.category\": \"" + customLogType.getName() + "\"}}\n" + + " ]\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; + + Response searchResponse = makeRequest(client(), "POST", String.format(Locale.getDefault(), "%s/_search", SecurityAnalyticsPlugin.RULE_BASE_URI), Collections.singletonMap("pre_packaged", "false"), + new StringEntity(request), new BasicHeader("Content-Type", "application/json")); + Assert.assertEquals("Searching rules successful", RestStatus.OK, restStatus(searchResponse)); + + responseBody = asMap(searchResponse); + Assert.assertEquals(0, ((Map) ((Map) responseBody.get("hits")).get("total")).get("value")); + deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.CUSTOM_LOG_TYPE_URI + "/" + logTypeId, Collections.emptyMap(), new StringEntity("")); Assert.assertEquals("Delete custom log type successful", RestStatus.OK, restStatus(deleteResponse)); }