Skip to content

Commit

Permalink
threat intel alert model and crud operations
Browse files Browse the repository at this point in the history
Signed-off-by: Surya Sashank Nistala <[email protected]>
  • Loading branch information
eirsep committed Jun 24, 2024
1 parent 703787b commit 89e85f9
Show file tree
Hide file tree
Showing 7 changed files with 437 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.opensearch.securityanalytics.model.threatintel;

import org.apache.commons.lang3.StringUtils;
import org.opensearch.commons.alerting.model.BaseAlert;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.ToXContent;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
package org.opensearch.securityanalytics.model.threatintel;

import org.opensearch.commons.alerting.model.ActionExecutionResult;
import org.opensearch.commons.alerting.model.Alert;
import org.opensearch.commons.authuser.User;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.core.xcontent.XContentParserUtils;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.opensearch.securityanalytics.util.XContentUtils.getInstant;

public class ThreatIntelAlert extends BaseEntity {

public static final String ALERT_ID_FIELD = "id";
public static final String SCHEMA_VERSION_FIELD = "schema_version";
public static final String ALERT_VERSION_FIELD = "version";
public static final String USER_FIELD = "user";
public static final String TRIGGER_NAME_FIELD = "trigger_id";
public static final String TRIGGER_ID_FIELD = "trigger_name";
public static final String STATE_FIELD = "state";
public static final String START_TIME_FIELD = "start_time";
public static final String END_TIME_FIELD = "end_time";
public static final String ACKNOWLEDGED_TIME_FIELD = "acknowledged_time";
public static final String ERROR_MESSAGE_FIELD = "error_message";
public static final String SEVERITY_FIELD = "severity";
public static final String ACTION_EXECUTION_RESULTS_FIELD = "action_execution_results";
public static final String NO_ID = "";
public static final long NO_VERSION = 1L;
private static final long NO_SCHEMA_VERSION = 0;

private final String id;
private final long version;
private final long schemaVersion;
private final User user;
private final String triggerName;
private final String triggerId;
private final Alert.State state;
private final Instant startTime;
private final Instant endTime;
private final Instant acknowledgedTime;
private final String errorMessage;
private final String severity;
private final List<ActionExecutionResult> actionExecutionResults;

public ThreatIntelAlert(
String id,
long version,
long schemaVersion,
User user,
String triggerId,
String triggerName,
Alert.State state,
Instant startTime,
Instant endTime,
Instant acknowledgedTime,
String errorMessage,
String severity,
List<ActionExecutionResult> actionExecutionResults) {

this.id = id != null ? id : NO_ID;
this.version = version != 0 ? version : NO_VERSION;
this.schemaVersion = schemaVersion;
this.user = user;
this.triggerId = triggerId;
this.triggerName = triggerName;
this.state = state;
this.startTime = startTime;
this.endTime = endTime;
this.acknowledgedTime = acknowledgedTime;
this.errorMessage = errorMessage;
this.severity = severity;
this.actionExecutionResults = actionExecutionResults;
}

public ThreatIntelAlert(StreamInput sin) throws IOException {
this.id = sin.readString();
this.version = sin.readLong();
this.schemaVersion = sin.readLong();
this.user = sin.readBoolean() ? new User(sin) : null;
this.triggerId = sin.readString();
this.triggerName = sin.readString();
this.state = sin.readEnum(Alert.State.class);
this.startTime = sin.readInstant();
this.endTime = sin.readOptionalInstant();
this.acknowledgedTime = sin.readOptionalInstant();
this.errorMessage = sin.readOptionalString();
this.severity = sin.readString();
this.actionExecutionResults = sin.readList(ActionExecutionResult::new);
}

public boolean isAcknowledged() {
return state == Alert.State.ACKNOWLEDGED;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(id);
out.writeLong(version);
out.writeLong(schemaVersion);
out.writeBoolean(user != null);
if (user != null) {
user.writeTo(out);
}
out.writeString(triggerId);
out.writeString(triggerName);
out.writeEnum(state);
out.writeInstant(startTime);
out.writeOptionalInstant(endTime);
out.writeOptionalInstant(acknowledgedTime);
out.writeOptionalString(errorMessage);
out.writeString(severity);
out.writeCollection(actionExecutionResults);
}

public static ThreatIntelAlert parse(XContentParser xcp, long version) throws IOException {
String id = NO_ID;
long schemaVersion = NO_SCHEMA_VERSION;
User user = null;
String triggerId = null;
String triggerName = null;
Alert.State state = null;
Instant startTime = null;
String severity = null;
Instant endTime = null;
Instant acknowledgedTime = null;
String errorMessage = null;
List<ActionExecutionResult> actionExecutionResults = new ArrayList<>();

while (xcp.nextToken() != XContentParser.Token.END_OBJECT) {
String fieldName = xcp.currentName();
xcp.nextToken();
switch (fieldName) {
case USER_FIELD:
user = xcp.currentToken() == XContentParser.Token.VALUE_NULL ? null : User.parse(xcp);
break;
case ALERT_ID_FIELD:
id = xcp.text();
break;
case ALERT_VERSION_FIELD:
version = xcp.longValue();
break;
case SCHEMA_VERSION_FIELD:
schemaVersion = xcp.intValue();
break;
case TRIGGER_ID_FIELD:
triggerId = xcp.text();
break;
case TRIGGER_NAME_FIELD:
triggerName = xcp.text();
break;
case STATE_FIELD:
state = Alert.State.valueOf(xcp.text());
break;
case ERROR_MESSAGE_FIELD:
errorMessage = xcp.textOrNull();
break;
case SEVERITY_FIELD:
severity = xcp.text();
break;
case ACTION_EXECUTION_RESULTS_FIELD:
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, xcp.currentToken(), xcp);
while (xcp.nextToken() != XContentParser.Token.END_ARRAY) {
actionExecutionResults.add(ActionExecutionResult.parse(xcp));
}
break;
case START_TIME_FIELD:
startTime = getInstant(xcp);
break;
case END_TIME_FIELD:
endTime = getInstant(xcp);
break;
case ACKNOWLEDGED_TIME_FIELD:
acknowledgedTime = getInstant(xcp);
break;
}
}

return new ThreatIntelAlert(id, version, schemaVersion, user, triggerId, triggerName, state, startTime, endTime, acknowledgedTime, errorMessage, severity, actionExecutionResults);
}

public static Alert readFrom(StreamInput sin) throws IOException {
return new Alert(sin);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return createXContentBuilder(builder, true);
}

@Override
public String getId() {
return id;
}

public XContentBuilder toXContentWithUser(XContentBuilder builder) throws IOException {
return createXContentBuilder(builder, false);
}

private XContentBuilder createXContentBuilder(XContentBuilder builder, boolean secure) throws IOException {
builder.startObject();
builder.field(ALERT_ID_FIELD, id);
builder.field(ALERT_VERSION_FIELD, version);
builder.field(SCHEMA_VERSION_FIELD, schemaVersion);
builder.field(TRIGGER_NAME_FIELD, triggerName);
builder.field(TRIGGER_ID_FIELD, triggerName);
builder.field(STATE_FIELD, state);
builder.field(ERROR_MESSAGE_FIELD, errorMessage);
builder.field(SEVERITY_FIELD, severity);
builder.field(ACTION_EXECUTION_RESULTS_FIELD, actionExecutionResults.toArray());
builder.field(START_TIME_FIELD, startTime);
builder.field(END_TIME_FIELD, endTime);
builder.field(ACKNOWLEDGED_TIME_FIELD, acknowledgedTime);
if (!secure) {
if (user == null) {
builder.nullField(USER_FIELD);
} else {
builder.field(USER_FIELD, user);
}
}
return builder.endObject();
}

public Map<String, Object> asTemplateArg() {
Map<String, Object> map = new HashMap<>();
map.put(ACKNOWLEDGED_TIME_FIELD, acknowledgedTime != null ? acknowledgedTime.toEpochMilli() : null);
map.put(ALERT_ID_FIELD, id);
map.put(ALERT_VERSION_FIELD, version);
map.put(END_TIME_FIELD, endTime != null ? endTime.toEpochMilli() : null);
map.put(ERROR_MESSAGE_FIELD, errorMessage);
map.put(SEVERITY_FIELD, severity);
map.put(START_TIME_FIELD, startTime.toEpochMilli());
map.put(STATE_FIELD, state.toString());
map.put(TRIGGER_ID_FIELD, triggerId);
map.put(TRIGGER_NAME_FIELD, triggerName);
return map;
}

public long getVersion() {
return version;
}

public long getSchemaVersion() {
return schemaVersion;
}

public User getUser() {
return user;
}

public String getTriggerName() {
return triggerName;
}

public Alert.State getState() {
return state;
}

public Instant getStartTime() {
return startTime;
}

public Instant getEndTime() {
return endTime;
}

public Instant getAcknowledgedTime() {
return acknowledgedTime;
}

public String getErrorMessage() {
return errorMessage;
}

public String getSeverity() {
return severity;
}

public List<ActionExecutionResult> getActionExecutionResults() {
return actionExecutionResults;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void createIndexIfNotExists(final ActionListener<Void> listener) {
.settings(getIndexSettings());
client.admin().indices().create(createIndexRequest, ActionListener.wrap(
r -> {
log.debug("Ioc match index created");
log.debug("{} index created", getIndexName());
listener.onResponse(null);
}, e -> {
if (e instanceof ResourceAlreadyExistsException) {
Expand All @@ -135,7 +135,7 @@ public void createIndexIfNotExists(final ActionListener<Void> listener) {
}
));
} catch (Exception e) {
log.error("Failure in creating ioc_match index", e);
log.error(String.format("Failure in creating %s index", getIndexName()), e);
listener.onFailure(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.opensearch.securityanalytics.threatIntel.iocscan.dao;

import org.opensearch.client.Client;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.securityanalytics.model.threatintel.ThreatIntelAlert;

public class ThreatIntelAlertService extends BaseEntityCrudService<ThreatIntelAlert> {

public ThreatIntelAlertService(Client client, ClusterService clusterService, NamedXContentRegistry xContentRegistry) {
super(client, clusterService, xContentRegistry);
}

@Override
protected String getIndexMapping() {
return null; //TODO
}

@Override
protected String getIndexName() {
return ".opensearch-sap-threat-intel-alerts";
}

@Override
public String getEntityName() {
return "threat_intel_alert";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.opensearch.securityanalytics.util;

import java.io.IOException;
import java.time.Instant;
import java.util.Map;

import org.opensearch.common.io.stream.BytesStreamOutput;
Expand All @@ -16,6 +17,8 @@
import org.opensearch.core.common.io.stream.Writeable;
import org.opensearch.core.xcontent.MediaTypeRegistry;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.core.xcontent.XContentParserUtils;

public class XContentUtils {

Expand All @@ -37,4 +40,17 @@ public static BytesReference getBytesReference(Writeable writeable) throws IOExc
return bytes;
}

public static Instant getInstant(XContentParser xcp) throws IOException {
Instant lastUpdateTime;
if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) {
lastUpdateTime = null;
} else if (xcp.currentToken().isValue()) {
lastUpdateTime = Instant.ofEpochMilli(xcp.longValue());
} else {
XContentParserUtils.throwUnknownToken(xcp.currentToken(), xcp.getTokenLocation());
lastUpdateTime = null;
}
return lastUpdateTime;
}

}
Loading

0 comments on commit 89e85f9

Please sign in to comment.