From 6a7dc6ed72fe3150e8ee9e6f86cac4f11acf7f13 Mon Sep 17 00:00:00 2001 From: Andriy Tretyak Date: Wed, 6 Nov 2019 20:40:06 +0200 Subject: [PATCH] Compliance Frameworks and Reports #304 --- ...pliance-framework-control-issue.service.ts | 31 +++++ .../compliance-framework-control.service.ts | 34 ++++++ .../compliance-framework.service.ts | 32 +++++ ...ompliance_framework_control.component.html | 49 ++++++++ .../compliance_framework_control.component.ts | 111 ++++++++++++++++++ ...nce_framework_control_issue.component.html | 48 ++++++++ ...iance_framework_control_issue.component.ts | 57 +++++++++ .../compliance_framework_list.component.html | 50 ++++++++ .../compliance_framework_list.component.ts | 93 +++++++++++++++ .../compliance/compliance_response.ts | 49 ++++++++ src/organization/organization.routes.ts | 37 ++++++ src/organization/orgnavigator.component.html | 6 +- 12 files changed, 596 insertions(+), 1 deletion(-) create mode 100644 src/organization/compliance/compliance-framework-control-issue.service.ts create mode 100644 src/organization/compliance/compliance-framework-control.service.ts create mode 100644 src/organization/compliance/compliance-framework.service.ts create mode 100644 src/organization/compliance/compliance_framework_control.component.html create mode 100644 src/organization/compliance/compliance_framework_control.component.ts create mode 100644 src/organization/compliance/compliance_framework_control_issue.component.html create mode 100644 src/organization/compliance/compliance_framework_control_issue.component.ts create mode 100644 src/organization/compliance/compliance_framework_list.component.html create mode 100644 src/organization/compliance/compliance_framework_list.component.ts create mode 100644 src/organization/compliance/compliance_response.ts diff --git a/src/organization/compliance/compliance-framework-control-issue.service.ts b/src/organization/compliance/compliance-framework-control-issue.service.ts new file mode 100644 index 00000000000..6dc99b307e6 --- /dev/null +++ b/src/organization/compliance/compliance-framework-control-issue.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { AccountService } from '../../account/account.service'; +import { SpinnerService } from '../../common/spinner/spinner.service'; +import { BaseListService } from 'common/service/base-list-service'; + + +@Injectable() +export class ComplianceFrameworkControlIssueService extends BaseListService { + private controlId: number; + constructor(protected accountService: AccountService, protected spinnerService: SpinnerService) { + super(accountService, spinnerService); + + this.path = `/organization/${this.accountService.getOrganizationId()}/compliance_framework_control_issue_list`; + } + + public setControlId(controlId: number) { + this.controlId = controlId; + } + + getRequest(requestPage: number = 1) { + let request = { + control_id: this.controlId, + filters: this.getFilters(), + limit: this.requestLimit, + sort: this.listSorting, + page: requestPage, + }; + return request; + } +} + diff --git a/src/organization/compliance/compliance-framework-control.service.ts b/src/organization/compliance/compliance-framework-control.service.ts new file mode 100644 index 00000000000..38947afaa09 --- /dev/null +++ b/src/organization/compliance/compliance-framework-control.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { AccountService } from '../../account/account.service'; +import { TableCache } from 'common/cache/table-cache'; +import { share } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { FrameworkControlListResponse } from './compliance_response'; + + +@Injectable() +export class ComplianceFrameworkControlService extends TableCache { + public complianceControlResponse$: Observable; + private complianceControlObserver: any; + + constructor( + protected accountService: AccountService + ) { + super(TableCache.PAGE_ROWS, TableCache.REQUEST_LIMIT); + this.complianceControlResponse$ = new Observable(observer => this.complianceControlObserver = observer).pipe(share()); + } + + getComplianceControl(frameworkId: number) { + let data = this.getRequest(frameworkId); + this.accountService.executePost(`/organization/${this.accountService.getOrganizationId()}/compliance_framework_control_list`, data).subscribe((data) => { + this.complianceControlObserver.next(data); + }); + } + + getRequest(frameworkId: number) { + let request = { + org_framework_id: frameworkId + }; + return request; + } +} diff --git a/src/organization/compliance/compliance-framework.service.ts b/src/organization/compliance/compliance-framework.service.ts new file mode 100644 index 00000000000..2f184ba6afd --- /dev/null +++ b/src/organization/compliance/compliance-framework.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { AccountService } from '../../account/account.service'; +import { TableCache } from 'common/cache/table-cache'; +import { share } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { FrameworkListResponse } from './compliance_response'; + + +@Injectable() +export class ComplianceFrameworkService extends TableCache { + public complianceFrameworkResponse$: Observable; + private complianceFrameworkObserver: any; + + constructor( + protected accountService: AccountService + ) { + super(TableCache.PAGE_ROWS, TableCache.REQUEST_LIMIT); + this.complianceFrameworkResponse$ = new Observable(observer => this.complianceFrameworkObserver = observer).pipe(share()); + } + + getComplianceFramework() { + let data = this.getRequest(); + this.accountService.executePost(`/organization/${this.accountService.getOrganizationId()}/compliance_framework_list`, data).subscribe((data) => { + this.complianceFrameworkObserver.next(data); + }); + } + + getRequest() { + let request = {}; + return request; + } +} diff --git a/src/organization/compliance/compliance_framework_control.component.html b/src/organization/compliance/compliance_framework_control.component.html new file mode 100644 index 00000000000..e7f66443b97 --- /dev/null +++ b/src/organization/compliance/compliance_framework_control.component.html @@ -0,0 +1,49 @@ + + +
+ +
+
+
+

Compliance Frameworks Control + + Total: {{ totalRecords }} +

+
+
+
+

There is currently nothing to display.

+
+
+ + + + + + + + + +
{{col.header}}
+ + +
+ + + + {{ rowData.control_tid }} + + {{ rowData.num_rules }} + {{ rowData.num_passes }} + {{ rowData.num_checks - rowData.num_passes }} + {{ rowData.num_devices - rowData.num_incompliant_devices }} + {{ rowData.num_incompliant_devices }} + + +
+
+
+
+
+
diff --git a/src/organization/compliance/compliance_framework_control.component.ts b/src/organization/compliance/compliance_framework_control.component.ts new file mode 100644 index 00000000000..68769d5e89c --- /dev/null +++ b/src/organization/compliance/compliance_framework_control.component.ts @@ -0,0 +1,111 @@ +import { Component, OnInit } from '@angular/core'; +import { TableCache } from 'common/cache/table-cache'; +import { ColumnWithSort } from 'common/object'; +import { SpinnerService } from 'common/spinner/spinner.service'; +import { AccountService } from 'account/account.service'; +import { Location } from '@angular/common'; +import { Router, ActivatedRoute } from '@angular/router'; +import { ComplianceFrameworkControlService } from './compliance-framework-control.service'; +import { FrameworkControlListResponse, FrameworkControlListItems } from './compliance_response'; +import { Breadcrumb } from 'common/breadcrumb/breadcrumb.model'; +import { BreadcrumbService } from 'common/breadcrumb/breadcrumb.service'; + + +@Component({ + templateUrl: 'compliance_framework_control.component.html', + providers: [ComplianceFrameworkControlService], + host: { + '(window:resize)': 'onResize($event)' + } +}) + +export class ComplianceFrameworkControlComponent implements OnInit { + public items: Array; + public totalRecords: number; + public ROWS: number = TableCache.PAGE_ROWS; + public emptyResponse: boolean; + public window = window; + public HEADER_HEIGHT: number = 310; + private frameworkId: number; + public cols: Array = [ + { field: 'control_tid', header: 'Control ID', order: 0, width: '100px', disabled: false }, + { field: 'rules_count', header: 'Rules Count', order: 0, width: '100px', disabled: false }, + { field: 'checks_compliant', header: 'Checks Compliant', order: 0, width: '100px', disabled: false }, + { field: 'checks_incompliant', header: 'Checks Incompliant', order: 0, width: '100px', disabled: false }, + { field: 'devices_compliant', header: 'Devices Compliant', order: 0, width: '100px', disabled: false }, + { field: 'devices_incompliant', header: 'Devices Incompliant', order: 0, width: '100px', disabled: false } + ]; + + constructor( + private service: ComplianceFrameworkControlService, + private spinnerService: SpinnerService, + private accountService: AccountService, + private route: ActivatedRoute, + private breadcrumbService: BreadcrumbService, + private location: Location, + private router: Router + ) { + this.frameworkId = parseInt(route.snapshot.params['framework_id']); + this.items = []; + this.totalRecords = 0; + this.emptyResponse = true; + } + + ngOnInit() { + this.service.complianceControlResponse$.subscribe( + (data: FrameworkControlListResponse) => { + this.spinnerService.setState(false); + if (data.items && data.items.length) { + this.items = data.items; + this.totalRecords = this.items.length; + this.emptyResponse = false; + } + }, + err => { + this.emptyResponse = true; + this.spinnerService.setState(false); + this.accountService.handleError(err); + } + ); + this.refresh(); + } + + refresh() { + this.spinnerService.setState(true); + this.service.getComplianceControl(this.frameworkId); + this.setBreadcrumb(); + } + + setBreadcrumb() { + let breadcrumbs = new Array(); + breadcrumbs.push(new Breadcrumb('Compliance', this.getRoutePath('/compliance_framework'))); + breadcrumbs.push(new Breadcrumb(this.frameworkId.toString(), null)); + this.breadcrumbService.setBreadcrumbs(breadcrumbs); + } + + getColumnWidth(fieldName) { + let field = this.cols.filter(function (item) { + return item.field === fieldName; + })[0]; + return field.width; + } + + itemSelected(event, item: FrameworkControlListItems) { + event.preventDefault(); + new Promise((resolve) => { + this.location.replaceState(this.location.path().split('?')[0] + '?id=' + item.control_id); + resolve(); + }).then(() => { + let subPath = `/compliance_framework_control_issue/${this.frameworkId}/${item.control_id}`; + let url = this.accountService.navigateOrganization(subPath); + this.router.navigateByUrl(url); + }); + } + + getRoutePath(subPath) { + return this.accountService.navigateOrganization(subPath); + } + + onResize(event) {} +} + diff --git a/src/organization/compliance/compliance_framework_control_issue.component.html b/src/organization/compliance/compliance_framework_control_issue.component.html new file mode 100644 index 00000000000..75d080ea7f1 --- /dev/null +++ b/src/organization/compliance/compliance_framework_control_issue.component.html @@ -0,0 +1,48 @@ + + +
+ +
+
+
+

Compliance Frameworks Issues and Subcontrols + + Total: {{ totalRecords }} +

+
+
+
+

There is currently nothing to display.

+
+
+ + + + + + + + + +
{{col.header}}
+ + +
+ + + {{ rowData.sub_control_id }} + {{ rowData.rule_title }} + {{ rowData.num_devices - rowData.num_devices_incompliant }} + {{ rowData.num_devices_incompliant }} + {{ rowData.num_checks - rowData.num_checks_passed }} + {{ rowData.num_checks_passed }} + + +
+
+
+
+
+
diff --git a/src/organization/compliance/compliance_framework_control_issue.component.ts b/src/organization/compliance/compliance_framework_control_issue.component.ts new file mode 100644 index 00000000000..2fcf0b2c95f --- /dev/null +++ b/src/organization/compliance/compliance_framework_control_issue.component.ts @@ -0,0 +1,57 @@ +import { Component, Injector } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ComplianceFrameworkControlIssueService } from './compliance-framework-control-issue.service'; +import { Breadcrumb } from 'common/breadcrumb/breadcrumb.model'; +import { BaseList } from 'common/list/base-list'; + + +@Component({ + templateUrl: 'compliance_framework_control_issue.component.html', + providers: [ComplianceFrameworkControlIssueService], + host: { + '(window:resize)': 'onResize($event)' + } +}) + +export class ComplianceFrameworkControlIssueComponent extends BaseList { + private controlId: number; + private frameworkId: string; + + constructor( + service: ComplianceFrameworkControlIssueService, + protected route: ActivatedRoute, + injector: Injector + ) { + super(service, injector); + + this.HEADER_HEIGHT = 320; + this.frameworkId = route.snapshot.params['framework_id']; + this.controlId = parseInt(route.snapshot.params['id']); + this.service.setControlId(this.controlId); + } + + ngOnInit() { + super.ngOnInit(); + this.cols = [ + { field: 'sub_control_id', header: 'Subcontrol ID', order: 0, width: '100px', disabled: false }, + { field: 'rule_title', header: 'Rule Title', order: 0, width: '300px', disabled: false }, + { field: 'devices_compliant', header: 'Devices Compliant', order: 0, width: '100px', disabled: false }, + { field: 'devices_incompliant', header: 'Devices Incompliant', order: 0, width: '100px', disabled: false }, + { field: 'checks_compliant', header: 'Checks Compliant', order: 0, width: '100px', disabled: false }, + { field: 'checks_incompliant', header: 'Checks Incompliant', order: 0, width: '100px', disabled: false } + ]; + } + + setBreadcrumb() { + super.setBreadcrumb(); + let breadcrumbs = new Array(); + breadcrumbs.push(new Breadcrumb('Compliance', this.getRoutePath('/compliance_framework'))); + breadcrumbs.push(new Breadcrumb(this.frameworkId, this.getRoutePath(`/compliance_framework_control/${this.frameworkId}`))); + breadcrumbs.push(new Breadcrumb(this.controlId.toString(), null)); + this.breadcrumbService.setBreadcrumbs(breadcrumbs); + } + + onResize(event) { + super.onResize(event); + } +} diff --git a/src/organization/compliance/compliance_framework_list.component.html b/src/organization/compliance/compliance_framework_list.component.html new file mode 100644 index 00000000000..bb1e130e522 --- /dev/null +++ b/src/organization/compliance/compliance_framework_list.component.html @@ -0,0 +1,50 @@ + + +
+ +
+
+
+

Compliance Frameworks + + Total: {{ totalRecords }} +

+
+
+
+

There is currently nothing to display.

+
+
+ + + + + + + + + +
{{col.header}}
+ + +
+ + + + {{ rowData.framework_family }} {{ rowData.framework_baseline }} + + {{ rowData.num_checks_compliant / rowData.num_checks|mypercent }} + {{ rowData.num_rules }} + {{ rowData.num_checks }} + {{ rowData.num_checks - rowData.num_checks_compliant }} + {{ rowData.num_devices }} + {{ rowData.num_devices - rowData.num_devices_incompliant }} + + +
+
+
+
+
+
diff --git a/src/organization/compliance/compliance_framework_list.component.ts b/src/organization/compliance/compliance_framework_list.component.ts new file mode 100644 index 00000000000..8ee0955c195 --- /dev/null +++ b/src/organization/compliance/compliance_framework_list.component.ts @@ -0,0 +1,93 @@ +import { Component, OnInit } from '@angular/core'; +import { ComplianceFrameworkService } from './compliance-framework.service'; +import { TableCache } from 'common/cache/table-cache'; +import { ColumnWithSort } from 'common/object'; +import { FrameworkListResponse, FrameworkListItems } from './compliance_response'; +import { SpinnerService } from 'common/spinner/spinner.service'; +import { AccountService } from 'account/account.service'; +import { Location } from '@angular/common'; +import { Router } from '@angular/router'; + + +@Component({ + templateUrl: 'compliance_framework_list.component.html', + providers: [ComplianceFrameworkService], + host: { + '(window:resize)': 'onResize($event)' + } +}) + +export class ComplianceFrameworkComponent implements OnInit { + public items: Array; + public totalRecords: number; + public ROWS: number = TableCache.PAGE_ROWS; + public emptyResponse: boolean; + public window = window; + public HEADER_HEIGHT: number = 310; + public cols: Array = [ + { field: 'framework', header: 'Framework', order: 0, width: '100px', disabled: false }, + { field: 'compliance_score', header: 'Compliance Score', order: 0, width: '100px', disabled: false }, + { field: 'rules_count', header: 'Rules Count', order: 0, width: '100px', disabled: false }, + { field: 'checks_count', header: 'Checks Count', order: 0, width: '100px', disabled: false }, + { field: 'checks_incompliant', header: 'Checks Incompliant', order: 0, width: '100px', disabled: false }, + { field: 'devices_count', header: 'Devices Count', order: 0, width: '100px', disabled: false }, + { field: 'devices_incompliant', header: 'Devices w/issues', order: 0, width: '100px', disabled: false } + ]; + + constructor( + private service: ComplianceFrameworkService, + private spinnerService: SpinnerService, + private accountService: AccountService, + private location: Location, + private router: Router + ) { + this.items = []; + this.totalRecords = 0; + this.emptyResponse = true; + } + + ngOnInit() { + this.service.complianceFrameworkResponse$.subscribe( + (data: FrameworkListResponse) => { + this.spinnerService.setState(false); + if (data.items && data.items.length) { + this.items = data.items; + this.totalRecords = this.items.length; + this.emptyResponse = false; + } + }, + err => { + this.emptyResponse = true; + this.spinnerService.setState(false); + this.accountService.handleError(err); + } + ); + this.refresh(); + } + + refresh() { + this.spinnerService.setState(true); + this.service.getComplianceFramework(); + } + + getColumnWidth(fieldName) { + let field = this.cols.filter(function (item) { + return item.field === fieldName; + })[0]; + return field.width; + } + + itemSelected(event, item: FrameworkListItems) { + event.preventDefault(); + new Promise((resolve) => { + this.location.replaceState(this.location.path().split('?')[0] + '?id=' + item.org_framework_id); + resolve(); + }).then(() => { + let subPath = `/compliance_framework_control/${item.org_framework_id}`; + let url = this.accountService.navigateOrganization(subPath); + this.router.navigateByUrl(url); + }); + } + + onResize(event) {} +} diff --git a/src/organization/compliance/compliance_response.ts b/src/organization/compliance/compliance_response.ts new file mode 100644 index 00000000000..a0774db30bd --- /dev/null +++ b/src/organization/compliance/compliance_response.ts @@ -0,0 +1,49 @@ +import { BasicListResponse } from 'common/response'; + +export interface FrameworkListResponse extends BasicListResponse { + items: Array; +} + +export interface FrameworkListItems { + org_framework_id: number; + framework_family: string; + framework_baseline: string; + num_rules: number; + num_checks: number; + num_checks_compliant: number; + num_devices: number; + num_devices_incompliant: number; +} + +export interface FrameworkControlListResponse extends BasicListResponse { + items: Array; +} + +export interface FrameworkControlListItems { + control_id: number; + control_uid: string; + control_tid: string; + num_rules: number; + num_checks: number; + num_passes: number; + num_devices: number; + num_incompliant_devices; +} + +export interface FrameworkControlIssueListResponse extends BasicListResponse { + items: Array; +} + +export interface FrameworkControlIssueListItems { + control_id: number; + control_tid: string; + sub_control_id: number; + sub_control_tid: string; + rule_item_id: number; + rule_title: string; + rule_section: string; + num_devices: number; + num_devices_incompliant: number; + num_checks: number; + num_checks_passed: number; +} diff --git a/src/organization/organization.routes.ts b/src/organization/organization.routes.ts index 154c6fdcf93..33696985a56 100755 --- a/src/organization/organization.routes.ts +++ b/src/organization/organization.routes.ts @@ -58,6 +58,7 @@ import { CollectorDetailComponent } from './collector/collector_detail.component import { GroupPolicyListComponent } from './group_policy/group_policy_list.component'; import { UserAuditComponent } from './intelligence/user_audit.component'; import { SoftwareListComponent } from './software/software_list.component'; +import { ComplianceFrameworkComponent } from './compliance/compliance_framework_list.component'; import { SoftwareDetailComponent } from './software/software_detail.component'; import { BenchmarkRuleDevicesComponent } from './benchmark_rules/benchmark_rule_devices.component'; import { CreateExceptionDialogComponent } from './benchmark_rules/create-exception-dialog.component'; @@ -78,6 +79,8 @@ import { ColumnChartWidgetComponent } from 'common/widget/column_chart/column-ch import { PieChartWidgetComponent } from 'common/widget/pie_chart/pie-chart.component'; import { ListWidgetComponent } from 'common/widget/list/list.component'; import { StackedColumnChartWidgetComponent } from 'common/widget/stacked_column_chart/stacked-column-chart.component'; +import { ComplianceFrameworkControlComponent } from './compliance/compliance_framework_control.component'; +import { ComplianceFrameworkControlIssueComponent } from './compliance/compliance_framework_control_issue.component'; export const OrganizationRoutes: Routes = [ @@ -641,6 +644,37 @@ export const OrganizationRoutes: Routes = [ breadcrumb: 'Overview', isTopLevel: true } + }, + { + path: 'compliance_framework', + component: ComplianceFrameworkComponent, + canActivate: [OrganizationGuard], + data: { + breadcrumb: 'Compliance', + isTopLevel: true + } + }, + { + path: 'compliance_framework_control/:framework_id', + component: ComplianceFrameworkControlComponent, + canActivate: [OrganizationGuard], + data: { + breadcrumb: 'Compliance', + isTopLevel: false, + defaultParent: 'Compliance', + defaultParentPath: 'compliance' + } + }, + { + path: 'compliance_framework_control_issue/:framework_id/:id', + component: ComplianceFrameworkControlIssueComponent, + canActivate: [OrganizationGuard], + data: { + breadcrumb: 'Compliance', + isTopLevel: false, + defaultParent: 'Compliance', + defaultParentPath: 'compliance' + } } ] } @@ -667,6 +701,9 @@ export const OrganizationComponents = [ GroupPolicyListComponent, GroupPolicyDetailComponent, SoftwareListComponent, + ComplianceFrameworkComponent, + ComplianceFrameworkControlComponent, + ComplianceFrameworkControlIssueComponent, SoftwareDetailComponent, ChangeListComponent, ChangeDetailComponent, diff --git a/src/organization/orgnavigator.component.html b/src/organization/orgnavigator.component.html index b621620b83d..03b0ef0b388 100644 --- a/src/organization/orgnavigator.component.html +++ b/src/organization/orgnavigator.component.html @@ -47,7 +47,8 @@
  • Changes
  • GPOs
  • Software
  • -
  • +
  • Compliance
  • +
  • ...