diff --git a/security-c4po-angular/src/app/header/header.component.html b/security-c4po-angular/src/app/header/header.component.html index cce961f..2b64594 100644 --- a/security-c4po-angular/src/app/header/header.component.html +++ b/security-c4po-angular/src/app/header/header.component.html @@ -11,6 +11,15 @@
+ + + + -
- - - - + diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.scss b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.scss index 6dc3c74..eabc9c5 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.scss +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.scss @@ -10,33 +10,9 @@ /*nb-tab { position: fixed; }*/ - } - .content-footer { - height: 5%; - - .pentest-status-dialog { - margin: 1rem 2.25rem 1rem 0; - - .status { - width: 12rem; - } - - .basic { - background-color: nb-theme(color-basic-default); - } - .info { - background-color: nb-theme(color-info-default); - } - .warning { - background-color: nb-theme(color-warning-default); - } - .success { - background-color: nb-theme(color-success-default); - } - } - - .save-pentest-button { + .content-footer { + height: 5%; margin: 1rem 6rem 1rem 0; } } diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts index fb98cac..cde1354 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts @@ -1,17 +1,12 @@ import {Component, OnInit} from '@angular/core'; import * as FA from '@fortawesome/free-solid-svg-icons'; -import {BehaviorSubject, Observable} from 'rxjs'; -import {Select, Store} from '@ngxs/store'; +import {BehaviorSubject} from 'rxjs'; +import {Store} from '@ngxs/store'; import {ProjectState} from '@shared/stores/project-state/project-state'; import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy'; -import {Pentest, transformPentestToRequestBody} from '@shared/models/pentest.model'; -import {PentestStatus} from '@shared/models/pentest-status.model'; -import {StatusText} from '@shared/widgets/status-tag/status-tag.component'; +import {Pentest} from '@shared/models/pentest.model'; import {PentestService} from '@shared/services/pentest.service'; -import {NotificationService, PopupType} from '@shared/services/notification.service'; -import {Project} from '@shared/models/project.model'; -import {isNotNullOrUndefined} from 'codelyzer/util/isNotNullOrUndefined'; -import {filter} from 'rxjs/operators'; +import {NotificationService} from '@shared/services/notification.service'; @UntilDestroy() @Component({ @@ -23,28 +18,10 @@ export class PentestContentComponent implements OnInit { // HTML only readonly fa = FA; - @Select(ProjectState.project) - selectedProject$: Observable; - selectedProjectId: string; - pentest$: BehaviorSubject = new BehaviorSubject(null); - pentestChanged$: BehaviorSubject = new BehaviorSubject(false); currentNumberOfFindings$: BehaviorSubject = new BehaviorSubject(0); currentNumberOfComments$: BehaviorSubject = new BehaviorSubject(0); - // Pentest Status Handler - currentStatus: PentestStatus = PentestStatus.NOT_STARTED; - private initialPentestStatus: PentestStatus; - status = PentestStatus; - readonly statusTexts: Array = [ - {value: PentestStatus.NOT_STARTED, translationText: 'pentest.statusText.not_started'}, - /* ToDo: Disabled not needed inside pentest */ - /*{value: PentestStatus.DISABLED, translationText: 'pentest.statusText.disabled'},*/ - {value: PentestStatus.OPEN, translationText: 'pentest.statusText.open'}, - {value: PentestStatus.IN_PROGRESS, translationText: 'pentest.statusText.in_progress'}, - {value: PentestStatus.COMPLETED, translationText: 'pentest.statusText.completed'} - ]; - constructor( private readonly pentestService: PentestService, private notificationService: NotificationService, @@ -52,26 +29,11 @@ export class PentestContentComponent implements OnInit { } ngOnInit(): void { - this.selectedProject$.pipe( - filter(isNotNullOrUndefined), - untilDestroyed(this) - ).subscribe({ - next: (project) => { - this.selectedProjectId = project.id; - }, - error: (err) => { - console.error(err); - } - }); - this.store.select(ProjectState.pentest).pipe( untilDestroyed(this) ).subscribe({ next: (selectedPentest: Pentest) => { - console.warn(selectedPentest); this.pentest$.next(selectedPentest); - this.currentStatus = selectedPentest.status; - this.initialPentestStatus = selectedPentest.status; const findings = selectedPentest.findingIds ? selectedPentest.findingIds.length : 0; this.currentNumberOfFindings$.next(findings); const comments = selectedPentest.commentIds ? selectedPentest.commentIds.length : 0; @@ -82,78 +44,4 @@ export class PentestContentComponent implements OnInit { } }); } - - onClickSavePentest(): void { - this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus}); - this.pentestService.savePentest(this.selectedProjectId, transformPentestToRequestBody(this.pentest$.getValue())) - .subscribe({ - next: (pentest: Pentest) => { - this.pentest$.next(pentest); - this.initialPentestStatus = pentest.status; - this.notificationService.showPopup('pentest.popup.save.success', PopupType.SUCCESS); - }, - error: err => { - console.log(err); - this.notificationService.showPopup('pentest.popup.save.failed', PopupType.FAILURE); - } - }); - } - - onClickUpdatePentest(): void { - this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus}); - this.pentestService.updatePentest(transformPentestToRequestBody(this.pentest$.getValue())) - .subscribe({ - next: (pentest: Pentest) => { - this.pentest$.next(pentest); - this.initialPentestStatus = pentest.status; - this.notificationService.showPopup('pentest.popup.update.success', PopupType.SUCCESS); - }, - error: err => { - console.log(err); - this.notificationService.showPopup('pentest.popup.update.failed', PopupType.FAILURE); - } - }); - } - - /** - * @return true if initial pentest Status is different from current pentest status - */ - pentestStatusChanged(): boolean { - if (this.initialPentestStatus !== this.currentStatus) { - this.pentestChanged$.next(true); - } else { - this.pentestChanged$.next(false); - } - return this.pentestChanged$.getValue(); - } - - /** - * @return the correct nb-status for current pentest-status - */ - getPentestFillStatus(value: PentestStatus): string { - let pentestFillStatus; - switch (value) { - case PentestStatus.NOT_STARTED: { - pentestFillStatus = 'basic'; - break; - } - case PentestStatus.OPEN: { - pentestFillStatus = 'info'; - break; - } - case PentestStatus.IN_PROGRESS: { - pentestFillStatus = 'warning'; - break; - } - case PentestStatus.COMPLETED: { - pentestFillStatus = 'success'; - break; - } - default: { - pentestFillStatus = 'basic'; - break; - } - } - return pentestFillStatus; - } } diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html index 72d7498..fcd12bb 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html @@ -19,7 +19,7 @@ {{ 'finding.severity' | translate }} - + diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss index 51ebbfb..fc4b47e 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss @@ -7,7 +7,8 @@ .finding-cell { // Add style here height: 4.5rem !important; - max-height: 4.5rem !important; + // max-height: 4.5rem !important; + overflow: hidden; } .finding-cell:hover { @@ -19,9 +20,15 @@ width: 125px; max-width: 125px; // border-style: none; + // ToDo: Fix size issue on lower screen resolution height: 4.5rem !important; } + .cell { + height: 4.5rem !important; + max-height: 4.5rem !important; + } + .border-style { border-top-style: none; border-left-style: none; diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts index bedb3b1..5bbbbdb 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts @@ -1,7 +1,7 @@ -import {Component, Input, OnInit} from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import {PentestService} from '@shared/services/pentest.service'; import {BehaviorSubject, Observable} from 'rxjs'; -import {Pentest, transformPentestToRequestBody} from '@shared/models/pentest.model'; +import {Pentest} from '@shared/models/pentest.model'; import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy'; import {filter, mergeMap, tap} from 'rxjs/operators'; import {NotificationService, PopupType} from '@shared/services/notification.service'; @@ -18,6 +18,9 @@ import {isNotNullOrUndefined} from 'codelyzer/util/isNotNullOrUndefined'; import {FindingDialogService} from '@shared/modules/finding-dialog/service/finding-dialog.service'; import {FindingDialogComponent} from '@shared/modules/finding-dialog/finding-dialog.component'; import {PentestStatus} from '@shared/models/pentest-status.model'; +import {Store} from '@ngxs/store'; +import {UpdatePentestFindings} from '@shared/stores/project-state/project-state.actions'; +import {ProjectState} from '@shared/stores/project-state/project-state'; @UntilDestroy() @Component({ @@ -30,16 +33,17 @@ export class PentestFindingsComponent implements OnInit { constructor(private readonly pentestService: PentestService, private dataSourceBuilder: NbTreeGridDataSourceBuilder, private notificationService: NotificationService, - private findingDialogService: FindingDialogService) { + private findingDialogService: FindingDialogService, + private store: Store) { this.dataSource = dataSourceBuilder.create(this.data, this.getters); } - @Input() pentestInfo$: BehaviorSubject = new BehaviorSubject(null); + loading$: BehaviorSubject = new BehaviorSubject(true); // HTML only readonly fa = FA; - loading$: BehaviorSubject = new BehaviorSubject(true); + notStartedStatus: PentestStatus = PentestStatus.NOT_STARTED; columns: Array = [ FindingColumns.FINDING_ID, FindingColumns.SEVERITY, FindingColumns.TITLE, FindingColumns.IMPACT, FindingColumns.ACTIONS @@ -54,23 +58,35 @@ export class PentestFindingsComponent implements OnInit { expandedGetter: (node: FindingEntry) => !!node.expanded, }; - // HTML only - notStartedStatus: PentestStatus = PentestStatus.NOT_STARTED; - ngOnInit(): void { - this.loadFindingsData(); + this.store.select(ProjectState.pentest).pipe( + untilDestroyed(this) + ).subscribe({ + next: (selectedPentest: Pentest) => { + this.pentestInfo$.next(selectedPentest); + this.loadFindingsData(); + }, + error: err => { + console.error(err); + } + }); } loadFindingsData(): void { this.pentestService.getFindingsByPentestId(this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '') .pipe( untilDestroyed(this), - filter(isNotNullOrUndefined), + /*filter(isNotNullOrUndefined),*/ tap(() => this.loading$.next(true)) ) .subscribe({ next: (findings: Finding[]) => { - this.data = transformFindingsToObjectiveEntries(findings); + // ToDo: Handle this case before in pipe + if (findings) { + this.data = transformFindingsToObjectiveEntries(findings); + } else { + this.data = []; + } this.dataSource.setData(this.data, this.getters); this.loading$.next(false); }, @@ -104,8 +120,8 @@ export class PentestFindingsComponent implements OnInit { ), untilDestroyed(this) ).subscribe({ - next: () => { - // ToDo: Parse new Counter to overview / -> dispatch to store maybe already update it + next: (finding) => { + this.store.dispatch(new UpdatePentestFindings(finding.id)); this.loadFindingsData(); this.notificationService.showPopup('finding.popup.save.success', PopupType.SUCCESS); }, diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.spec.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.spec.ts index 9b19381..77007b4 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.spec.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.spec.ts @@ -9,14 +9,41 @@ import {ThemeModule} from '@assets/@theme/theme.module'; import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; import {HttpLoaderFactory} from '../../../common-app.module'; import {HttpClient} from '@angular/common/http'; -import {NgxsModule} from '@ngxs/store'; -import {ProjectState} from '@shared/stores/project-state/project-state'; +import {NgxsModule, Store} from '@ngxs/store'; +import {PROJECT_STATE_NAME, ProjectState, ProjectStateModel} from '@shared/stores/project-state/project-state'; import {Category} from '@shared/models/category.model'; import {PentestStatus} from '@shared/models/pentest-status.model'; +const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + selectedProject: { + id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', + client: 'E Corp', + title: 'Some Mock API (v1.0) Scanning', + createdAt: new Date('2019-01-10T09:00:00'), + tester: 'Novatester', + testingProgress: 0, + createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' + }, + // Manages Categories + disabledCategories: [], + selectedCategory: Category.INFORMATION_GATHERING, + // Manages Pentests of Category + disabledPentests: [], + selectedPentest: { + id: '56c47c56-3bcd-45f1-a05b-c197dbd33112', + category: Category.INFORMATION_GATHERING, + refNumber: 'OTF-001', + childEntries: [], + status: PentestStatus.NOT_STARTED, + findingIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'], + commentIds: [] + }, +}; + describe('PentestInfoComponent', () => { let component: PentestInfoComponent; let fixture: ComponentFixture; + let store: Store; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -44,6 +71,11 @@ describe('PentestInfoComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(PentestInfoComponent); + store = TestBed.inject(Store); + store.reset({ + ...store.snapshot(), + [PROJECT_STATE_NAME]: DESIRED_PROJECT_STATE_SESSION + }); component = fixture.componentInstance; component.pentestInfo$.next({ id: '56c47c56-3bcd-45f1-a05b-c197dbd33112', diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.ts index 54df9f1..bf58159 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-info/pentest-info.component.ts @@ -3,7 +3,11 @@ import {BehaviorSubject} from 'rxjs'; import {Pentest} from '@shared/models/pentest.model'; import {getPentestInfoForObjective} from '@shared/functions/infos/get-pentest-info-for-objective'; import {getTitleKeyForRefNumber} from '@shared/functions/categories/get-title-key-for-ref-number.function'; +import {ProjectState} from '@shared/stores/project-state/project-state'; +import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy'; +import {Store} from '@ngxs/store'; +@UntilDestroy() @Component({ selector: 'app-pentest-info', templateUrl: './pentest-info.component.html', @@ -11,12 +15,21 @@ import {getTitleKeyForRefNumber} from '@shared/functions/categories/get-title-ke }) export class PentestInfoComponent implements OnInit { - @Input() pentestInfo$: BehaviorSubject = new BehaviorSubject(null); - constructor() { } + constructor(private store: Store) { } ngOnInit(): void { + this.store.selectOnce(ProjectState.pentest).pipe( + untilDestroyed(this) + ).subscribe({ + next: (selectedPentest: Pentest) => { + this.pentestInfo$.next(selectedPentest); + }, + error: err => { + console.error(err); + } + }); } getPentestHeaderForObjective(refNumber: string): string { diff --git a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html index 296b165..e2962fc 100644 --- a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html +++ b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html @@ -14,7 +14,41 @@

{{selectedProjectTitle$.getValue()}} / {{pentest$.getValue().refNumber}}

- + + + + +
+ + + {{ status.translationText | translate }} + + +
+
+ +
+ + + +
diff --git a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.scss b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.scss index 78c5dca..2d69bf0 100644 --- a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.scss +++ b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.scss @@ -4,6 +4,7 @@ .exit-button-container { .exit-element-icon { } + .exit-element-text { padding-left: 0.5rem; } @@ -12,6 +13,35 @@ .pentest-status-container { display: flex; align-content: flex-end; - margin-right: 0.5rem; + // margin-right: 0.5rem; + // height: 5%; + + .pentest-status-dialog { + margin: 1rem 2.25rem 1rem 0; + + .status { + width: 12rem; + } + + .basic { + background-color: nb-theme(color-basic-default); + } + + .info { + background-color: nb-theme(color-info-default); + } + + .warning { + background-color: nb-theme(color-warning-default); + } + + .success { + background-color: nb-theme(color-success-default); + } + } + + .save-pentest-button { + margin: 1rem 0 1rem 0; + } } } diff --git a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.spec.ts b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.spec.ts index 6ff228b..2020be2 100644 --- a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.spec.ts +++ b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.spec.ts @@ -11,8 +11,10 @@ import {NgxsModule, Store} from '@ngxs/store'; import {PROJECT_STATE_NAME, ProjectState, ProjectStateModel} from '@shared/stores/project-state/project-state'; import {Category} from '@shared/models/category.model'; import {PentestStatus} from '@shared/models/pentest-status.model'; +import {NotificationService} from '@shared/services/notification.service'; +import {NotificationServiceMock} from '@shared/services/notification.service.mock'; -const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { +const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -58,6 +60,9 @@ describe('PentestHeaderComponent', () => { }), RouterTestingModule.withRoutes([]), NgxsModule.forRoot([ProjectState]) + ], + providers: [ + {provide: NotificationService, useValue: new NotificationServiceMock()}, ] }) .compileComponents(); diff --git a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts index ef77635..29b91a0 100644 --- a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts +++ b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts @@ -6,10 +6,13 @@ import {Store} from '@ngxs/store'; import {Router} from '@angular/router'; import {ChangePentest} from '@shared/stores/project-state/project-state.actions'; import {BehaviorSubject} from 'rxjs'; -import {PentestStatus} from '@shared/models/pentest-status.model'; import {ProjectState} from '@shared/stores/project-state/project-state'; import {Project} from '@shared/models/project.model'; -import {Pentest} from '@shared/models/pentest.model'; +import {Pentest, transformPentestToRequestBody} from '@shared/models/pentest.model'; +import {NotificationService, PopupType} from '@shared/services/notification.service'; +import {PentestStatus} from '@shared/models/pentest-status.model'; +import {PentestService} from '@shared/services/pentest.service'; +import {StatusText} from '@shared/widgets/status-tag/status-tag.component'; @UntilDestroy() @Component({ @@ -23,8 +26,27 @@ export class PentestHeaderComponent implements OnInit { pentest$: BehaviorSubject = new BehaviorSubject(null); selectedProjectTitle$: BehaviorSubject = new BehaviorSubject(''); + pentestChanged$: BehaviorSubject = new BehaviorSubject(false); + + // Pentest Status Handler + status = PentestStatus; + currentStatus: PentestStatus = PentestStatus.NOT_STARTED; + private initialPentestStatus: PentestStatus; + // Status Text Translation Texts + readonly statusTexts: Array = [ + {value: PentestStatus.NOT_STARTED, translationText: 'pentest.statusText.not_started'}, + /* ToDo: Disabled not needed inside pentest */ + /*{value: PentestStatus.DISABLED, translationText: 'pentest.statusText.disabled'},*/ + {value: PentestStatus.OPEN, translationText: 'pentest.statusText.open'}, + {value: PentestStatus.IN_PROGRESS, translationText: 'pentest.statusText.in_progress'}, + {value: PentestStatus.COMPLETED, translationText: 'pentest.statusText.completed'} + ]; + + selectedProjectId$: BehaviorSubject = new BehaviorSubject(''); constructor(private store: Store, + private pentestService: PentestService, + private notificationService: NotificationService, private readonly router: Router) { } @@ -33,6 +55,7 @@ export class PentestHeaderComponent implements OnInit { untilDestroyed(this) ).subscribe({ next: (selectedProject: Project) => { + this.selectedProjectId$.next(selectedProject.id); this.selectedProjectTitle$.next(selectedProject?.title); }, error: err => { @@ -44,6 +67,8 @@ export class PentestHeaderComponent implements OnInit { untilDestroyed(this) ).subscribe({ next: (selectedPentest: Pentest) => { + this.currentStatus = selectedPentest.status; + this.initialPentestStatus = selectedPentest.status; this.pentest$.next(selectedPentest); }, error: err => { @@ -61,4 +86,76 @@ export class PentestHeaderComponent implements OnInit { } ).finally(); } + + onClickSavePentest(): void { + this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus}); + this.pentestService.savePentest(this.selectedProjectId$.getValue(), transformPentestToRequestBody(this.pentest$.getValue())) + .subscribe({ + next: (pentest: Pentest) => { + this.store.dispatch(new ChangePentest(pentest)); + this.notificationService.showPopup('pentest.popup.save.success', PopupType.SUCCESS); + }, + error: err => { + console.log(err); + this.notificationService.showPopup('pentest.popup.save.failed', PopupType.FAILURE); + } + }); + } + + onClickUpdatePentest(): void { + this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus}); + this.pentestService.updatePentest(transformPentestToRequestBody(this.pentest$.getValue())) + .subscribe({ + next: (pentest: Pentest) => { + this.store.dispatch(new ChangePentest(pentest)); + this.notificationService.showPopup('pentest.popup.update.success', PopupType.SUCCESS); + }, + error: err => { + console.log(err); + this.notificationService.showPopup('pentest.popup.update.failed', PopupType.FAILURE); + } + }); + } + + /** + * @return true if initial pentest Status is different from current pentest status + */ + pentestStatusChanged(): boolean { + if (this.initialPentestStatus !== this.currentStatus) { + this.pentestChanged$.next(true); + } else { + this.pentestChanged$.next(false); + } + return this.pentestChanged$.getValue(); + } + + /** + * @return the correct nb-status for current pentest-status + */ + getPentestFillStatus(value: PentestStatus): string { + let pentestFillStatus; + switch (value) { + case PentestStatus.NOT_STARTED: { + pentestFillStatus = 'basic'; + break; + } + case PentestStatus.OPEN: { + pentestFillStatus = 'info'; + break; + } + case PentestStatus.IN_PROGRESS: { + pentestFillStatus = 'warning'; + break; + } + case PentestStatus.COMPLETED: { + pentestFillStatus = 'success'; + break; + } + default: { + pentestFillStatus = 'basic'; + break; + } + } + return pentestFillStatus; + } } diff --git a/security-c4po-angular/src/shared/services/pentest.service.ts b/security-c4po-angular/src/shared/services/pentest.service.ts index 52cce89..7b1e2ea 100644 --- a/security-c4po-angular/src/shared/services/pentest.service.ts +++ b/security-c4po-angular/src/shared/services/pentest.service.ts @@ -8,8 +8,7 @@ import {Store} from '@ngxs/store'; import {ProjectState} from '@shared/stores/project-state/project-state'; import {catchError, map, switchMap} from 'rxjs/operators'; import {getTempPentestsForCategory} from '@shared/functions/categories/get-temp-pentests-for-category.function'; -import {Finding, FindingDialogBody} from '@shared/models/finding.model'; -import {Severity} from '@shared/models/severity.enum'; +import {Finding} from '@shared/models/finding.model'; import {Comment} from '@shared/models/comment.model'; import {v4 as UUID} from 'uuid'; @@ -85,46 +84,43 @@ export class PentestService { * @param pentestId the id of the project */ public getFindingsByPentestId(pentestId: string): Observable { - if (pentestId) { - return this.http.get(`${this.apiBaseURL}/${pentestId}/findings`); - } else { - // return of([]); - // Todo: Remove mocked Findings - return of([ - { - id: 'ca96cc19-88ff-4874-8406-dc892620afd4', - title: 'This is a creative title', - description: 'test', - impact: 'This impacts only the UI', - severity: Severity.LOW, - reproduction: '' - }, - { - id: 'ca96cc19-88ff-4874-8406-dc892620afd4', - title: 'This is a creative title', - description: 'test', - impact: 'This is impacts some things', - severity: Severity.MEDIUM, - reproduction: '' - }, - { - id: 'ca96cc19-88ff-4874-8406-dc892620afd4', - title: 'This is a creative title', - description: 'test', - impact: 'This is impacts a lot', - severity: Severity.HIGH, - reproduction: '' - }, - { - id: 'ca96cc19-88ff-4874-8406-dc892620afd4', - title: 'This is a creative title', - description: 'test', - impact: 'This is impacts a lot', - severity: Severity.CRITICAL, - reproduction: '' - } - ]); - } + return this.http.get(`${this.apiBaseURL}/${pentestId}/findings`); + // return of([]); + /*Todo: Remove mocked Findings? + return of([ + { + id: 'ca96cc19-88ff-4874-8406-dc892620afd4', + title: 'This is a creative title', + description: 'test', + impact: 'This impacts only the UI', + severity: Severity.LOW, + reproduction: '' + }, + { + id: 'ca96cc19-88ff-4874-8406-dc892620afd4', + title: 'This is a creative title', + description: 'test', + impact: 'This is impacts some things', + severity: Severity.MEDIUM, + reproduction: '' + }, + { + id: 'ca96cc19-88ff-4874-8406-dc892620afd4', + title: 'This is a creative title', + description: 'test', + impact: 'This is impacts a lot', + severity: Severity.HIGH, + reproduction: '' + }, + { + id: 'ca96cc19-88ff-4874-8406-dc892620afd4', + title: 'This is a creative title', + description: 'test', + impact: 'This is impacts a lot', + severity: Severity.CRITICAL, + reproduction: '' + } + ]);*/ } /** @@ -141,26 +137,22 @@ export class PentestService { * @param pentestId the id of the project */ public getCommentsByPentestId(pentestId: string): Observable { - console.warn('Comments for:', pentestId); - if (pentestId) { - return this.http.get(`${this.apiBaseURL}/${pentestId}/comments`); - } else { - // return of([]); - // Todo: Remove mocked Comments - return of([ - { - id: 'ca96cc19-88ff-4874-8406-dc892620afd2', - title: 'This is a creative title', - description: 'This is a creative description', - relatedFindings: ['ca96cc19-88ff-4874-8406-dc892620afd4'], - }, - { - id: 'ca96cc19-88ff-4874-8406-dc892620afd4', - title: 'This is a creative title', - description: 'This is a creative description', - relatedFindings: [], - } - ]); - } + return this.http.get(`${this.apiBaseURL}/${pentestId}/comments`); + // return of([]); + /* ToDo: Use mocked Comments? + return of([ + { + id: 'ca96cc19-88ff-4874-8406-dc892620afd2', + title: 'This is a creative title', + description: 'This is a creative description', + relatedFindings: ['ca96cc19-88ff-4874-8406-dc892620afd4'], + }, + { + id: 'ca96cc19-88ff-4874-8406-dc892620afd4', + title: 'This is a creative title', + description: 'This is a creative description', + relatedFindings: [], + } + ]);*/ } } diff --git a/security-c4po-angular/src/shared/stores/project-state/project-state.actions.ts b/security-c4po-angular/src/shared/stores/project-state/project-state.actions.ts index 3110836..d97a00d 100644 --- a/security-c4po-angular/src/shared/stores/project-state/project-state.actions.ts +++ b/security-c4po-angular/src/shared/stores/project-state/project-state.actions.ts @@ -33,3 +33,10 @@ export class ChangePentest { constructor(public pentest: Pentest) { } } + +export class UpdatePentestFindings { + static readonly type = '[ProjectState] UpdatePentestFindings'; + + constructor(public findingId: string) { + } +} diff --git a/security-c4po-angular/src/shared/stores/project-state/project-state.ts b/security-c4po-angular/src/shared/stores/project-state/project-state.ts index 2e0ef30..07be0d9 100644 --- a/security-c4po-angular/src/shared/stores/project-state/project-state.ts +++ b/security-c4po-angular/src/shared/stores/project-state/project-state.ts @@ -1,7 +1,13 @@ import {Action, Selector, State, StateContext} from '@ngxs/store'; import {Injectable} from '@angular/core'; import {Project} from '@shared/models/project.model'; -import {ChangeCategory, ChangePentest, ChangeProject, InitProjectState} from '@shared/stores/project-state/project-state.actions'; +import { + ChangeCategory, + ChangePentest, + ChangeProject, + InitProjectState, + UpdatePentestFindings +} from '@shared/stores/project-state/project-state.actions'; import {Category} from '@shared/models/category.model'; import {Pentest} from '@shared/models/pentest.model'; @@ -80,4 +86,26 @@ export class ProjectState { selectedPentest: {...pentest, projectId: state.selectedProject.id} }); } + + @Action(UpdatePentestFindings) + updatePentestFindings(ctx: StateContext, {findingId}: UpdatePentestFindings): void { + const state = ctx.getState(); + let stateSelectedPentest: Pentest = state.selectedPentest; + const stateFindingIds: Array = stateSelectedPentest.findingIds || []; + let updatedFindingIds: Array = []; + if (!stateFindingIds.includes(findingId)) { + updatedFindingIds = [...stateFindingIds, findingId]; + } else { + // ToDo: Add logic to remove findingId from array + } + // overwrites only findingIds + stateSelectedPentest = { + ...stateSelectedPentest, + findingIds: updatedFindingIds + }; + // path project state + ctx.patchState({ + selectedPentest: stateSelectedPentest + }); + } } diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectController.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectController.kt index b0193f2..63015af 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectController.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectController.kt @@ -45,6 +45,7 @@ class ProjectController(private val projectService: ProjectService) { @DeleteMapping("/{id}") fun deleteProject(@PathVariable(value = "id") id: String): Mono> { + // ToDo: Delete all associated Pentests, Findings and Comments return this.projectService.deleteProject(id).map{ ResponseEntity.ok().body(it.toProjectDeleteResponseBody()) }.switchIfEmpty {