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)); }