diff --git a/backend/src/main/java/com/park/utmstack/service/soc_ai/SocAIService.java b/backend/src/main/java/com/park/utmstack/service/soc_ai/SocAIService.java index 9d2011ef5..ce5549f27 100644 --- a/backend/src/main/java/com/park/utmstack/service/soc_ai/SocAIService.java +++ b/backend/src/main/java/com/park/utmstack/service/soc_ai/SocAIService.java @@ -36,7 +36,7 @@ public SocAIService(UtmAlertSocaiProcessingRequestService socaiProcessingRequest } - private void sendData(Object data) { + public void sendData(Object data) { final String ctx = CLASSNAME + ".sendData"; try { OkHttpClient client = new OkHttpClient.Builder() diff --git a/backend/src/main/java/com/park/utmstack/web/rest/soc_ai/UtmSocAiResource.java b/backend/src/main/java/com/park/utmstack/web/rest/soc_ai/UtmSocAiResource.java new file mode 100644 index 000000000..6f74e9b08 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/web/rest/soc_ai/UtmSocAiResource.java @@ -0,0 +1,47 @@ +package com.park.utmstack.web.rest.soc_ai; + +import com.park.utmstack.domain.application_events.enums.ApplicationEventType; +import com.park.utmstack.service.application_events.ApplicationEventService; +import com.park.utmstack.service.soc_ai.SocAIService; +import com.park.utmstack.web.rest.AccountResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RestController +@RequestMapping("/api/soc-ai") +public class UtmSocAiResource { + + private final Logger log = LoggerFactory.getLogger(AccountResource.class); + + private static final String CLASSNAME = "UtmSocAiResource"; + + private final ApplicationEventService applicationEventService; + private final SocAIService socAIService; + public UtmSocAiResource(SocAIService socAIService, ApplicationEventService applicationEventService) { + this.socAIService = socAIService; + this.applicationEventService = applicationEventService; + } + + @PostMapping("/alerts") + public ResponseEntity sendData(@RequestBody String[] alertsId) { + final String ctx = CLASSNAME + ".sendAlertsIds"; + try { + socAIService.sendData(alertsId); + return ResponseEntity.ok().body(Map.of("status", "success", "message", "Processing successful")); + } catch (Exception e) { + String msg = ctx + ": " + e.getMessage(); + log.error(msg); + applicationEventService.createEvent(msg, ApplicationEventType.ERROR); + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("status", "error", "message", msg)); + } + } +} diff --git a/frontend/src/app/blocks/interceptor/auth-expired.interceptor.ts b/frontend/src/app/blocks/interceptor/auth-expired.interceptor.ts index 57d91111b..9236a70ff 100644 --- a/frontend/src/app/blocks/interceptor/auth-expired.interceptor.ts +++ b/frontend/src/app/blocks/interceptor/auth-expired.interceptor.ts @@ -4,10 +4,13 @@ import {Observable} from 'rxjs'; import {tap} from 'rxjs/operators'; import {AuthServerProvider} from '../../core/auth/auth-jwt.service'; import {HttpCancelService} from '../service/httpcancel.service'; +import {AccountService} from "../../core/auth/account.service"; @Injectable() export class AuthExpiredInterceptor implements HttpInterceptor { - constructor(private authServerProvider: AuthServerProvider, private httpCancelService: HttpCancelService) { + constructor(private authServerProvider: AuthServerProvider, + private httpCancelService: HttpCancelService, + private accountService: AccountService) { } intercept(request: HttpRequest, next: HttpHandler): Observable> { @@ -19,6 +22,7 @@ export class AuthExpiredInterceptor implements HttpInterceptor { if (err instanceof HttpErrorResponse) { if (err.status === 401 || err.status === 403) { this.authServerProvider.logout().subscribe(() => { + this.accountService.authenticate(null); this.httpCancelService.cancelPendingRequests(); console.log('UTMStack 401'); }); diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html index 1ba44af29..e036035dc 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html @@ -218,7 +218,7 @@
{{getRuleName()}}
- diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.html b/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.html index da1a9c8f0..ec1edcb65 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.html +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.html @@ -1,5 +1,5 @@
+ *ngIf="socAiResponse && socAiResponse.status === indexSocAiStatus.Processing">

The analysis of this alert is currently in progress. @@ -15,7 +15,7 @@

-
+
Warning! This evaluation has been made by an Artificial Intelligence beta that is still in training mode. Use this information carefully and at your own risk. @@ -36,7 +36,7 @@ -
+
{{socAiResponse.nextSteps.length > 1 ? 'Next steps' : 'Next step'}}:
+ +
+
+ Info! + The SOC AI integration did not analyze this Alert; use the button below to kick-start an analysis +
+ +
+ +
+ +
+ +
+
+ Error! + An error occurred while the SOC AI was processing this alert; use the below button to relaunch the analysis. +
+ +
+ +
+ +
diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.ts index f13c033f5..b0ff5d3a8 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/alert-soc-ai.component.ts @@ -1,10 +1,12 @@ import {HttpResponse} from '@angular/common/http'; import {Component, Input, OnDestroy, OnInit} from '@angular/core'; +import {UtmToastService} from '../../../../../shared/alert/utm-toast.service'; import {LOG_INDEX_PATTERN, SOC_AI_INDEX_PATTERN} from '../../../../../shared/constants/main-index-pattern.constant'; import {ElasticOperatorsEnum} from '../../../../../shared/enums/elastic-operators.enum'; import {ElasticDataService} from '../../../../../shared/services/elasticsearch/elastic-data.service'; import {ElasticFilterType} from '../../../../../shared/types/filter/elastic-filter.type'; -import {SocAiType} from './soc-ai.type'; +import {AlertSocAiService} from '../../services/alert-soc-ai.service'; +import {IndexSocAiStatus, SocAiType} from './soc-ai.type'; @Component({ selector: 'app-alert-soc-ai', @@ -15,11 +17,14 @@ export class AlertSocAiComponent implements OnInit, OnDestroy { @Input() alertID: string; @Input() socAiActive: boolean; socAiResponse: SocAiType; + indexSocAiStatus = IndexSocAiStatus; loading = false; + loadingProcess = false; private interval: any; - constructor(private elasticDataService: ElasticDataService, ) { - } + constructor(private elasticDataService: ElasticDataService, + private alertSocAiService: AlertSocAiService, + private utmToastService: UtmToastService) {} ngOnInit() { if (this.socAiActive) { @@ -27,29 +32,34 @@ export class AlertSocAiComponent implements OnInit, OnDestroy { } } - ngOnDestroy() { - clearInterval(this.interval); - } - getSocAiResponse() { + this.loading = true; const filter: ElasticFilterType[] = [{ field: 'activityId', operator: ElasticOperatorsEnum.IS, value: this.alertID }]; - this.elasticDataService.search(1, 1, - 1, SOC_AI_INDEX_PATTERN, filter).subscribe( - (res: HttpResponse) => { + this.elasticDataService.search(1, 1, 1, SOC_AI_INDEX_PATTERN, filter) + .subscribe((res: HttpResponse) => { this.loading = false; - if (!res || res.body === null || res.body.length === 0) { + if (!res || res.body.length === 0) { + this.socAiResponse = res.body; + } else { + this.socAiResponse = res.body[0]; + } + + if (this.socAiResponse.status === IndexSocAiStatus.Processing) { if (!this.interval) { this.startInterval(); } } else { - this.socAiResponse = res.body[0]; + if (this.interval) { + clearInterval(this.interval); + } } }, (res: HttpResponse) => { + this.loading = false; } ); } @@ -60,4 +70,26 @@ export class AlertSocAiComponent implements OnInit, OnDestroy { }, 10000); } + ngOnDestroy() { + clearInterval(this.interval); + } + + processAlert() { + this.loadingProcess = true; + this.alertSocAiService.processAlertBySoc([this.alertID]) + .subscribe((res) => { + setTimeout(() => { + this.loadingProcess = false; + this.getSocAiResponse(); + }, 3000); + }, + (error) => { + this.utmToastService.showError('Error', 'An error occurred while processing the alert. Please try again later.'); + this.loadingProcess = false; + }); + } + + isEmpty(object: any) { + return object && Object.keys(object).length === 0; + } } diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/soc-ai.type.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/soc-ai.type.ts index 9f2ebb0d7..d41dfd9dc 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/soc-ai.type.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-soc-ai/soc-ai.type.ts @@ -2,6 +2,7 @@ export class SocAiType { severity: string; category: string; alertName: string; + status: string; activityId: string; classification: string; reasoning: string[]; @@ -13,3 +14,10 @@ export class SocAiNextStep { action: string; details: string; } + +export enum IndexSocAiStatus { + Completed = 'Completed', + Processing = 'Processing', + Error = 'Error', + NotFound = 'NotFound' +} diff --git a/frontend/src/app/data-management/alert-management/shared/services/alert-soc-ai.service.ts b/frontend/src/app/data-management/alert-management/shared/services/alert-soc-ai.service.ts new file mode 100644 index 000000000..97128ba9c --- /dev/null +++ b/frontend/src/app/data-management/alert-management/shared/services/alert-soc-ai.service.ts @@ -0,0 +1,21 @@ +import {HttpClient, HttpResponse} from '@angular/common/http'; +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {SERVER_API_URL} from '../../../../app.constants'; + + +@Injectable({ + providedIn: 'root' +}) +export class AlertSocAiService { + + public resourceUrl = SERVER_API_URL + 'api/soc-ai'; + + constructor(private http: HttpClient) { + } + + processAlertBySoc(alertId: string[]): Observable> { + return this.http.post>(this.resourceUrl + '/alerts', alertId, {observe: 'response'}); + } + +}