diff --git a/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java b/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java index dfed2464a..cca6fab61 100644 --- a/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java +++ b/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java @@ -67,14 +67,17 @@ import org.opensearch.securityanalytics.model.IocDao; import org.opensearch.securityanalytics.model.ThreatIntelFeedData; import org.opensearch.securityanalytics.resthandler.*; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.dao.SATIFSourceConfigDao; import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfig; +import org.opensearch.securityanalytics.threatIntel.resthandler.RestGetTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.resthandler.RestIndexTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.service.DetectorThreatIntelService; import org.opensearch.securityanalytics.threatIntel.service.SATIFSourceConfigService; import org.opensearch.securityanalytics.threatIntel.service.ThreatIntelFeedDataService; import org.opensearch.securityanalytics.threatIntel.action.PutTIFJobAction; +import org.opensearch.securityanalytics.threatIntel.transport.TransportGetTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.transport.TransportIndexTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.transport.TransportPutTIFJobAction; import org.opensearch.securityanalytics.threatIntel.common.TIFLockService; @@ -189,7 +192,7 @@ public Collection createComponents(Client client, TIFJobParameterService tifJobParameterService = new TIFJobParameterService(client, clusterService); TIFJobUpdateService tifJobUpdateService = new TIFJobUpdateService(clusterService, tifJobParameterService, threatIntelFeedDataService, builtInTIFMetadataLoader); TIFLockService threatIntelLockService = new TIFLockService(clusterService, client); - SaTifSourceConfigDao = new SATIFSourceConfigDao(client, clusterService, threadPool, threatIntelLockService); + SaTifSourceConfigDao = new SATIFSourceConfigDao(client, clusterService, threadPool, xContentRegistry, threatIntelLockService); SATIFSourceConfigService SaTifSourceConfigService = new SATIFSourceConfigService(SaTifSourceConfigDao, threatIntelLockService); @@ -239,7 +242,8 @@ public List getRestHandlers(Settings settings, new RestIndexCustomLogTypeAction(), new RestSearchCustomLogTypeAction(), new RestDeleteCustomLogTypeAction(), - new RestIndexTIFSourceConfigAction() + new RestIndexTIFSourceConfigAction(), + new RestGetTIFSourceConfigAction() ); } @@ -375,7 +379,8 @@ public List> getSettings() { new ActionHandler<>(SearchCustomLogTypeAction.INSTANCE, TransportSearchCustomLogTypeAction.class), new ActionHandler<>(DeleteCustomLogTypeAction.INSTANCE, TransportDeleteCustomLogTypeAction.class), new ActionHandler<>(PutTIFJobAction.INSTANCE, TransportPutTIFJobAction.class), - new ActionHandler<>(SAIndexTIFSourceConfigAction.INSTANCE, TransportIndexTIFSourceConfigAction.class) + new ActionHandler<>(SAIndexTIFSourceConfigAction.INSTANCE, TransportIndexTIFSourceConfigAction.class), + new ActionHandler<>(SAGetTIFSourceConfigAction.INSTANCE, TransportGetTIFSourceConfigAction.class) ); } diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigAction.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigAction.java new file mode 100644 index 000000000..f2a0099e7 --- /dev/null +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigAction.java @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.securityanalytics.threatIntel.action; + +import org.opensearch.action.ActionType; + +import static org.opensearch.securityanalytics.threatIntel.sacommons.IndexTIFSourceConfigAction.GET_TIF_SOURCE_CONFIG_ACTION_NAME; + +/** + * Get TIF Source Config Action + */ +public class SAGetTIFSourceConfigAction extends ActionType { + + public static final SAGetTIFSourceConfigAction INSTANCE = new SAGetTIFSourceConfigAction(); + public static final String NAME = GET_TIF_SOURCE_CONFIG_ACTION_NAME; + private SAGetTIFSourceConfigAction() { + super(NAME, SAGetTIFSourceConfigResponse::new); + } +} diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigRequest.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigRequest.java new file mode 100644 index 000000000..6f64809bd --- /dev/null +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigRequest.java @@ -0,0 +1,61 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.securityanalytics.threatIntel.action; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; +import java.util.Locale; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Get threat intel feed source config request + */ +public class SAGetTIFSourceConfigRequest extends ActionRequest { + private final String id; + private final Long version; + public static final String TIF_SOURCE_CONFIG_ID = "tif_source_config_id"; + + public SAGetTIFSourceConfigRequest(String id, Long version) { + super(); + this.id = id; + this.version = version; + } + + public SAGetTIFSourceConfigRequest(StreamInput sin) throws IOException { + this(sin.readString(), // id + sin.readLong()); // version + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(id); + out.writeLong(version); + } + + public String getId() { + return id; + } + + public Long getVersion() { + return version; + } + + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (id == null || id.isEmpty()) { + validationException = addValidationError(String.format(Locale.getDefault(), "%s is missing", TIF_SOURCE_CONFIG_ID), validationException); + } + return validationException; + } + +} diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigResponse.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigResponse.java new file mode 100644 index 000000000..e239b87af --- /dev/null +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAGetTIFSourceConfigResponse.java @@ -0,0 +1,101 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.opensearch.securityanalytics.threatIntel.action; + +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; + +import java.io.IOException; + +import static org.opensearch.securityanalytics.util.RestHandlerUtils._ID; +import static org.opensearch.securityanalytics.util.RestHandlerUtils._VERSION; + +public class SAGetTIFSourceConfigResponse extends ActionResponse implements ToXContentObject { + private final String id; + + private final Long version; + + private final RestStatus status; + + private final SATIFSourceConfigDto SaTifSourceConfigDto; + + + public SAGetTIFSourceConfigResponse(String id, Long version, RestStatus status, SATIFSourceConfigDto SaTifSourceConfigDto) { + super(); + this.id = id; + this.version = version; + this.status = status; + this.SaTifSourceConfigDto = SaTifSourceConfigDto; + } + + public SAGetTIFSourceConfigResponse(StreamInput sin) throws IOException { + this( + sin.readString(), // id + sin.readLong(), // version + sin.readEnum(RestStatus.class), // status + sin.readBoolean()? SATIFSourceConfigDto.readFrom(sin) : null // SA tif config dto + ); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(id); + out.writeLong(version); + out.writeEnum(status); + if (SaTifSourceConfigDto != null) { + out.writeBoolean((true)); + SaTifSourceConfigDto.writeTo(out); + } else { + out.writeBoolean(false); + } + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject() + .field(_ID, id) + .field(_VERSION, version); + builder.startObject("tif_config") + .field(SATIFSourceConfigDto.FEED_NAME_FIELD, SaTifSourceConfigDto.getName()) + .field(SATIFSourceConfigDto.FEED_FORMAT_FIELD, SaTifSourceConfigDto.getFeedFormat()) + .field(SATIFSourceConfigDto.FEED_TYPE_FIELD, SaTifSourceConfigDto.getFeedType()) + .field(SATIFSourceConfigDto.STATE_FIELD, SaTifSourceConfigDto.getState()) + .field(SATIFSourceConfigDto.ENABLED_TIME_FIELD, SaTifSourceConfigDto.getEnabledTime()) + .field(SATIFSourceConfigDto.ENABLED_FIELD, SaTifSourceConfigDto.isEnabled()) + .field(SATIFSourceConfigDto.CREATED_AT_FIELD, SaTifSourceConfigDto.getCreatedAt()) + .field(SATIFSourceConfigDto.LAST_UPDATE_TIME_FIELD, SaTifSourceConfigDto.getLastUpdateTime()) + .field(SATIFSourceConfigDto.LAST_REFRESHED_TIME_FIELD, SaTifSourceConfigDto.getLastRefreshedTime()) + .field(SATIFSourceConfigDto.REFRESH_TYPE_FIELD, SaTifSourceConfigDto.getRefreshType()) + .field(SATIFSourceConfigDto.LAST_REFRESHED_USER_FIELD, SaTifSourceConfigDto.getLastRefreshedUser()) + .field(SATIFSourceConfigDto.SCHEDULE_FIELD, SaTifSourceConfigDto.getSchedule()) + // source + .field(SATIFSourceConfigDto.CREATED_BY_USER_FIELD, SaTifSourceConfigDto.getCreatedByUser()) + .field(SATIFSourceConfigDto.IOC_MAP_STORE_FIELD, SaTifSourceConfigDto.getIocMapStore()) + .field(SATIFSourceConfigDto.IOC_TYPES_FIELD, SaTifSourceConfigDto.getIocTypes()) + .endObject(); + return builder.endObject(); + } + + public String getId() { + return id; + } + + public Long getVersion() { + return version; + } + + public RestStatus getStatus() { + return status; + } + + public SATIFSourceConfigDto getSaTifSourceConfigDto() { + return SaTifSourceConfigDto; + } +} \ No newline at end of file diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAIndexTIFSourceConfigRequest.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAIndexTIFSourceConfigRequest.java index a9c73d63f..a44a412ae 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAIndexTIFSourceConfigRequest.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/action/SAIndexTIFSourceConfigRequest.java @@ -78,6 +78,10 @@ public WriteRequest.RefreshPolicy getRefreshPolicy() { return refreshPolicy; } + public RestRequest.Method getMethod() { + return method; + } + @Override public ActionRequestValidationException validate() { ActionRequestValidationException errors = new ActionRequestValidationException(); diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/common/RefreshType.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/common/RefreshType.java new file mode 100644 index 000000000..0ac915781 --- /dev/null +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/common/RefreshType.java @@ -0,0 +1,15 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.securityanalytics.threatIntel.common; + +/** + * Refresh Types: Full + * TODO: Add other refresh types such as the delta + */ +public enum RefreshType { + + FULL +} diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/dao/SATIFSourceConfigDao.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/dao/SATIFSourceConfigDao.java index dacac650c..3e3cfa311 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/dao/SATIFSourceConfigDao.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/dao/SATIFSourceConfigDao.java @@ -7,10 +7,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.OpenSearchStatusException; import org.opensearch.ResourceAlreadyExistsException; import org.opensearch.action.StepListener; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexResponse; +import org.opensearch.action.get.GetRequest; +import org.opensearch.action.get.GetResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.support.WriteRequest; @@ -18,17 +21,20 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.xcontent.LoggingDeprecationHandler; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.action.ActionListener; import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.ToXContent; import org.opensearch.jobscheduler.spi.LockModel; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.securityanalytics.SecurityAnalyticsPlugin; -import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigRequest; import org.opensearch.securityanalytics.threatIntel.common.StashedThreadContext; import org.opensearch.securityanalytics.threatIntel.common.TIFLockService; import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfig; -import org.opensearch.securityanalytics.threatIntel.sacommons.IndexTIFSourceConfigResponse; import org.opensearch.securityanalytics.util.SecurityAnalyticsException; import org.opensearch.threadpool.ThreadPool; @@ -48,22 +54,29 @@ public class SATIFSourceConfigDao { private final ClusterService clusterService; private final ClusterSettings clusterSettings; private final ThreadPool threadPool; + private final NamedXContentRegistry xContentRegistry; private final TIFLockService lockService; - - public SATIFSourceConfigDao(final Client client, final ClusterService clusterService, ThreadPool threadPool, final TIFLockService lockService) { + public SATIFSourceConfigDao(final Client client, + final ClusterService clusterService, + ThreadPool threadPool, + NamedXContentRegistry xContentRegistry, + final TIFLockService lockService + ) { this.client = client; this.clusterService = clusterService; this.clusterSettings = clusterService.getClusterSettings(); this.threadPool = threadPool; + this.xContentRegistry = xContentRegistry; this.lockService = lockService; } public void indexTIFSourceConfig(SATIFSourceConfig SaTifSourceConfig, TimeValue indexTimeout, final LockModel lock, - final ActionListener actionListener) { + final ActionListener actionListener + ) { StepListener createIndexStepListener = new StepListener<>(); createIndexStepListener.whenComplete(v -> { try { @@ -83,7 +96,7 @@ public void indexTIFSourceConfig(SATIFSourceConfig SaTifSourceConfig, } }, exception -> { lockService.releaseLock(lock); - log.error("failed to release lock", exception); + log.error("Failed to release lock", exception); actionListener.onFailure(exception); }); createJobIndexIfNotExists(createIndexStepListener); @@ -111,11 +124,6 @@ private static SATIFSourceConfig createSATIFSourceConfig(SATIFSourceConfig SaTif ); } - public ThreadPool getThreadPool() { - return threadPool; - } - - // Get the job config index mapping private String getIndexMapping() { try { @@ -130,7 +138,7 @@ private String getIndexMapping() { } } - // Create Threat intel config index + // Create TIF source config index /** * Index name: .opensearch-sap--job * Mapping: /mappings/threat_intel_job_mapping.json @@ -148,21 +156,61 @@ public void createJobIndexIfNotExists(final StepListener stepListener) { StashedThreadContext.run(client, () -> client.admin().indices().create(createIndexRequest, new ActionListener<>() { @Override public void onResponse(final CreateIndexResponse createIndexResponse) { - log.debug("Job index created"); + log.debug("[{}] index created", SecurityAnalyticsPlugin.JOB_INDEX_NAME); stepListener.onResponse(null); } @Override public void onFailure(final Exception e) { if (e instanceof ResourceAlreadyExistsException) { - log.info("index[{}] already exist", SecurityAnalyticsPlugin.JOB_INDEX_NAME); + log.info("Index [{}] already exists", SecurityAnalyticsPlugin.JOB_INDEX_NAME); stepListener.onResponse(null); return; } - log.error("Failed to create security analytics threat intel source config index", e); + log.error("Failed to create [{}] index", SecurityAnalyticsPlugin.JOB_INDEX_NAME, e); stepListener.onFailure(e); } })); } + + // Get TIF source config + public void getTIFSourceConfig( + String tifSourceConfigId, + Long version, + ActionListener actionListener + ) { + GetRequest getRequest = new GetRequest(SecurityAnalyticsPlugin.JOB_INDEX_NAME, tifSourceConfigId).version(version); + client.get(getRequest, new ActionListener<>() { + @Override + public void onResponse(GetResponse response) { + try { + if (!response.isExists()) { + actionListener.onFailure(SecurityAnalyticsException.wrap(new OpenSearchStatusException("Threat intel source config not found.", RestStatus.NOT_FOUND))); + return; + } + SATIFSourceConfig SaTifSourceConfig = null; + if (!response.isSourceEmpty()) { + XContentParser xcp = XContentHelper.createParser( + xContentRegistry, LoggingDeprecationHandler.INSTANCE, + response.getSourceAsBytesRef(), XContentType.JSON + ); + SaTifSourceConfig = SATIFSourceConfig.docParse(xcp, response.getId(), response.getVersion()); + assert SaTifSourceConfig != null; + } + log.debug("Threat intel source config with id [{}] fetched.", response.getId()); + actionListener.onResponse(SaTifSourceConfig); + } catch (IOException ex) { + log.error("Failed to fetch threat intel source config document", ex); + actionListener.onFailure(ex); + } + } + @Override + public void onFailure(Exception e) { + log.error("Failed to fetch threat intel source config document " + tifSourceConfigId, e); + actionListener.onFailure(e); + } + }); + } + } diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfig.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfig.java index 56cfb7fa2..dc9420381 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfig.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfig.java @@ -20,12 +20,14 @@ import org.opensearch.jobscheduler.spi.schedule.IntervalSchedule; import org.opensearch.jobscheduler.spi.schedule.ScheduleParser; import org.opensearch.securityanalytics.threatIntel.common.FeedType; +import org.opensearch.securityanalytics.threatIntel.common.RefreshType; import org.opensearch.securityanalytics.threatIntel.common.TIFJobState; import org.opensearch.securityanalytics.threatIntel.sacommons.TIFSourceConfig; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -77,7 +79,7 @@ public class SATIFSourceConfig implements TIFSourceConfig, Writeable, ScheduledJ private Instant lastUpdateTime; private IntervalSchedule schedule; private TIFJobState state; - public String refreshType; + public RefreshType refreshType; public Instant lastRefreshedTime; public String lastRefreshedUser; private Boolean isEnabled; @@ -85,7 +87,7 @@ public class SATIFSourceConfig implements TIFSourceConfig, Writeable, ScheduledJ private List iocTypes; public SATIFSourceConfig(String id, Long version, String feedName, String feedFormat, FeedType feedType, String createdByUser, Instant createdAt, - Instant enabledTime, Instant lastUpdateTime, IntervalSchedule schedule, TIFJobState state, String refreshType, Instant lastRefreshedTime, String lastRefreshedUser, + Instant enabledTime, Instant lastUpdateTime, IntervalSchedule schedule, TIFJobState state, RefreshType refreshType, Instant lastRefreshedTime, String lastRefreshedUser, Boolean isEnabled, Map iocMapStore, List iocTypes) { this.id = id != null ? id : NO_ID; this.version = version != null ? version : NO_VERSION; @@ -95,9 +97,9 @@ public SATIFSourceConfig(String id, Long version, String feedName, String feedFo this.createdByUser = createdByUser; this.createdAt = createdAt != null ? createdAt : Instant.now(); - if (this.isEnabled == null && this.enabledTime == null) { + if (isEnabled == null && enabledTime == null) { this.enabledTime = Instant.now(); - } else if (this.isEnabled != null && !this.isEnabled) { + } else if (isEnabled != null && !isEnabled) { this.enabledTime = null; } else { this.enabledTime = enabledTime; @@ -105,14 +107,12 @@ public SATIFSourceConfig(String id, Long version, String feedName, String feedFo this.lastUpdateTime = lastUpdateTime != null ? lastUpdateTime : Instant.now(); this.schedule = schedule; - - this.state = (this.state == null) ? TIFJobState.CREATING : state; - - this.refreshType = refreshType; + this.state = state != null ? state : TIFJobState.CREATING; + this.refreshType = refreshType != null ? refreshType : RefreshType.FULL; this.lastRefreshedTime = lastRefreshedTime; this.lastRefreshedUser = lastRefreshedUser; this.isEnabled = isEnabled; - this.iocMapStore = iocMapStore; + this.iocMapStore = iocMapStore != null ? iocMapStore : new HashMap<>(); this.iocTypes = iocTypes; } @@ -123,13 +123,13 @@ public SATIFSourceConfig(StreamInput sin) throws IOException { sin.readString(), // feed name sin.readString(), // feed format FeedType.valueOf(sin.readString()), // feed type - sin.readString(), // created by user + sin.readOptionalString(), // created by user sin.readInstant(), // created at - sin.readInstant(), // enabled time + sin.readOptionalInstant(), // enabled time sin.readInstant(), // last update time new IntervalSchedule(sin), // schedule TIFJobState.valueOf(sin.readString()), // state - sin.readString(), // refresh type + RefreshType.valueOf(sin.readString()), // state sin.readOptionalInstant(), // last refreshed time sin.readOptionalString(), // last refreshed user sin.readBoolean(), // is enabled @@ -144,15 +144,15 @@ public void writeTo(final StreamOutput out) throws IOException { out.writeString(feedName); out.writeString(feedFormat); out.writeString(feedType.name()); - out.writeString(createdByUser); + out.writeOptionalString(createdByUser); out.writeInstant(createdAt); - out.writeInstant(enabledTime); + out.writeOptionalInstant(enabledTime); out.writeInstant(lastUpdateTime); schedule.writeTo(out); out.writeString(state.name()); - out.writeString(refreshType); - out.writeOptionalInstant(lastRefreshedTime == null ? null : lastRefreshedTime); - out.writeOptionalString(lastRefreshedUser == null? null : lastRefreshedUser); + out.writeString(refreshType.name()); + out.writeOptionalInstant(lastRefreshedTime); + out.writeOptionalString(lastRefreshedUser); out.writeBoolean(isEnabled); out.writeMap(iocMapStore); out.writeStringCollection(iocTypes); @@ -188,7 +188,7 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa builder.field(SCHEDULE_FIELD, schedule); builder.field(STATE_FIELD, state.name()); - builder.field(REFRESH_TYPE_FIELD, refreshType); + builder.field(REFRESH_TYPE_FIELD, refreshType.name()); if (lastRefreshedTime == null) { builder.nullField(LAST_REFRESHED_TIME_FIELD); } else { @@ -204,6 +204,18 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa return builder; } + public static SATIFSourceConfig docParse(XContentParser xcp, String id, Long version) throws IOException { + XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.nextToken(), xcp); + XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, xcp.nextToken(), xcp); + XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.nextToken(), xcp); + SATIFSourceConfig SaTifSourceConfig = parse(xcp, id, version); + XContentParserUtils.ensureExpectedToken(XContentParser.Token.END_OBJECT, xcp.nextToken(), xcp); + + SaTifSourceConfig.setId(id); + SaTifSourceConfig.setVersion(version); + return SaTifSourceConfig; + } + public static SATIFSourceConfig parse(XContentParser xcp, String id, Long version) throws IOException { if (id == null) { id = NO_ID; @@ -221,7 +233,7 @@ public static SATIFSourceConfig parse(XContentParser xcp, String id, Long versio Instant lastUpdateTime = null; IntervalSchedule schedule = null; TIFJobState state = null; - String refreshType = null; + RefreshType refreshType = null; Instant lastRefreshedTime = null; String lastRefreshedUser = null; Boolean isEnabled = null; @@ -296,7 +308,7 @@ public static SATIFSourceConfig parse(XContentParser xcp, String id, Long versio if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) { refreshType = null; } else { - refreshType = xcp.text(); + refreshType = toRefreshType(xcp.text()); } break; case LAST_REFRESHED_TIME_FIELD: @@ -383,6 +395,15 @@ public static FeedType toFeedType(String feedType) { } } + public static RefreshType toRefreshType(String stateName) { + try { + return RefreshType.valueOf(stateName); + } catch (IllegalArgumentException e) { + log.error("Invalid refresh type, cannot be parsed.", e); + return null; + } + } + public static SATIFSourceConfig readFrom(StreamInput sin) throws IOException { return new SATIFSourceConfig(sin); } @@ -466,10 +487,10 @@ public Instant getLastRefreshedTime() { public void setLastRefreshedTime(Instant lastRefreshedTime) { this.lastRefreshedTime = lastRefreshedTime; } - public String getRefreshType() { + public RefreshType getRefreshType() { return refreshType; } - public void setRefreshType(String refreshType) { + public void setRefreshType(RefreshType refreshType) { this.refreshType = refreshType; } public boolean isEnabled() { diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfigDto.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfigDto.java index dfba113ee..89dc80d17 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfigDto.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/model/SATIFSourceConfigDto.java @@ -20,6 +20,7 @@ import org.opensearch.jobscheduler.spi.schedule.IntervalSchedule; import org.opensearch.jobscheduler.spi.schedule.ScheduleParser; import org.opensearch.securityanalytics.threatIntel.common.FeedType; +import org.opensearch.securityanalytics.threatIntel.common.RefreshType; import org.opensearch.securityanalytics.threatIntel.common.TIFJobState; import org.opensearch.securityanalytics.threatIntel.sacommons.TIFSourceConfigDto; @@ -74,7 +75,7 @@ public class SATIFSourceConfigDto implements Writeable, ToXContentObject, TIFSou private Instant lastUpdateTime; private IntervalSchedule schedule; private TIFJobState state; - public String refreshType; + public RefreshType refreshType; public Instant lastRefreshedTime; public String lastRefreshedUser; private Boolean isEnabled; @@ -102,7 +103,7 @@ public SATIFSourceConfigDto(SATIFSourceConfig SaTifSourceConfig) { } public SATIFSourceConfigDto(String id, Long version, String feedName, String feedFormat, FeedType feedType, String createdByUser, Instant createdAt, - Instant enabledTime, Instant lastUpdateTime, IntervalSchedule schedule, TIFJobState state, String refreshType, Instant lastRefreshedTime, String lastRefreshedUser, + Instant enabledTime, Instant lastUpdateTime, IntervalSchedule schedule, TIFJobState state, RefreshType refreshType, Instant lastRefreshedTime, String lastRefreshedUser, Boolean isEnabled, Map iocMapStore, List iocTypes) { this.id = id != null ? id : NO_ID; this.version = version != null ? version : NO_VERSION; @@ -112,9 +113,9 @@ public SATIFSourceConfigDto(String id, Long version, String feedName, String fee this.createdByUser = createdByUser; this.createdAt = createdAt != null ? createdAt : Instant.now(); - if (this.isEnabled == null && this.enabledTime == null) { + if (isEnabled == null && enabledTime == null) { this.enabledTime = Instant.now(); - } else if (this.isEnabled != null && !this.isEnabled) { + } else if (isEnabled != null && !isEnabled) { this.enabledTime = null; } else { this.enabledTime = enabledTime; @@ -122,14 +123,12 @@ public SATIFSourceConfigDto(String id, Long version, String feedName, String fee this.lastUpdateTime = lastUpdateTime != null ? lastUpdateTime : Instant.now(); this.schedule = schedule; - - this.state = (this.state == null) ? TIFJobState.CREATING : state; - - this.refreshType = refreshType; + this.state = state != null ? state : TIFJobState.CREATING; + this.refreshType = refreshType != null ? refreshType : RefreshType.FULL; this.lastRefreshedTime = lastRefreshedTime; this.lastRefreshedUser = lastRefreshedUser; this.isEnabled = isEnabled; - this.iocMapStore = (this.iocMapStore == null) ? new HashMap<>() : iocMapStore; + this.iocMapStore = iocMapStore != null ? iocMapStore : new HashMap<>(); this.iocTypes = iocTypes; } @@ -143,15 +142,15 @@ public void writeTo(final StreamOutput out) throws IOException { out.writeString(feedName); out.writeString(feedFormat); out.writeString(feedType.name()); - out.writeString(createdByUser); + out.writeOptionalString(createdByUser); out.writeInstant(createdAt); - out.writeInstant(enabledTime); + out.writeOptionalInstant(enabledTime); out.writeInstant(lastUpdateTime); schedule.writeTo(out); out.writeString(state.name()); - out.writeOptionalString(refreshType == null? null: refreshType); - out.writeOptionalInstant(lastRefreshedTime == null ? null : lastRefreshedTime); - out.writeOptionalString(lastRefreshedUser == null? null : lastRefreshedUser); + out.writeString(refreshType.name()); + out.writeOptionalInstant(lastRefreshedTime); + out.writeOptionalString(lastRefreshedUser); out.writeBoolean(isEnabled); out.writeMap(iocMapStore); out.writeStringCollection(iocTypes); @@ -187,7 +186,7 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa builder.field(SCHEDULE_FIELD, schedule); builder.field(STATE_FIELD, state.name()); - builder.field(REFRESH_TYPE_FIELD, refreshType); + builder.field(REFRESH_TYPE_FIELD, refreshType.name()); if (lastRefreshedTime == null) { builder.nullField(LAST_REFRESHED_TIME_FIELD); } else { @@ -220,7 +219,7 @@ public static SATIFSourceConfigDto parse(XContentParser xcp, String id, Long ver Instant lastUpdateTime = null; IntervalSchedule schedule = null; TIFJobState state = null; - String refreshType = null; + RefreshType refreshType = null; Instant lastRefreshedTime = null; String lastRefreshedUser = null; Boolean isEnabled = null; @@ -294,7 +293,7 @@ public static SATIFSourceConfigDto parse(XContentParser xcp, String id, Long ver if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) { refreshType = null; } else { - refreshType = xcp.text(); + refreshType = toRefreshType(xcp.text()); } break; case LAST_REFRESHED_TIME_FIELD: @@ -382,6 +381,15 @@ public static FeedType toFeedType(String feedType) { } } + public static RefreshType toRefreshType(String stateName) { + try { + return RefreshType.valueOf(stateName); + } catch (IllegalArgumentException e) { + log.error("Invalid refresh type, cannot be parsed.", e); + return null; + } + } + // Getters and Setters public String getId() { @@ -462,10 +470,10 @@ public Instant getLastRefreshedTime() { public void setLastRefreshedTime(Instant lastRefreshedTime) { this.lastRefreshedTime = lastRefreshedTime; } - public String getRefreshType() { + public RefreshType getRefreshType() { return refreshType; } - public void setRefreshType(String refreshType) { + public void setRefreshType(RefreshType refreshType) { this.refreshType = refreshType; } public boolean isEnabled() { diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestGetTIFSourceConfigAction.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestGetTIFSourceConfigAction.java new file mode 100644 index 000000000..6eb669c92 --- /dev/null +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestGetTIFSourceConfigAction.java @@ -0,0 +1,51 @@ +package org.opensearch.securityanalytics.threatIntel.resthandler; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestActions; +import org.opensearch.rest.action.RestToXContentListener; +import org.opensearch.securityanalytics.SecurityAnalyticsPlugin; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigAction; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigRequest; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import static org.opensearch.rest.RestRequest.Method.GET; + +public class RestGetTIFSourceConfigAction extends BaseRestHandler { + + private static final Logger log = LogManager.getLogger(RestGetTIFSourceConfigAction.class); + + @Override + public String getName() { + return "get_tif_config_action"; + } + + @Override + public List routes() { + return List.of(new Route(GET, String.format(Locale.getDefault(), "%s/{%s}", SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI, SAGetTIFSourceConfigRequest.TIF_SOURCE_CONFIG_ID))); + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + String SaTifSourceConfigId = request.param(SAGetTIFSourceConfigRequest.TIF_SOURCE_CONFIG_ID, SATIFSourceConfigDto.NO_ID); + + if (SaTifSourceConfigId == null || SaTifSourceConfigId.isEmpty()) { + throw new IllegalArgumentException("missing id"); + } + + SAGetTIFSourceConfigRequest req = new SAGetTIFSourceConfigRequest(SaTifSourceConfigId, RestActions.parseVersion(request)); + + return channel -> client.execute( + SAGetTIFSourceConfigAction.INSTANCE, + req, + new RestToXContentListener<>(channel) + ); + } +} diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java index 0545048c4..4e5d15d5c 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java @@ -19,6 +19,7 @@ import org.opensearch.rest.RestResponse; import org.opensearch.rest.action.RestResponseListener; import org.opensearch.securityanalytics.SecurityAnalyticsPlugin; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigRequest; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigRequest; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigResponse; @@ -30,7 +31,6 @@ import java.util.List; import java.util.Locale; - public class RestIndexTIFSourceConfigAction extends BaseRestHandler { private static final Logger log = LogManager.getLogger(RestIndexTIFSourceConfigAction.class); @Override @@ -41,7 +41,8 @@ public String getName() { public List routes() { return List.of( new Route(RestRequest.Method.POST, SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI), - new Route(RestRequest.Method.PUT, SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI + "/{tifConfigId}") + new Route(RestRequest.Method.PUT, String.format(Locale.getDefault(), "%s/{%s}", + SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI, SAGetTIFSourceConfigRequest.TIF_SOURCE_CONFIG_ID)) ); } diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/sacommons/IndexTIFSourceConfigAction.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/sacommons/IndexTIFSourceConfigAction.java index ab358d453..a4f196ea1 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/sacommons/IndexTIFSourceConfigAction.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/sacommons/IndexTIFSourceConfigAction.java @@ -6,6 +6,6 @@ package org.opensearch.securityanalytics.threatIntel.sacommons; public class IndexTIFSourceConfigAction { - public static final String INDEX_TIF_SOURCE_CONFIG_ACTION_NAME = "cluster:admin/security_analytics/tifConfig/write"; - + public static final String INDEX_TIF_SOURCE_CONFIG_ACTION_NAME = "cluster:admin/security_analytics/tifSource/write"; + public static final String GET_TIF_SOURCE_CONFIG_ACTION_NAME = "cluster:admin/security_analytics/tifSource/get"; } diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/service/SATIFSourceConfigService.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/service/SATIFSourceConfigService.java index e2bd0400c..ec0dfb104 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/service/SATIFSourceConfigService.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/service/SATIFSourceConfigService.java @@ -70,6 +70,27 @@ public void onFailure(Exception e) { } } + public void getTIFSourceConfig( + final String SaTifSourceConfigId, + final Long version, + final ActionListener listener + ) { + try { + SaTifSourceConfigDao.getTIFSourceConfig(SaTifSourceConfigId, version, new ActionListener<>() { + @Override + public void onResponse(SATIFSourceConfig SaTifSourceConfig) { + listener.onResponse(SaTifSourceConfig); + } + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } + }); + } catch (Exception e) { + listener.onFailure(e); + } + } + /** * Converts the DTO to entity * @param SaTifSourceConfigDto diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/transport/TransportGetTIFSourceConfigAction.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/transport/TransportGetTIFSourceConfigAction.java new file mode 100644 index 000000000..93dd34ebc --- /dev/null +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/transport/TransportGetTIFSourceConfigAction.java @@ -0,0 +1,84 @@ +package org.opensearch.securityanalytics.threatIntel.transport; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.OpenSearchStatusException; +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.common.settings.Settings; +import org.opensearch.commons.authuser.User; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.securityanalytics.settings.SecurityAnalyticsSettings; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigAction; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigRequest; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigResponse; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfig; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; +import org.opensearch.securityanalytics.threatIntel.service.SATIFSourceConfigService; +import org.opensearch.securityanalytics.transport.SecureTransportAction; +import org.opensearch.tasks.Task; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +public class TransportGetTIFSourceConfigAction extends HandledTransportAction implements SecureTransportAction { + + private static final Logger log = LogManager.getLogger(TransportGetTIFSourceConfigAction.class); + + private final ClusterService clusterService; + + private final Settings settings; + + private final ThreadPool threadPool; + + private volatile Boolean filterByEnabled; + + private final SATIFSourceConfigService SaTifConfigService; + + @Inject + public TransportGetTIFSourceConfigAction(TransportService transportService, + ActionFilters actionFilters, + ClusterService clusterService, + final ThreadPool threadPool, + Settings settings, + final SATIFSourceConfigService SaTifConfigService) { + super(SAGetTIFSourceConfigAction.NAME, transportService, actionFilters, SAGetTIFSourceConfigRequest::new); + this.clusterService = clusterService; + this.threadPool = threadPool; + this.settings = settings; + this.filterByEnabled = SecurityAnalyticsSettings.FILTER_BY_BACKEND_ROLES.get(this.settings); + this.clusterService.getClusterSettings().addSettingsUpdateConsumer(SecurityAnalyticsSettings.FILTER_BY_BACKEND_ROLES, this::setFilterByEnabled); + this.SaTifConfigService = SaTifConfigService; + } + + @Override + protected void doExecute(Task task, SAGetTIFSourceConfigRequest request, ActionListener actionListener) { + // validate user + User user = readUserFromThreadContext(this.threadPool); + String validateBackendRoleMessage = validateUserBackendRoles(user, this.filterByEnabled); + if (!"".equals(validateBackendRoleMessage)) { + actionListener.onFailure(new OpenSearchStatusException("Do not have permissions to resource", RestStatus.FORBIDDEN)); + return; + } + + SaTifConfigService.getTIFSourceConfig(request.getId(), request.getVersion(), new ActionListener<>() { + @Override + public void onResponse(SATIFSourceConfig SaTifSourceConfig) { + SATIFSourceConfigDto SaTifSourceConfigDto = new SATIFSourceConfigDto(SaTifSourceConfig); + actionListener.onResponse(new SAGetTIFSourceConfigResponse(SaTifSourceConfigDto.getId(), SaTifSourceConfigDto.getVersion(), RestStatus.OK, SaTifSourceConfigDto)); + } + + @Override + public void onFailure(Exception e) { + actionListener.onFailure(e); + } + }); + } + + private void setFilterByEnabled(boolean filterByEnabled) { + this.filterByEnabled = filterByEnabled; + } + +} diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/transport/TransportIndexTIFSourceConfigAction.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/transport/TransportIndexTIFSourceConfigAction.java index c64341521..e5a475eea 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/transport/TransportIndexTIFSourceConfigAction.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/transport/TransportIndexTIFSourceConfigAction.java @@ -17,11 +17,13 @@ import org.opensearch.core.action.ActionListener; import org.opensearch.core.rest.RestStatus; import org.opensearch.securityanalytics.settings.SecurityAnalyticsSettings; +import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigRequest; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigResponse; import org.opensearch.securityanalytics.threatIntel.common.TIFLockService; import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfig; import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; +import org.opensearch.securityanalytics.threatIntel.sacommons.IndexTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.service.SATIFSourceConfigService; import org.opensearch.securityanalytics.transport.SecureTransportAction; import org.opensearch.securityanalytics.util.SecurityAnalyticsException; @@ -63,7 +65,7 @@ public TransportIndexTIFSourceConfigAction( final TIFLockService lockService, final Settings settings ) { - super(INDEX_TIF_SOURCE_CONFIG_ACTION_NAME, transportService, actionFilters, SAIndexTIFSourceConfigRequest::new); + super(SAIndexTIFSourceConfigAction.NAME, transportService, actionFilters, SAIndexTIFSourceConfigRequest::new); this.threadPool = threadPool; this.SaTifSourceConfigService = SaTifSourceConfigService; this.lockService = lockService; diff --git a/src/test/java/org/opensearch/securityanalytics/TestHelpers.java b/src/test/java/org/opensearch/securityanalytics/TestHelpers.java index 26f3c8216..9c1e659bf 100644 --- a/src/test/java/org/opensearch/securityanalytics/TestHelpers.java +++ b/src/test/java/org/opensearch/securityanalytics/TestHelpers.java @@ -32,6 +32,10 @@ import org.opensearch.securityanalytics.model.IocDao; import org.opensearch.securityanalytics.model.IocDto; import org.opensearch.securityanalytics.model.ThreatIntelFeedData; +import org.opensearch.securityanalytics.threatIntel.common.FeedType; +import org.opensearch.securityanalytics.threatIntel.common.RefreshType; +import org.opensearch.securityanalytics.threatIntel.common.TIFJobState; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.rest.OpenSearchRestTestCase; @@ -2829,6 +2833,83 @@ public static IocDto randomIocDto( )); } + public static SATIFSourceConfigDto randomSATIFSourceConfigDto() { + return randomSATIFSourceConfigDto( + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ); + } + + public static SATIFSourceConfigDto randomSATIFSourceConfigDto( + String feedName, + String feedFormat, + FeedType feedType, + String createdByUser, + Instant createdAt, + Instant enabledTime, + Instant lastUpdateTime, + org.opensearch.jobscheduler.spi.schedule.IntervalSchedule schedule, + TIFJobState state, + RefreshType refreshType, + Instant lastRefreshedTime, + String lastRefreshedUser, + Boolean isEnabled, + Map iocMapStore, + List iocTypes + ) { + if (feedName == null) { + feedName = randomString(); + } + if (feedFormat == null) { + feedFormat = "STIX"; + } + if (feedType == null) { + feedType = FeedType.INTERNAL; + } + if (isEnabled == null) { + isEnabled = true; + } + if (schedule == null) { + schedule = new org.opensearch.jobscheduler.spi.schedule.IntervalSchedule(Instant.now(), 1, ChronoUnit.DAYS); + } + if (iocTypes == null) { + iocTypes = List.of("ip"); + } + + return new SATIFSourceConfigDto( + null, + null, + feedName, + feedFormat, + feedType, + createdByUser, + createdAt, + enabledTime, + lastUpdateTime, + schedule, + state, + refreshType, + lastRefreshedTime, + lastRefreshedUser, + isEnabled, + iocMapStore, + iocTypes + ); + } + public static XContentParser getParser(String xc) throws IOException { XContentParser parser = XContentType.JSON.xContent() .createParser(xContentRegistry(), LoggingDeprecationHandler.INSTANCE, xc); diff --git a/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigActionTests.java b/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigActionTests.java new file mode 100644 index 000000000..f0b932472 --- /dev/null +++ b/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigActionTests.java @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.opensearch.securityanalytics.action; + +import org.junit.Assert; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigAction; +import org.opensearch.test.OpenSearchTestCase; + +public class GetTIFSourceConfigActionTests extends OpenSearchTestCase { + public void testGetTIFSourceConfigActionName() { + Assert.assertNotNull(SAGetTIFSourceConfigAction.INSTANCE.name()); + Assert.assertEquals(SAGetTIFSourceConfigAction.INSTANCE.name(), SAGetTIFSourceConfigAction.NAME); + } +} \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigRequestTests.java b/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigRequestTests.java new file mode 100644 index 000000000..376d10b01 --- /dev/null +++ b/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigRequestTests.java @@ -0,0 +1,44 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.opensearch.securityanalytics.action; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.common.UUIDs; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigRequest; +import org.opensearch.test.OpenSearchTestCase; + +import java.io.IOException; + +public class GetTIFSourceConfigRequestTests extends OpenSearchTestCase { + public void testStreamInOut() throws IOException { + BytesStreamOutput out = new BytesStreamOutput(); + String id = UUIDs.base64UUID(); + Long version = 1L; + + SAGetTIFSourceConfigRequest request = new SAGetTIFSourceConfigRequest(id, version); + request.writeTo(out); + + StreamInput sin = StreamInput.wrap(out.bytes().toBytesRef().bytes); + SAGetTIFSourceConfigRequest newReq = new SAGetTIFSourceConfigRequest(sin); + + assertEquals(id, newReq.getId()); + assertEquals(version, newReq.getVersion()); + } + + public void testValidate() { + String id = UUIDs.base64UUID(); + Long version = 1L; + + SAGetTIFSourceConfigRequest request = new SAGetTIFSourceConfigRequest(id, version); + ActionRequestValidationException e = request.validate(); + assertNull(e); + + request = new SAGetTIFSourceConfigRequest("", 0L); + e = request.validate(); + assertNotNull(e); + } +} \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigResponseTests.java b/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigResponseTests.java new file mode 100644 index 000000000..c6e5b08e3 --- /dev/null +++ b/src/test/java/org/opensearch/securityanalytics/action/GetTIFSourceConfigResponseTests.java @@ -0,0 +1,85 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.securityanalytics.action; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Assert; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.jobscheduler.spi.schedule.IntervalSchedule; +import org.opensearch.securityanalytics.threatIntel.action.SAGetTIFSourceConfigResponse; +import org.opensearch.securityanalytics.threatIntel.common.FeedType; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; +import org.opensearch.test.OpenSearchTestCase; + +import java.io.IOException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; + +public class GetTIFSourceConfigResponseTests extends OpenSearchTestCase { + private static final Logger log = LogManager.getLogger(GetTIFSourceConfigResponseTests.class); + + public void testStreamInOut() throws IOException { + String feedName = "test_feed_name"; + String feedFormat = "STIX"; + FeedType feedType = FeedType.INTERNAL; + IntervalSchedule schedule = new IntervalSchedule(Instant.now(), 1, ChronoUnit.DAYS); + List iocTypes = List.of("ip", "dns"); + + SATIFSourceConfigDto SaTifSourceConfigDto = new SATIFSourceConfigDto( + null, + null, + feedName, + feedFormat, + feedType, + null, + Instant.now(), + null, + Instant.now(), + schedule, + null, + null, + Instant.now(), + null, + false, + null, + iocTypes + ); + + SAGetTIFSourceConfigResponse response = new SAGetTIFSourceConfigResponse(SaTifSourceConfigDto.getId(), SaTifSourceConfigDto.getVersion(), RestStatus.OK, SaTifSourceConfigDto); + log.error(SaTifSourceConfigDto.getLastUpdateTime()); + Assert.assertNotNull(response); + + BytesStreamOutput out = new BytesStreamOutput(); + response.writeTo(out); + + StreamInput sin = StreamInput.wrap(out.bytes().toBytesRef().bytes); + SAGetTIFSourceConfigResponse newResponse = new SAGetTIFSourceConfigResponse(sin); + + Assert.assertEquals(SaTifSourceConfigDto.getId(), newResponse.getId()); + Assert.assertEquals(SaTifSourceConfigDto.getVersion(), newResponse.getVersion()); + Assert.assertEquals(RestStatus.OK, newResponse.getStatus()); + Assert.assertNotNull(newResponse.getSaTifSourceConfigDto()); + Assert.assertEquals(feedName, newResponse.getSaTifSourceConfigDto().getName()); + Assert.assertEquals(feedFormat, newResponse.getSaTifSourceConfigDto().getFeedFormat()); + Assert.assertEquals(feedType, newResponse.getSaTifSourceConfigDto().getFeedType()); + Assert.assertEquals(SaTifSourceConfigDto.getState(), newResponse.getSaTifSourceConfigDto().getState()); + Assert.assertEquals(SaTifSourceConfigDto.getEnabledTime(), newResponse.getSaTifSourceConfigDto().getEnabledTime()); + Assert.assertEquals(SaTifSourceConfigDto.getCreatedAt(), newResponse.getSaTifSourceConfigDto().getCreatedAt()); + Assert.assertEquals(SaTifSourceConfigDto.getLastUpdateTime(), newResponse.getSaTifSourceConfigDto().getLastUpdateTime()); + Assert.assertEquals(SaTifSourceConfigDto.isEnabled(), newResponse.getSaTifSourceConfigDto().isEnabled()); + Assert.assertEquals(SaTifSourceConfigDto.getLastRefreshedTime(), newResponse.getSaTifSourceConfigDto().getLastRefreshedTime()); + Assert.assertEquals(SaTifSourceConfigDto.getLastRefreshedUser(), newResponse.getSaTifSourceConfigDto().getLastRefreshedUser()); + Assert.assertEquals(schedule, newResponse.getSaTifSourceConfigDto().getSchedule()); + Assert.assertEquals(SaTifSourceConfigDto.getCreatedByUser(), newResponse.getSaTifSourceConfigDto().getCreatedByUser()); + Assert.assertEquals(SaTifSourceConfigDto.getIocMapStore(), newResponse.getSaTifSourceConfigDto().getIocMapStore()); + Assert.assertTrue(iocTypes.containsAll(newResponse.getSaTifSourceConfigDto().getIocTypes()) && + newResponse.getSaTifSourceConfigDto().getIocTypes().containsAll(iocTypes)); + } +} diff --git a/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigActionTests.java b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigActionTests.java new file mode 100644 index 000000000..c8b8b29bd --- /dev/null +++ b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigActionTests.java @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.opensearch.securityanalytics.action; + +import org.junit.Assert; +import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigAction; +import org.opensearch.test.OpenSearchTestCase; + +public class IndexTIFSourceConfigActionTests extends OpenSearchTestCase { + public void testIndexTIFSourceConfigActionName() { + Assert.assertNotNull(SAIndexTIFSourceConfigAction.INSTANCE.name()); + Assert.assertEquals(SAIndexTIFSourceConfigAction.INSTANCE.name(), SAIndexTIFSourceConfigAction.NAME); + } +} \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigRequestTests.java b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigRequestTests.java new file mode 100644 index 000000000..21ca175fe --- /dev/null +++ b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigRequestTests.java @@ -0,0 +1,37 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.opensearch.securityanalytics.action; + +import org.junit.Assert; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.rest.RestRequest; +import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigRequest; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; +import org.opensearch.test.OpenSearchTestCase; + +import java.io.IOException; + +import static org.opensearch.securityanalytics.TestHelpers.randomSATIFSourceConfigDto; + +public class IndexTIFSourceConfigRequestTests extends OpenSearchTestCase { + + public void testTIFSourceConfigPostRequest() throws IOException { + SATIFSourceConfigDto SaTifSourceConfigDto = randomSATIFSourceConfigDto(); + String id = SaTifSourceConfigDto.getId(); + SAIndexTIFSourceConfigRequest request = new SAIndexTIFSourceConfigRequest(id, WriteRequest.RefreshPolicy.IMMEDIATE, RestRequest.Method.POST, SaTifSourceConfigDto); + Assert.assertNotNull(request); + + BytesStreamOutput out = new BytesStreamOutput(); + request.writeTo(out); + + StreamInput sin = StreamInput.wrap(out.bytes().toBytesRef().bytes); + SAIndexTIFSourceConfigRequest newRequest = new SAIndexTIFSourceConfigRequest(sin); + Assert.assertEquals(id, request.getTIFConfigId()); + Assert.assertEquals(RestRequest.Method.POST, newRequest.getMethod()); + Assert.assertNotNull(newRequest.getTIFConfigDto()); + } +} \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigResponseTests.java b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigResponseTests.java new file mode 100644 index 000000000..cce168ae8 --- /dev/null +++ b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigResponseTests.java @@ -0,0 +1,67 @@ +package org.opensearch.securityanalytics.action; + +import org.junit.Assert; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.jobscheduler.spi.schedule.IntervalSchedule; +import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigResponse; +import org.opensearch.securityanalytics.threatIntel.common.FeedType; +import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; +import org.opensearch.test.OpenSearchTestCase; + +import java.io.IOException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; + +public class IndexTIFSourceConfigResponseTests extends OpenSearchTestCase { + + public void testIndexTIFSourceConfigPostResponse() throws IOException { + String feedName = "test_feed_name"; + String feedFormat = "STIX"; + FeedType feedType = FeedType.INTERNAL; + IntervalSchedule schedule = new IntervalSchedule(Instant.now(), 1, ChronoUnit.DAYS); + List iocTypes = List.of("ip", "dns"); + + SATIFSourceConfigDto SaTifSourceConfigDto = new SATIFSourceConfigDto( + null, + null, + feedName, + feedFormat, + feedType, + null, + null, + null, + null, + schedule, + null, + null, + null, + null, + true, + null, + iocTypes + ); + + SAIndexTIFSourceConfigResponse response = new SAIndexTIFSourceConfigResponse(SaTifSourceConfigDto.getId(), SaTifSourceConfigDto.getVersion(), RestStatus.OK, SaTifSourceConfigDto); + Assert.assertNotNull(response); + + BytesStreamOutput out = new BytesStreamOutput(); + response.writeTo(out); + + StreamInput sin = StreamInput.wrap(out.bytes().toBytesRef().bytes); + SAIndexTIFSourceConfigResponse newResponse = new SAIndexTIFSourceConfigResponse(sin); + + Assert.assertEquals(SaTifSourceConfigDto.getId(), newResponse.getTIFConfigId()); + Assert.assertEquals(SaTifSourceConfigDto.getVersion(), newResponse.getVersion()); + Assert.assertEquals(RestStatus.OK, newResponse.getStatus()); + Assert.assertNotNull(newResponse.getTIFConfigDto()); + Assert.assertEquals(feedName, newResponse.getTIFConfigDto().getName()); + Assert.assertEquals(feedFormat, newResponse.getTIFConfigDto().getFeedFormat()); + Assert.assertEquals(feedType, newResponse.getTIFConfigDto().getFeedType()); + Assert.assertEquals(schedule, newResponse.getTIFConfigDto().getSchedule()); + Assert.assertTrue(iocTypes.containsAll(newResponse.getTIFConfigDto().getIocTypes()) && + newResponse.getTIFConfigDto().getIocTypes().containsAll(iocTypes)); + } +} \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java index 2a59f7781..da9fe8ca2 100644 --- a/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java @@ -31,14 +31,18 @@ public class SATIFSourceConfigRestApiIT extends SecurityAnalyticsRestTestCase { private static final Logger log = LogManager.getLogger(SATIFSourceConfigRestApiIT.class); public void testCreateSATIFSourceConfig() throws IOException { + String feedName = "test_feed_name"; + String feedFormat = "STIX"; + FeedType feedType = FeedType.INTERNAL; IntervalSchedule schedule = new IntervalSchedule(Instant.now(), 1, ChronoUnit.DAYS); + List iocTypes = List.of("ip", "dns"); SATIFSourceConfigDto SaTifSourceConfigDto = new SATIFSourceConfigDto( null, null, - "feedname", - "stix", - FeedType.CUSTOM, + feedName, + feedFormat, + feedType, null, null, null, @@ -50,7 +54,7 @@ public void testCreateSATIFSourceConfig() throws IOException { null, true, null, - List.of("ip", "dns") + iocTypes ); Response response = makeRequest(client(), "POST", SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI, Collections.emptyMap(), toHttpEntity(SaTifSourceConfigDto)); @@ -73,4 +77,60 @@ public void testCreateSATIFSourceConfig() throws IOException { List hits = executeSearch(JOB_INDEX_NAME, request); Assert.assertEquals(1, hits.size()); } + + public void testGetSATIFSourceConfigById() throws IOException { + String feedName = "test_feed_name"; + String feedFormat = "STIX"; + FeedType feedType = FeedType.INTERNAL; + IntervalSchedule schedule = new IntervalSchedule(Instant.now(), 1, ChronoUnit.DAYS); + List iocTypes = List.of("ip", "dns"); + + SATIFSourceConfigDto SaTifSourceConfigDto = new SATIFSourceConfigDto( + null, + null, + feedName, + feedFormat, + feedType, + null, + null, + null, + null, + schedule, + null, + null, + null, + null, + true, + null, + iocTypes + ); + + Response response = makeRequest(client(), "POST", SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI, Collections.emptyMap(), toHttpEntity(SaTifSourceConfigDto)); + Assert.assertEquals(201, response.getStatusLine().getStatusCode()); + Map responseBody = asMap(response); + + String createdId = responseBody.get("_id").toString(); + Assert.assertNotEquals("response is missing Id", SATIFSourceConfigDto.NO_ID, createdId); + + response = makeRequest(client(), "GET", SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI + "/" + createdId, Collections.emptyMap(), null); + Map getResponse = entityAsMap(response); + + String responseId = responseBody.get("_id").toString(); + Assert.assertEquals("Created Id and returned Id do not match", createdId, responseId); + + int responseVersion = Integer.parseInt(responseBody.get("_version").toString()); + Assert.assertTrue("Incorrect version", responseVersion > 0); + + String returnedFeedName = (String) ((Map)responseBody.get("tif_config")).get("feed_name"); + Assert.assertEquals("Created feed name and returned feed name do not match", feedName, returnedFeedName); + + String returnedFeedFormat = (String) ((Map)responseBody.get("tif_config")).get("feed_format"); + Assert.assertEquals("Created feed format and returned feed format do not match", feedFormat, returnedFeedFormat); + + String returnedFeedType = (String) ((Map)responseBody.get("tif_config")).get("feed_type"); + Assert.assertEquals("Created feed type and returned feed type do not match", feedType, SATIFSourceConfigDto.toFeedType(returnedFeedType)); + + List returnedIocTypes = (List) ((Map)responseBody.get("tif_config")).get("ioc_types"); + Assert.assertTrue("Created ioc types and returned ioc types do not match", iocTypes.containsAll(returnedIocTypes) && returnedIocTypes.containsAll(iocTypes)); + } }