| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges, OnInit, OnDestroy } from '@angular/core'; |
| import { Subscription } from 'rxjs'; |
| |
| import {Pagination} from '../../../model/pagination'; |
| import {SortEvent} from '../../../shared/metron-table/metron-table.directive'; |
| import {ColumnMetadata} from '../../../model/column-metadata'; |
| import {Alert} from '../../../model/alert'; |
| import {SearchService} from '../../../service/search.service'; |
| import {Sort} from '../../../utils/enums'; |
| import {Filter} from '../../../model/filter'; |
| import {AlertSource} from '../../../model/alert-source'; |
| import {UpdateService} from '../../../service/update.service'; |
| import {MetaAlertService} from '../../../service/meta-alert.service'; |
| import {MetaAlertAddRemoveRequest} from '../../../model/meta-alert-add-remove-request'; |
| import {GetRequest} from '../../../model/get-request'; |
| import { GlobalConfigService } from '../../../service/global-config.service'; |
| import { DialogService } from '../../../service/dialog.service'; |
| import { ConfirmationType } from 'app/model/confirmation-type'; |
| import {HttpErrorResponse} from '@angular/common/http'; |
| |
| import { merge } from '../../../shared/context-menu/context-menu.util' |
| |
| export enum MetronAlertDisplayState { |
| COLLAPSE, EXPAND |
| } |
| |
| export interface SortChangedEvent { |
| sortBy: string; |
| sortOrder: string; |
| } |
| |
| export interface PageChangedEvent { |
| from: number; |
| size: number; |
| } |
| |
| @Component({ |
| selector: 'app-table-view', |
| templateUrl: './table-view.component.html', |
| styleUrls: ['./table-view.component.scss'] |
| }) |
| |
| export class TableViewComponent implements OnInit, OnChanges, OnDestroy { |
| |
| isStatusFieldPresent = false; |
| metaAlertsDisplayState: {[key: string]: MetronAlertDisplayState} = {}; |
| metronAlertDisplayState = MetronAlertDisplayState; |
| globalConfig: {} = {}; |
| configSubscription: Subscription; |
| |
| merge: Function = merge; |
| |
| @Input() alerts: Alert[] = []; |
| @Input() pagination: Pagination; |
| @Input() alertsColumnsToDisplay: ColumnMetadata[] = []; |
| @Input() selectedAlerts: Alert[] = []; |
| |
| @Output() onResize = new EventEmitter<void>(); |
| @Output() onAddFilter = new EventEmitter<Filter>(); |
| @Output() onSortChanged = new EventEmitter<SortChangedEvent>(); |
| @Output() onPageChanged = new EventEmitter<PageChangedEvent>(); |
| @Output() onShowDetails = new EventEmitter<Alert>(); |
| @Output() onShowConfigureTable = new EventEmitter<Alert>(); |
| @Output() onSelectedAlertsChange = new EventEmitter< Alert[]>(); |
| |
| constructor(public searchService: SearchService, |
| public updateService: UpdateService, |
| public metaAlertService: MetaAlertService, |
| public globalConfigService: GlobalConfigService, |
| public dialogService: DialogService) { |
| } |
| |
| ngOnInit() { |
| this.configSubscription = this.globalConfigService.get().subscribe((config: {}) => { |
| this.globalConfig = config; |
| if (this.globalConfig['source.type.field']) { |
| let filteredAlertsColumnsToDisplay = this.alertsColumnsToDisplay.filter(colName => colName.name !== 'source:type'); |
| if (filteredAlertsColumnsToDisplay.length < this.alertsColumnsToDisplay.length) { |
| this.alertsColumnsToDisplay = filteredAlertsColumnsToDisplay; |
| this.alertsColumnsToDisplay.splice(2, 0, new ColumnMetadata(this.globalConfig['source.type.field'], 'string')); |
| } |
| } |
| }); |
| } |
| |
| ngOnChanges(changes: SimpleChanges) { |
| if (changes && changes['alerts'] && changes['alerts'].currentValue) { |
| let expandedMetaAlerts = this.getGUIDOfAllExpandedMetaAlerts(); |
| this.updateExpandedStateForChangedData(expandedMetaAlerts); |
| } |
| |
| if (changes && changes['alertsColumnsToDisplay'] && changes['alertsColumnsToDisplay'].currentValue) { |
| this.isStatusFieldPresent = this.alertsColumnsToDisplay.some(col => col.name === 'alert_status'); |
| } |
| } |
| |
| ngOnDestroy() { |
| this.configSubscription.unsubscribe(); |
| } |
| |
| threatScoreFieldName() { |
| return this.globalConfig['threat.triage.score.field'] |
| } |
| |
| hasScore(alertSource) { |
| return !!this.getScore(alertSource); |
| } |
| |
| getScore(alertSource) { |
| return alertSource[this.threatScoreFieldName()]; |
| } |
| |
| updateExpandedStateForChangedData(expandedMetaAlerts: string[]) { |
| this.alerts.forEach(alert => { |
| if (alert.source.metron_alert && alert.source.metron_alert.length > 0) { |
| this.metaAlertsDisplayState[alert.id] = expandedMetaAlerts.indexOf(alert.id) === -1 ? |
| MetronAlertDisplayState.COLLAPSE : MetronAlertDisplayState.EXPAND; |
| } |
| }); |
| } |
| |
| getGUIDOfAllExpandedMetaAlerts(): string[] { |
| let expandedMetaAlerts = []; |
| Object.keys(this.metaAlertsDisplayState).forEach(id => { |
| if (this.metaAlertsDisplayState[id] === MetronAlertDisplayState.EXPAND) { |
| expandedMetaAlerts.push(id); |
| } |
| }); |
| |
| return expandedMetaAlerts; |
| } |
| |
| onSort(sortEvent: SortEvent) { |
| let sortOrder = (sortEvent.sortOrder === Sort.ASC ? 'asc' : 'desc'); |
| let sortBy = sortEvent.sortBy === 'id' ? '_uid' : sortEvent.sortBy; |
| this.onSortChanged.emit({ sortBy, sortOrder }); |
| } |
| |
| getValue(alert: Alert, column: ColumnMetadata, formatData: boolean) { |
| if (column.name === 'id') { |
| return this.formatValue(column, alert['id']); |
| } |
| |
| return this.getValueFromSource(alert.source, column, formatData); |
| } |
| |
| getValueFromSource(alertSource: AlertSource, column: ColumnMetadata, formatData: boolean) { |
| let returnValue = ''; |
| try { |
| switch (column.name) { |
| case 'alert_status': |
| returnValue = alertSource['alert_status'] ? alertSource['alert_status'] : 'NEW'; |
| break; |
| default: |
| returnValue = alertSource[column.name]; |
| break; |
| } |
| } catch (e) { |
| } |
| |
| if (formatData) { |
| returnValue = this.formatValue(column, returnValue); |
| } |
| return returnValue; |
| } |
| |
| fireSelectedAlertsChanged() { |
| this.onSelectedAlertsChange.emit(this.selectedAlerts); |
| } |
| |
| formatValue(column: ColumnMetadata, returnValue: string) { |
| try { |
| if (column.name.endsWith(':ts') || column.name.endsWith('timestamp')) { |
| returnValue = new Date(parseInt(returnValue, 10)).toISOString().replace('T', ' ').slice(0, 19); |
| } |
| } catch (e) {} |
| |
| return returnValue; |
| } |
| |
| onPageChange() { |
| this.onPageChanged.emit({ from: this.pagination.from, size: this.pagination.size }); |
| } |
| |
| selectRow($event, alert: Alert) { |
| if ($event.target.checked) { |
| this.selectedAlerts.push(alert); |
| } else { |
| this.selectedAlerts.splice(this.selectedAlerts.indexOf(alert), 1); |
| } |
| this.fireSelectedAlertsChanged(); |
| } |
| |
| selectAllRows($event) { |
| this.selectedAlerts = []; |
| if ($event.target.checked) { |
| this.selectedAlerts = this.alerts; |
| } |
| this.fireSelectedAlertsChanged(); |
| } |
| |
| resize() { |
| this.onResize.emit(); |
| } |
| |
| addFilter(field: string, value: string) { |
| field = (field === 'id') ? '_id' : field; |
| this.onAddFilter.emit(new Filter(field, value)); |
| } |
| |
| showMetaAlertDetails($event, alertSource: AlertSource) { |
| let alert = new Alert(); |
| alert.source = alertSource; |
| this.showDetails($event, alert); |
| } |
| |
| showDetails($event, alert: Alert) { |
| if ($event.target.parentElement.firstElementChild.type !== 'checkbox' && $event.target.nodeName !== 'A') { |
| this.onShowDetails.emit(alert); |
| } |
| } |
| |
| showConfigureTable() { |
| this.onShowConfigureTable.emit(); |
| } |
| |
| toggleExpandCollapse($event, alert: Alert) { |
| if (this.metaAlertsDisplayState[alert.id] === MetronAlertDisplayState.COLLAPSE) { |
| this.metaAlertsDisplayState[alert.id] = MetronAlertDisplayState.EXPAND; |
| } else { |
| this.metaAlertsDisplayState[alert.id] = MetronAlertDisplayState.COLLAPSE; |
| } |
| |
| $event.stopPropagation(); |
| return false; |
| } |
| |
| deleteOneAlertFromMetaAlert($event, alert: Alert, metaAlertIndex: number) { |
| const confirmedSubscription = this.dialogService |
| .launchDialog('Do you wish to remove the alert from the meta alert?') |
| .subscribe(action => { |
| if (action === ConfirmationType.Confirmed) { |
| this.doDeleteOneAlertFromMetaAlert(alert, metaAlertIndex); |
| } |
| confirmedSubscription.unsubscribe(); |
| }); |
| $event.stopPropagation(); |
| } |
| |
| deleteMetaAlert($event, alert: Alert) { |
| const confirmedSubscription = this.dialogService |
| .launchDialog('Do you wish to remove all the alerts from meta alert?') |
| .subscribe(action => { |
| if (action === ConfirmationType.Confirmed) { |
| this.doDeleteMetaAlert(alert); |
| } |
| confirmedSubscription.unsubscribe(); |
| }); |
| $event.stopPropagation(); |
| } |
| |
| doDeleteOneAlertFromMetaAlert(alert, metaAlertIndex) { |
| let alertToRemove = alert.source.metron_alert[metaAlertIndex]; |
| let metaAlertAddRemoveRequest = new MetaAlertAddRemoveRequest(); |
| metaAlertAddRemoveRequest.metaAlertGuid = alert.source.guid; |
| metaAlertAddRemoveRequest.alerts = [new GetRequest(alertToRemove.guid, alertToRemove[this.globalConfig['source.type.field']], '')]; |
| |
| this.metaAlertService.removeAlertsFromMetaAlert(metaAlertAddRemoveRequest).subscribe(() => { |
| }, (res: HttpErrorResponse) => { |
| let message = res.error['message']; |
| this.dialogService |
| .launchDialog(message) |
| .subscribe(action => { |
| this.configSubscription.unsubscribe(); |
| }) |
| }); |
| } |
| |
| doDeleteMetaAlert(alert: Alert) { |
| this.metaAlertService.updateMetaAlertStatus(alert.source.guid, 'inactive').subscribe(() => { |
| }); |
| } |
| } |