Skip to content

Commit

Permalink
Merge pull request primefaces#68 from Nanitor/vulnerability-optional-ui
Browse files Browse the repository at this point in the history
Vulnerability optional feature and Paginate Device Vulnerabilities
  • Loading branch information
gunnsth authored Dec 11, 2018
2 parents 52c96b1 + dbc164a commit 90f11fe
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 34 deletions.
9 changes: 9 additions & 0 deletions src/account/account.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ export class AccountService {
return user.is_global_admin;
}


isOrgLicenseValid(organization: Organization) {
if (organization == null) {
return false;
Expand Down Expand Up @@ -432,6 +433,14 @@ export class AccountService {
return organization.license_expired;
}

isVulnerabilityEnabled(organization: Organization) {
if (organization == null) {
return false;
}

return organization.organization_vulnerability_enabled;
}

showAdminMenu() {
// Are we either organization admin or sysadmin.
return this.isCurrentOrgAdmin() || this.getUserInfo().is_admin;
Expand Down
59 changes: 58 additions & 1 deletion src/organization/device/device.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import {Injectable} from '@angular/core';
import {DeviceListResponse, DeviceDetailResponse, DeviceListItem} from './device_response';
import {
DeviceListResponse,
DeviceDetailResponse,
DeviceListItem,
DeviceVulnerabilityRequest,
DeviceVulnerabilityResponse, DeviceVulnerabilityItem
} from './device_response';
import {AccountService} from 'account/account.service';
import {Observable, Subscription} from 'rxjs';
import {BasicListRequest} from 'common/request';
Expand All @@ -8,6 +14,7 @@ import {Filter, ColumnSort} from 'common/object';
import {SpinnerService} from 'common/spinner/spinner.service';
import {CreateBaselineFilterForm, RemoveBaselineFilterForm} from "../benchmark_rules/benchmark_rules_response";
import { TableCache } from 'common/cache/table-cache';
import {LoggedInUserDetailItem, LoggedInUserDetailResponse} from "../loggedin_user/loggedin_user_response";

@Injectable()
export class DeviceService extends TableCache {
Expand All @@ -26,12 +33,29 @@ export class DeviceService extends TableCache {
private userFilters: Array<Filter>;
private profileFilterSubscription: Subscription;

// For device vulnerabilities.
public isVulnDetailRequesting: boolean;
private vulnDetailRequest: DeviceVulnerabilityRequest;
private vulnDetailResponse: DeviceVulnerabilityResponse;
private vulnDetailItems: Array<DeviceVulnerabilityItem>;
private vulnDetailUserFilters: Array<Filter>;
public vulnDetailResponse$: Observable<DeviceVulnerabilityResponse>;
private vulnDetailSorting: Array<ColumnSort>;
private vulnDetailObserver: any;
private vulnDetailPage: number;

constructor(public accountService: AccountService, public flashService: FlashService, private spinnerService: SpinnerService) {
super(TableCache.PAGE_ROWS, TableCache.REQUEST_LIMIT);
this.listResponse$ = new Observable<DeviceListResponse>(observer => this.listObserver = observer).share();
this.detailResponse$ = new Observable<DeviceDetailResponse>(observer => this.detailObserver = observer).share();
this.sorting$ = new Observable<Array<ColumnSort>>(observer => this.sortingObserver = observer).share();

this.vulnDetailResponse$ = new Observable<DeviceVulnerabilityResponse>(observer => this.vulnDetailObserver = observer).share();
this.vulnDetailItems = [];
this.vulnDetailRequest = new DeviceVulnerabilityRequest();
this.vulnDetailUserFilters = [];
this.vulnDetailPage = 0;

this.userFilters = [];
}

Expand All @@ -44,6 +68,39 @@ export class DeviceService extends TableCache {
return this.accountService.executeGet(`/organization/${this.accountService.getOrganizationId()}/device/${id}/detail`);
}

getVulnerabilities(deviceId: number, page: number, limit: number, force: boolean) {
if (this.vulnDetailPage == page && !force) {
return;
}
this.spinnerService.setState(true);
this.isVulnDetailRequesting = true;
this.vulnDetailRequest = new DeviceVulnerabilityRequest();
this.vulnDetailRequest.sort = this.vulnDetailSorting;
this.vulnDetailRequest.page = page;
this.vulnDetailRequest.limit = limit;
this.vulnDetailRequest.device_id = deviceId;

this.accountService.executePost(`/organization/${this.accountService.getOrganizationId()}/device/vulnerability/list`, this.vulnDetailRequest)
.map(p => p.json())
.subscribe(p => {
this.isVulnDetailRequesting = false;
this.vulnDetailResponse = p;
if (!this.vulnDetailResponse.items) {
this.vulnDetailResponse.items = [];
}
this.vulnDetailPage = page;
this.vulnDetailItems = this.vulnDetailResponse.items;
this.vulnDetailObserver.next(this.vulnDetailResponse);
this.spinnerService.setState(false);
},
error => {
this.isVulnDetailRequesting = false;
this.spinnerService.setState(false);
this.accountService.handleError(error);
});

}

getLoggedInUsers(id: string, userId: number) {
return this.accountService.executePost(`/organization/${this.accountService.getOrganizationId()}/device/loggedinuser_detail/${userId}`, null);
}
Expand Down
46 changes: 26 additions & 20 deletions src/organization/device/device_detail.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ <h2>Credentials2</h2>
<div>
<ul class="nav-tabs-nanitor list-inline">
<li id="tab1" class="active"><a (click)="setTabSelected(1)">Configurations</a></li>
<li id="tab2" *ngIf="response.vulnerabilities.length > 0"><a (click)="setTabSelected(2)">Vulnerabilities ({{ response.vulnerabilities.length }})</a></li>
<li id="tab2" *ngIf="vulnerabilityEnabled && response.num_vulnerabilities > 0"><a (click)="setTabSelected(2)">Vulnerabilities ({{ response.num_vulnerabilities }})</a></li>
<li id="tab3" *ngIf="response.rule_incidents.length > 0"><a (click)="setTabSelected(3)">Incidents ({{ response.rule_incidents.length }})</a></li>
<li id="tab4" *ngIf="response.patch_incidents.length > 0"><a (click)="setTabSelected(4)">Patches ({{ response.patch_incidents.length }})</a></li>
<li id="tab5" *ngIf="response.loggedin_users.length > 0"><a (click)="setTabSelected(5)">Users ({{ response.loggedin_users.length }})</a></li>
Expand Down Expand Up @@ -199,27 +199,33 @@ <h2>Credentials2</h2>
</table>
</div>

<div class="table-responsive" *ngIf="isTabSelected(2)">
<table class="table table-bordered" style="margin-top: 10px" *ngIf="response.vulnerabilities.length > 0">
<thead>
<tr>
<th>CVE ID</th>
<th>Summary</th>
<th>Score</th>
<th>Discovered</th>
<th>Published</th>
<div class="container-fluid white-wrapper" [class.nanitor-fade-in]="vulnResponse" [class.nanitor-hide]="!vulnResponse"
[ngStyle]="{'padding-top': '0px', 'margin-top': '10px'}"
*ngIf="isTabSelected(2)">
<p-table #ptable class="p-table" [columns]="vulnCols" [value]="vulnItems" [paginator]="true" [rows]="ROWS" [totalRecords]="vulnTotalRecords"
scrollable="true" [scrollHeight]="'100%'" sortMode="multiple" [lazy]="true" (onLazyLoad)="vulnSortingChanged($event)"
[scrollHeight]="window.innerHeight - HEADER_HEIGHT + 'px'" (onPage)="onPage($event)">
<ng-template pTemplate="colgroup" let-columns>
<colgroup>
<col *ngFor="let col of columns" [ngStyle]="{'width': col.width}">
</colgroup>
</ng-template>
<ng-template pTemplate="header" let-columns>
<tr valign="top" class="defence" width="100%">
<th *ngFor="let col of columns" class="table-header-cell" [pSortableColumn]="col.field" [pSortableColumnDisabled]="col.disabled" [ngStyle]="{'width': col.width}">
<div>{{col.header}}<p-sortIcon *ngIf="!col.disabled" [field]="col.field"></p-sortIcon></div>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of response.vulnerabilities">
<td><a href="{{ item.url }}" target="_blank">{{ item.cve_id }}</a></td>
<td>{{ item.summary }}</td>
<td>{{ item.score|mynumber }}</td>
<td>{{ item.created_at*1000 | moment:'MMM Do YYYY HH:mm' }}</td>
<td>{{ item.published_at*1000 | moment:'MMM Do YYYY HH:mm' }}</td>
</ng-template>
<ng-template pTemplate="body" let-rowData let-columns="columns">
<tr id="item-{{ rowData.id }}" class="table-row">
<td class="table-cell" [ngStyle]="{'width': getVulnColumnWidth('cve_id')}"><a href="{{ rowData.url }}" target="_blank">{{ rowData.cve_id }}</a></td>
<td class="table-cell" [ngStyle]="{'width': getVulnColumnWidth('summary')}" [pTooltip]="rowData.summary_full">{{ rowData.summary }}</td>
<td class="table-cell" [ngStyle]="{'width': getVulnColumnWidth('score')}">{{ rowData.score }}</td>
<td class="table-cell" [ngStyle]="{'width': getVulnColumnWidth('published_at')}">{{ rowData.published_at*1000 | moment:'MMM Do YYYY HH:mm' }}</td>
</tr>
</tbody>
</table>
</ng-template>
</p-table>
</div>

<div class="table-responsive" *ngIf="isTabSelected(3)">
Expand Down
88 changes: 85 additions & 3 deletions src/organization/device/device_detail.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/// <reference path="../../../custom_typings/bootstrap.d.ts" />
/// <reference path="../../../custom_typings/clipboard.d.ts" />
/// <reference path="../../../custom_typings/custom.d.ts" />
import {Component, OnInit} from '@angular/core';
import {DeviceDetailResponse, DeviceDetailItem, DeviceAssigmentItem} from './device_response';
import {Component, OnInit, Renderer, ViewChild} from '@angular/core';
import {
DeviceDetailResponse,
DeviceDetailItem,
DeviceAssigmentItem,
DeviceVulnerabilityResponse, DeviceVulnerabilityItem
} from './device_response';
import {BasicResponse} from '../../common/response';
import {DeviceService} from './device.service';
import {Router, ActivatedRoute} from '@angular/router';
Expand All @@ -14,6 +19,9 @@ import {SelectItem} from 'primeng/primeng';
import {Severity} from '../../common/enum';
import {BreadcrumbService} from '../../common/breadcrumb/breadcrumb.service';
import {Breadcrumb} from '../../common/breadcrumb/breadcrumb.model';
import {Table} from "primeng/table";
import {ColumnSort, ColumnWithSort} from "../../common/object";
import { isEqual, cloneDeep } from 'lodash';

declare var jQuery:JQueryStatic;

Expand All @@ -24,6 +32,16 @@ declare var jQuery:JQueryStatic;
})

export class DeviceDetailComponent implements OnInit{
@ViewChild('ptable') ptable: Table;
private HEADER_HEIGHT: number = 0;
private ROWS: number = 20;
private vulnCols: Array<ColumnWithSort>;
private vulnTotalRecords : number;
private vulnItems: Array<DeviceVulnerabilityItem>;
private vulnResponse: DeviceVulnerabilityResponse;
private multiSortMeta: Array<any> = null;
private savedScrollPos: number = 0;

private response: DeviceDetailResponse;
private deviceId: string;
private assignModel: DeviceAssignModel;
Expand All @@ -38,14 +56,28 @@ export class DeviceDetailComponent implements OnInit{
private exclusionProfileId: number;
private exclusionComment: string;

constructor(private service: DeviceService, private route: ActivatedRoute, private router: Router, private spinnerService: SpinnerService, private breadcrumbService: BreadcrumbService) {
private vulnerabilityEnabled: boolean;

constructor(private service: DeviceService, private route: ActivatedRoute, private router: Router, private spinnerService: SpinnerService, private breadcrumbService: BreadcrumbService,
private renderer: Renderer) {
this.deviceId = route.snapshot.params['id'];
this.response = null;
this.vulnResponse = null;
this.needsConnectionString = false;
this.tabSelected = 1;
this.vulnTotalRecords = 0;
this.vulnerabilityEnabled = false;
}

ngOnInit() {
this.vulnCols = [
{ field: 'cve_id', header: 'CVE', order: 0, width: '120px', disabled: true },
{ field: 'summary', header: 'Summary', order: 0, width: '200px', disabled: true },
{ field: 'score', header: 'Score', order: 0, width: '120px', disabled: true },
{ field: 'published_at', header: 'Published at', order: 0, width: '140px', disabled: true }
];

this.vulnerabilityEnabled = this.service.accountService.isVulnerabilityEnabled(this.service.accountService.getCurrentOrganization());
this.refresh();
}

Expand Down Expand Up @@ -83,15 +115,61 @@ export class DeviceDetailComponent implements OnInit{
p => {
this.setResponse(p);
this.spinnerService.setState(false);
if (this.vulnResponse) {
this.loadVulnerabilityData();
}
},
err => {
this.spinnerService.setState(false);
this.service.accountService.handleError(err);
}
);
this.service.vulnDetailResponse$.subscribe(p => {
this.spinnerService.setState(false);
this.vulnResponse = p;
this.vulnTotalRecords = this.vulnResponse.total;
this.vulnItems = this.vulnResponse.items;
},
err => {
this.spinnerService.setState(false);
this.service.accountService.handleError(err);
}
);

jQuery("#refresh-button").blur();
}

loadVulnerabilityData() {
let currentPage = 1;
if (this.ptable && this.ptable.first) {
currentPage = this.ptable.first / this.ROWS + 1;
this.ptable.setScrollPosition(0);
}
this.service.getVulnerabilities(parseInt(this.deviceId), currentPage, this.ROWS, true);
}

getVulnColumnWidth(field) {
let fieldd = this.vulnCols.filter(function (item) {
return item.field === field;
})[0];
return fieldd.width;
}

onPage(event) {
this.setFocusToList();
this.ptable.setScrollPosition(0);
this.savedScrollPos = 0;
}

vulnSortingChanged(event) {
//loadMore, ptable in case with lazy and virtual scroll enabled calls the same function
this.service.getVulnerabilities(parseInt(this.deviceId), (event.first ? event.first : 0) / this.ROWS + 1, this.ROWS, false);
}

setFocusToList() {
this.renderer.invokeElementMethod(jQuery(".ui-table-virtual-scroller"), 'focus', []);
}

clickRequestCheckin(event) {
event.preventDefault();
this.spinnerService.setState(true);
Expand Down Expand Up @@ -208,6 +286,7 @@ export class DeviceDetailComponent implements OnInit{
);
}


setResponse(r: DeviceDetailResponse) {
this.benchmarks = [];

Expand Down Expand Up @@ -262,6 +341,9 @@ export class DeviceDetailComponent implements OnInit{
}

setTabSelected(index: number) {
if (index == 2) {
this.loadVulnerabilityData();
}
jQuery("#tab" + this.tabSelected).removeClass("active");
this.tabSelected = index;
jQuery("#tab" + this.tabSelected).addClass("active");
Expand Down
4 changes: 2 additions & 2 deletions src/organization/device/device_list.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<input type="checkbox" class="checkbox" id="unlicensed" (click)="filterUnlicensedDevices($event)" [(ngModel)]="checkboxFilterValues['unlicensed']"/><label for="unlicensed" class="label-filter-pane">Unlicensed Devices</label>
<input type="checkbox" class="checkbox" id="outdated" (click)="filterDevicesWithOutdatedAgents($event)" [(ngModel)]="checkboxFilterValues['outdated']"/><label for="outdated" class="label-filter-pane">Devices W/ Outdated Agents</label>
<input type="checkbox" class="checkbox" id="laptopsonly" (click)="filterDevicesLaptopsOnly($event)" [(ngModel)]="checkboxFilterValues['laptopsonly']"/><label for="laptopsonly" class="label-filter-pane">Laptops</label>
<input type="checkbox" class="checkbox" id="vulnerableonly" (click)="filterVulnerableDevicesOnly($event)" [(ngModel)]="checkboxFilterValues['vulnerableonly']"/><label for="vulnerableonly" class="label-filter-pane">Devices W/ Vulnerabilities</label>
<input *ngIf="vulnerabilityEnabled" type="checkbox" class="checkbox" id="vulnerableonly" (click)="filterVulnerableDevicesOnly($event)" [(ngModel)]="checkboxFilterValues['vulnerableonly']"/><label for="vulnerableonly" class="label-filter-pane">Devices W/ Vulnerabilities</label>
</div>
<div class="input-container flex-container-column">
<label class="input-container-heading">Include:</label>
Expand Down Expand Up @@ -97,7 +97,7 @@ <h1 class="panel-title">
</div>
</td>
<td class="table-cell text-center" [ngStyle]="{'width': getColumnWidth('incident_count')}">{{ rowData.incident_count|mynumber }}</td>
<td class="table-cell text-center" [ngStyle]="{'width': getColumnWidth('num_vulnerabilities')}">{{ rowData.num_vulnerabilities|mynumber }}</td>
<td *ngIf="vulnerabilityEnabled" class="table-cell text-center" [ngStyle]="{'width': getColumnWidth('num_vulnerabilities')}">{{ rowData.num_vulnerabilities|mynumber }}</td>
<td class="table-cell text-center" [ngStyle]="{'width': getColumnWidth('num_patches')}">{{ rowData.num_patches|mynumber }}</td>
<td class="table-cell" [ngStyle]="{'width': getColumnWidth('num_benchmarks')}"><div class="text-center" [class.nanitor-cursor-help]="getBenchmarkTitles(rowData) != null" pTooltip="{{ getBenchmarkTitles(rowData) }}" tooltipPosition="bottom" showDelay="500">{{ rowData.num_benchmarks|mynumber }}</div></td>
<td class="table-cell text-center" [ngStyle]="{'width': getColumnWidth('baseline')}">{{ getBaselineScore(rowData) }}</td>
Expand Down
Loading

0 comments on commit 90f11fe

Please sign in to comment.