Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Object> 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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>, next: HttpHandler): Observable<HttpEvent<any>> {
Expand All @@ -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');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ <h5 class="card-title mb-0 text-uppercase label-header">
<h6 class="card-title text-blue-800 font-weight-light">
{{getRuleName()}}
</h6>
<button (click)="viewAlertDetail= false" aria-label="Close"
<button (click)="viewAlertDetail= false;onRefreshData(true)" aria-label="Close"
class="close button-close" type="button">
<div class="close-icon"></div>
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="has-fixed-height d-flex justify-content-center align-items-center w-100 flex-column"
*ngIf="!socAiResponse && !loading">
*ngIf="socAiResponse && socAiResponse.status === indexSocAiStatus.Processing">
<span inlineSVG="assets/icons/system/SEARCH_PROCESS.svg" class="svg-icon svg-icon-grey svg-icon-10x"></span>
<p class="text-justify font-size-lg mt-3 w-50">
<i class="icon-spinner2 spinner"></i> The analysis of this alert is currently in progress.
Expand All @@ -15,7 +15,7 @@
</div>
</ng-container>

<div *ngIf="socAiResponse && !loading" class="w-100">
<div *ngIf="socAiResponse && socAiResponse.status === indexSocAiStatus.Completed && !loading" class="w-100">
<div class="w-100 alert alert-warning alert-styled-right mb-3 alert-dismissible">
<span class="font-weight-semibold">Warning! </span>
<span>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.</span>
Expand All @@ -36,7 +36,7 @@
</li>
</ul>

<div *ngIf="socAiResponse.nextSteps.length>0" class="w-100 mt-3 d-flex flex-column">
<div *ngIf="socAiResponse.nextSteps.length > 0" class="w-100 mt-3 d-flex flex-column">
<span class="font-weight-semibold">{{socAiResponse.nextSteps.length > 1 ? 'Next steps' : 'Next step'}}:</span>
<div class="w-100 my-1" *ngFor="let step of socAiResponse.nextSteps, let index=index;">
<span class="font-weight-semibold font-size-base"><i class="icon-radio-unchecked"
Expand All @@ -46,3 +46,37 @@
</div>
</div>
</div>

<div *ngIf="isEmpty(socAiResponse) && !loading" class="w-100">
<div class="w-100 alert alert-info alert-styled-right mb-3 alert-dismissible">
<span class="font-weight-semibold">Info! </span>
<span>The SOC AI integration did not analyze this Alert; use the button below to kick-start an analysis</span>
</div>

<div class="d-flex justify-content-end mb-2">
<button (click)="processAlert()"
[disabled]="loadingProcess"
class="btn utm-button utm-button-primary ml-2">
<i [ngClass]="{'icon-spinner2 spinner': loadingProcess}"></i>
{{ loadingProcess ? 'Processing' : 'Process Alert'}}
</button>
</div>

</div>

<div *ngIf="socAiResponse && socAiResponse.status === indexSocAiStatus.Error && !loading" class="w-100">
<div class="w-100 alert alert-warning alert-styled-right mb-3 alert-dismissible">
<span class="font-weight-semibold">Error! </span>
<span>An error occurred while the SOC AI was processing this alert; use the below button to relaunch the analysis.</span>
</div>

<div class="d-flex justify-content-end mb-2">
<button (click)="processAlert()"
[disabled]="loadingProcess"
class="btn utm-button utm-button-primary ml-2">
<i [ngClass]="{'icon-spinner2 spinner': loadingProcess}"></i>
{{ loadingProcess ? 'Processing' : 'Reprocess Alert'}}
</button>
</div>

</div>
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -15,41 +17,49 @@ 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) {
this.getSocAiResponse();
}
}

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<any>) => {
this.elasticDataService.search(1, 1, 1, SOC_AI_INDEX_PATTERN, filter)
.subscribe((res: HttpResponse<any>) => {
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<any>) => {
this.loading = false;
}
);
}
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export class SocAiType {
severity: string;
category: string;
alertName: string;
status: string;
activityId: string;
classification: string;
reasoning: string[];
Expand All @@ -13,3 +14,10 @@ export class SocAiNextStep {
action: string;
details: string;
}

export enum IndexSocAiStatus {
Completed = 'Completed',
Processing = 'Processing',
Error = 'Error',
NotFound = 'NotFound'
}
Original file line number Diff line number Diff line change
@@ -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<HttpResponse<any>> {
return this.http.post<HttpResponse<any>>(this.resourceUrl + '/alerts', alertId, {observe: 'response'});
}

}