feat: As an user I want to add the pentest status of a project
This commit is contained in:
parent
2c7ac85f6e
commit
9e4fa27b92
|
@ -9,6 +9,7 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<app-report-state-tag class="state-tag" [currentReportState]="selectedProject$.getValue()?.state"></app-report-state-tag>
|
||||
<h4>{{selectedProject$.getValue().title}}</h4>
|
||||
|
||||
<div class="button-container">
|
||||
|
|
|
@ -24,6 +24,7 @@ import {Category} from '@shared/models/category.model';
|
|||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||
import {ExportReportDialogService} from '@shared/modules/export-report-dialog/service/export-report-dialog.service';
|
||||
import {ExportReportDialogServiceMock} from '@shared/modules/export-report-dialog/service/export-report-dialog.service.mock';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -33,6 +34,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -75,21 +75,16 @@ export class ObjectiveHeaderComponent implements OnInit {
|
|||
closeOnBackdropClick: false
|
||||
}
|
||||
).pipe(
|
||||
filter(value => !!value),
|
||||
mergeMap((value: ProjectDialogBody) => this.projectService.updateProject(this.selectedProject$.getValue().id, value)),
|
||||
untilDestroyed(this)
|
||||
).subscribe({
|
||||
next: (project: Project) => {
|
||||
next: (project) => {
|
||||
this.store.dispatch(new InitProjectState(
|
||||
project,
|
||||
[],
|
||||
[]
|
||||
)).pipe(untilDestroyed(this)).subscribe();
|
||||
this.notificationService.showPopup('project.popup.update.success', PopupType.SUCCESS);
|
||||
},
|
||||
error: error => {
|
||||
console.error(error);
|
||||
this.notificationService.showPopup('project.popup.update.failed', PopupType.FAILURE);
|
||||
)).pipe(
|
||||
untilDestroyed(this)
|
||||
).subscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import {ObjectiveOverviewRoutingModule} from './objective-overview-routing.modul
|
|||
import {ExportReportDialogModule} from '@shared/modules/export-report-dialog/export-report-dialog.module';
|
||||
import {ProjectDialogModule} from '@shared/modules/project-dialog/project-dialog.module';
|
||||
import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget.module';
|
||||
import {ReportStateTagModule} from '@shared/widgets/report-state-tag/report-state-tag.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -56,7 +57,8 @@ import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget
|
|||
// Table Widgets
|
||||
FindigWidgetModule,
|
||||
CommentWidgetModule,
|
||||
NbMenuModule
|
||||
NbMenuModule,
|
||||
ReportStateTagModule
|
||||
],
|
||||
exports: [
|
||||
ObjectiveHeaderComponent,
|
||||
|
|
|
@ -22,6 +22,7 @@ import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
|||
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
|
||||
import {CommentDialogService} from '@shared/modules/comment-dialog/service/comment-dialog.service';
|
||||
import {CommentDialogServiceMock} from '@shared/modules/comment-dialog/service/comment-dialog.service.mock';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -31,6 +32,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@ import {Category} from '@shared/models/category.model';
|
|||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||
import {NotificationService} from '@shared/services/toaster-service/notification.service';
|
||||
import {NotificationServiceMock} from '@shared/services/toaster-service/notification.service.mock';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -22,6 +23,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -22,6 +22,7 @@ import {FindingDialogService} from '@shared/modules/finding-dialog/service/findi
|
|||
import {FindingDialogServiceMock} from '@shared/modules/finding-dialog/service/finding-dialog.service.mock';
|
||||
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -31,6 +32,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@ 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 {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -22,6 +23,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@ import {Category} from '@shared/models/category.model';
|
|||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||
import {NotificationService} from '@shared/services/toaster-service/notification.service';
|
||||
import {NotificationServiceMock} from '@shared/services/toaster-service/notification.service.mock';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -22,6 +23,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -1,12 +1,63 @@
|
|||
<div fxLayout="row" fxLayoutGap="2rem">
|
||||
<div fxFlex="0 1 max-content" fxLayout="column" class="pentest-overview">
|
||||
<nb-layout fxFlex>
|
||||
<!--Header-->
|
||||
<nb-layout-header class="pentest-overview-header">
|
||||
<div fxLayout="row" fxLayoutGap="2rem" fxLayoutAlign="space-between center">
|
||||
<!--Filter-->
|
||||
<div fxLayout="row" fxLayoutGap="1rem" class="header-filer">
|
||||
<!--Actions-->
|
||||
<!--ToDo: Add searchbar that filters for title, client, tester-->
|
||||
<form class="project-filter-input">
|
||||
<nb-form-field>
|
||||
<fa-icon nbPrefix class ="search-prefix-icon" [icon]="fa.faSearch"></fa-icon>
|
||||
<input type="text" required
|
||||
fullWidth nbInput
|
||||
[formControl]="projectSearch"
|
||||
placeholder="{{ 'project.filter.placeholder' | translate }}"
|
||||
shape="semi-round"
|
||||
fieldSize="medium"
|
||||
status="basic">
|
||||
</nb-form-field>
|
||||
</form>
|
||||
<!--ToDo: Add dropdown to filter for specific state-->
|
||||
<button nbButton
|
||||
status="danger"
|
||||
size="medium"
|
||||
shape="semi-round"
|
||||
class="reset-filter-btn"
|
||||
(click)="onClickResetFilter()">
|
||||
<fa-icon [icon]="fa.faFilterCircleXmark" class="btn-icon"></fa-icon>
|
||||
{{'global.action.reset' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
<!--Button-->
|
||||
<div class="header-project-button">
|
||||
<button nbButton hero
|
||||
status="info"
|
||||
size="medium"
|
||||
shape="round"
|
||||
class="add-project-button"
|
||||
(click)="onClickAddProject()">
|
||||
<fa-icon [icon]="fa.faPlus" class="btn-icon"></fa-icon>
|
||||
{{'project.overview.add.project' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nb-layout-header>
|
||||
<!--Column-->
|
||||
<nb-layout-column class="pentest-overview-column">
|
||||
<div fxLayout="row" fxLayoutGap="2rem">
|
||||
<div *ngFor="let project of projects$ | async">
|
||||
<nb-card class="project-card" accent="success">
|
||||
<nb-card class="project-card" accent="{{getProjectAccentFillStatus(project?.state)}}">
|
||||
<nb-card-header fxLayoutAlign="start center"
|
||||
routerLink="id"
|
||||
fragment="{{project.id}}"
|
||||
class="project-link project-header"
|
||||
(click)="onClickRouteToProject(project)">
|
||||
<h4>{{project?.title}}</h4>
|
||||
<div fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<h4 class="header-title">{{project?.title}}</h4>
|
||||
<app-report-state-tag class="state-tag" [currentReportState]="project?.state"></app-report-state-tag>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body class="project-link"
|
||||
routerLink="id"
|
||||
|
@ -64,24 +115,17 @@
|
|||
</nb-card-footer>
|
||||
</nb-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="projects$.getValue() == null || projects$.getValue().length === 0 && loading$.getValue() === false" fxLayout="row" fxLayoutAlign="center center">
|
||||
</div>
|
||||
<!--Error Text-->
|
||||
<div *ngIf="projects$.getValue() == null || projects$.getValue().length === 0 && loading$.getValue() === false"
|
||||
fxLayout="row" fxLayoutAlign="center center">
|
||||
<p class="error-text">
|
||||
{{'project.overview.no.projects' | translate}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!--Loading Spinner-->
|
||||
<app-loading-spinner [isLoading$]="isLoading()" *ngIf="isLoading() | async"></app-loading-spinner>
|
||||
|
||||
<div fxLayoutAlign="end end">
|
||||
<button nbButton hero
|
||||
status="info"
|
||||
size="large"
|
||||
shape="round"
|
||||
class="add-project-button"
|
||||
(click)="onClickAddProject()">
|
||||
<fa-icon [icon]="fa.faPlus" class="new-project-icon"></fa-icon>
|
||||
{{'project.overview.add.project' | translate}}
|
||||
</button>
|
||||
</nb-layout-column>
|
||||
</nb-layout>
|
||||
</div>
|
||||
|
||||
<app-loading-spinner [isLoading$]="isLoading()" *ngIf="isLoading() | async"></app-loading-spinner>
|
||||
|
|
|
@ -1,6 +1,60 @@
|
|||
@import '../../assets/@theme/styles/themes';
|
||||
@import '../../assets/@theme/styles/variables';
|
||||
|
||||
.project-card {
|
||||
.pentest-overview {
|
||||
width: 100vw;
|
||||
height: 80vh;
|
||||
// ToDo: Disable and fix scrolling
|
||||
overflow: hidden;
|
||||
|
||||
.pentest-overview-header {
|
||||
width: 100vw;
|
||||
|
||||
.header-filer {
|
||||
|
||||
.project-filter-input {
|
||||
width: 24rem;
|
||||
|
||||
.search-prefix-icon {
|
||||
color:nb-theme(color-info-default);
|
||||
}
|
||||
}
|
||||
|
||||
.state-dialog {
|
||||
margin-left: auto;
|
||||
margin-right: 0;
|
||||
|
||||
.states {
|
||||
width: 14rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.reset-filter-btn {
|
||||
.btn-icon {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-project-button {
|
||||
position: fixed;
|
||||
right: 1.5rem;
|
||||
|
||||
.add-project-button {
|
||||
// align-content: flex-end;
|
||||
margin: 6rem 2rem 6rem 0;
|
||||
|
||||
.btn-icon {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pentest-overview-column {
|
||||
width: 100vw;
|
||||
|
||||
.project-card {
|
||||
max-width: 22rem;
|
||||
width: 22rem;
|
||||
min-width: 20rem;
|
||||
|
@ -12,6 +66,14 @@
|
|||
max-height: 8rem;
|
||||
height: 8rem;
|
||||
min-height: 6rem;
|
||||
|
||||
.header-title {
|
||||
width: 12.5rem;
|
||||
}
|
||||
|
||||
.state-tag {
|
||||
width: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.project-subheader {
|
||||
|
@ -33,9 +95,9 @@
|
|||
.project-button {
|
||||
height: 1.425rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-card:hover {
|
||||
.project-card:hover {
|
||||
background-color: nb-theme(color-basic-transparent-focus);
|
||||
// Increases element size on hover
|
||||
// Decreases usability which is why it is commented out
|
||||
|
@ -43,20 +105,14 @@
|
|||
margin-top: +0.625rem;
|
||||
transform: scale(1.025);
|
||||
*/
|
||||
}
|
||||
|
||||
.project-link:hover {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.add-project-button {
|
||||
margin: 6rem 2rem 6rem 0;
|
||||
.new-project-icon {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.error-text {
|
||||
.project-link:hover {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
.error-text {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import {BehaviorSubject, Observable} from 'rxjs';
|
|||
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
|
||||
import {ProjectService} from '@shared/services/api/project.service';
|
||||
import {NotificationService, PopupType} from '@shared/services/toaster-service/notification.service';
|
||||
import {filter, tap} from 'rxjs/operators';
|
||||
import {filter, startWith, tap} from 'rxjs/operators';
|
||||
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||
import {ProjectDialogComponent} from '@shared/modules/project-dialog/project-dialog.component';
|
||||
import {ProjectDialogService} from '@shared/modules/project-dialog/service/project-dialog.service';
|
||||
|
@ -13,6 +13,8 @@ import {Router} from '@angular/router';
|
|||
import {Route} from '@shared/models/route.enum';
|
||||
import {InitProjectState} from '@shared/stores/project-state/project-state.actions';
|
||||
import {Store} from '@ngxs/store';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
import {FormControl} from '@angular/forms';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
|
@ -26,6 +28,11 @@ export class ProjectOverviewComponent implements OnInit {
|
|||
|
||||
loading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
projects$: BehaviorSubject<Project[]> = new BehaviorSubject<Project[]>([]);
|
||||
allProjects$: BehaviorSubject<Project[]> = new BehaviorSubject<Project[]>([]);
|
||||
|
||||
// Search
|
||||
projectSearch: FormControl;
|
||||
protected filter$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private readonly notificationService: NotificationService,
|
||||
|
@ -38,6 +45,9 @@ export class ProjectOverviewComponent implements OnInit {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.loadProjects();
|
||||
// Setup Search
|
||||
this.projectSearch = new FormControl({value: '', disabled: !this.allProjects$.getValue()});
|
||||
this.setFilterObserverForProjects();
|
||||
}
|
||||
|
||||
loadProjects(): void {
|
||||
|
@ -49,6 +59,7 @@ export class ProjectOverviewComponent implements OnInit {
|
|||
.subscribe({
|
||||
next: (projects: Project[]) => {
|
||||
this.projects$.next(projects);
|
||||
this.allProjects$.next(projects);
|
||||
this.loading$.next(false);
|
||||
},
|
||||
error: err => {
|
||||
|
@ -156,6 +167,74 @@ export class ProjectOverviewComponent implements OnInit {
|
|||
return this.loading$.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML only
|
||||
* @return the correct nb-accent for current report state of the project
|
||||
*/
|
||||
getProjectAccentFillStatus(value: any): string {
|
||||
let reportStateFillStatus;
|
||||
const statusValue = typeof value !== 'number' ? ReportState[value] : value;
|
||||
// Check for correct accent color of status
|
||||
switch (statusValue) {
|
||||
case 6:
|
||||
case 7: {
|
||||
reportStateFillStatus = 'success';
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
reportStateFillStatus = 'info';
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
case 11:
|
||||
case 12: {
|
||||
reportStateFillStatus = 'warning';
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 10: {
|
||||
reportStateFillStatus = 'danger';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
reportStateFillStatus = 'control';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return reportStateFillStatus;
|
||||
}
|
||||
|
||||
onClickResetFilter(): void {
|
||||
this.projectSearch.reset('');
|
||||
this.projects$.next(this.allProjects$.getValue());
|
||||
}
|
||||
|
||||
private setFilterObserverForProjects(): void {
|
||||
this.filter$ = this.projectSearch.valueChanges.pipe(startWith(''));
|
||||
this.filter$.subscribe(
|
||||
(filterString: string) => {
|
||||
if (filterString.length === 0) {
|
||||
this.projects$.next(this.allProjects$.getValue());
|
||||
} else {
|
||||
const matchingProjects: Project[] = [];
|
||||
this.allProjects$.getValue().forEach(project => {
|
||||
// Project attributes that the user can filter through
|
||||
if (
|
||||
project.title.toLowerCase().includes(filterString.toLowerCase())
|
||||
|| project.client.toLowerCase().includes(filterString.toLowerCase())
|
||||
|| project.tester.toLowerCase().includes(filterString.toLowerCase())
|
||||
|| project.state.toString().toLowerCase().includes(filterString.toLowerCase())
|
||||
) {
|
||||
matchingProjects.push(project);
|
||||
}
|
||||
});
|
||||
this.projects$.next(matchingProjects);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private deleteProject(project: Project): void {
|
||||
this.projectService.deleteProjectById(project.id).pipe(
|
||||
untilDestroyed(this)
|
||||
|
|
|
@ -2,7 +2,15 @@ import {NgModule} from '@angular/core';
|
|||
import {CommonModule} from '@angular/common';
|
||||
import {ProjectOverviewComponent} from './project-overview.component';
|
||||
import {ProjectOverviewRoutingModule} from './project-overview-routing.module';
|
||||
import {NbButtonModule, NbCardModule, NbProgressBarModule} from '@nebular/theme';
|
||||
import {
|
||||
NbButtonModule,
|
||||
NbCardModule,
|
||||
NbFormFieldModule,
|
||||
NbInputModule,
|
||||
NbLayoutModule,
|
||||
NbProgressBarModule,
|
||||
NbSelectModule
|
||||
} from '@nebular/theme';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
|
@ -12,6 +20,8 @@ import {CommonAppModule} from '../common-app.module';
|
|||
import {ConfirmDialogModule} from '@shared/modules/confirm-dialog/confirm-dialog.module';
|
||||
import {SecurityConfirmDialogModule} from '@shared/modules/security-confirm-dialog/security-confirm-dialog.module';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {ReportStateTagModule} from '@shared/widgets/report-state-tag/report-state-tag.module';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -34,7 +44,13 @@ import {RouterModule} from '@angular/router';
|
|||
TranslateModule,
|
||||
ProjectDialogModule,
|
||||
ConfirmDialogModule,
|
||||
SecurityConfirmDialogModule
|
||||
ReportStateTagModule,
|
||||
SecurityConfirmDialogModule,
|
||||
NbLayoutModule,
|
||||
NbInputModule,
|
||||
NbFormFieldModule,
|
||||
ReactiveFormsModule,
|
||||
NbSelectModule
|
||||
]
|
||||
})
|
||||
export class ProjectOverviewModule {
|
||||
|
|
|
@ -8,7 +8,6 @@ import {HttpLoaderFactory} from '../../common-app.module';
|
|||
import {HttpClient, HttpClientModule} from '@angular/common/http';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {NgxsModule, Store} from '@ngxs/store';
|
||||
import {SessionState} from '@shared/stores/session-state/session-state';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {NbCardModule, NbDialogRef, NbLayoutModule} from '@nebular/theme';
|
||||
import {KeycloakService} from 'keycloak-angular';
|
||||
|
@ -27,6 +26,7 @@ import {PentestStatus} from '@shared/models/pentest-status.model';
|
|||
import {createSpyObj} from '@shared/modules/finding-dialog/finding-dialog.component.spec';
|
||||
import {ExportReportDialogService} from '@shared/modules/export-report-dialog/service/export-report-dialog.service';
|
||||
import {ExportReportDialogServiceMock} from '@shared/modules/export-report-dialog/service/export-report-dialog.service.mock';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -36,6 +36,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -52,6 +52,21 @@
|
|||
"failed": "Benutzername oder Passwort falsch",
|
||||
"unauthorized": "Benutzer nicht gefunden. Bitte registrieren und erneut versuchen"
|
||||
},
|
||||
"state": {
|
||||
"new": "Neu",
|
||||
"needs_more_info": "Benötigt mehr Informationen",
|
||||
"pre_submission": "Voranmeldung",
|
||||
"pending": "Pending",
|
||||
"triaged": "Ausstehend",
|
||||
"retesting": "Erneutes Testen",
|
||||
"resolved": "Aufgeklärt",
|
||||
"informative": "Informatif",
|
||||
"duplicate": "Duplikat",
|
||||
"not_applicable": "Unzutreffend",
|
||||
"spam": "Spam",
|
||||
"out_of_scope": "Außerhalb Anwendungsbereich",
|
||||
"accepted_risk": "Akzeptiertes Risiko"
|
||||
},
|
||||
"report": {
|
||||
"dialog": {
|
||||
"header": "Penetrationstestbereicht exportieren",
|
||||
|
@ -69,17 +84,22 @@
|
|||
"title.label": "Projekt Titel",
|
||||
"client.label": "Name des Auftraggebers",
|
||||
"tester.label": "Name des Pentester",
|
||||
"state.label": "Penteststatus",
|
||||
"summary.label": "Zusammenfassung",
|
||||
"summary.placeholder": "Sollte eine Zusammenfassung, einen Ansatz, einen Umfang und eine Bewertungsübersicht sowie allgemeine Empfehlungen enthalten",
|
||||
"title": "Titel",
|
||||
"client": "Klient",
|
||||
"tester": "Tester",
|
||||
"state": "Penteststatus",
|
||||
"summary": "Zusammenfassung",
|
||||
"createdAt": "Erstellt am",
|
||||
"overview": {
|
||||
"add.project": "Projekt hinzufügen",
|
||||
"no.projects": "Keine Projekte verfügbar"
|
||||
},
|
||||
"filter": {
|
||||
"placeholder": "Projekt suchen"
|
||||
},
|
||||
"create": {
|
||||
"header": "Neues Projekt erstellen"
|
||||
},
|
||||
|
@ -96,6 +116,7 @@
|
|||
"titleRequired": "Titel ist erforderlich.",
|
||||
"clientRequired": "Klient ist erforderlich.",
|
||||
"testerRequired": "Tester ist erforderlich.",
|
||||
"stateRequired": "Status ist erforderlich.",
|
||||
"summaryRequired": "Zusammenfassung ist erforderlich."
|
||||
},
|
||||
"popup": {
|
||||
|
|
|
@ -52,6 +52,21 @@
|
|||
"failed": "Wrong username or password",
|
||||
"unauthorized": "User not found. Please register and try again"
|
||||
},
|
||||
"state": {
|
||||
"new": "New",
|
||||
"needs_more_info": "Needs More Info",
|
||||
"pre_submission": "Pre-submission",
|
||||
"pending": "Pending",
|
||||
"triaged": "Triaged",
|
||||
"retesting": "Retesting",
|
||||
"resolved": "Resolved",
|
||||
"informative": "Informative",
|
||||
"duplicate": "Duplicate",
|
||||
"not_applicable": "Not Applicable",
|
||||
"spam": "Spam",
|
||||
"out_of_scope": "Out Of Scope",
|
||||
"accepted_risk": "Accepted Risk"
|
||||
},
|
||||
"report": {
|
||||
"dialog": {
|
||||
"header": "Export Penetrationtest Report",
|
||||
|
@ -69,17 +84,22 @@
|
|||
"title.label": "Project Title",
|
||||
"client.label": "Name of Client",
|
||||
"tester.label": "Name of Pentester",
|
||||
"state.label": "State of Pentest",
|
||||
"summary.label": "Summary",
|
||||
"summary.placeholder": "Should include Executive Summary, Approach, Scope and Assessment Overview as well as General Recommendations",
|
||||
"title": "Title",
|
||||
"client": "Client",
|
||||
"tester": "Tester",
|
||||
"state": "State of Pentest",
|
||||
"summary": "Summary",
|
||||
"createdAt": "Created at",
|
||||
"overview": {
|
||||
"add.project": "Add Project",
|
||||
"no.projects": "No projects available"
|
||||
},
|
||||
"filter": {
|
||||
"placeholder": "Search for project"
|
||||
},
|
||||
"create": {
|
||||
"header": "Create New Project"
|
||||
},
|
||||
|
@ -96,6 +116,7 @@
|
|||
"titleRequired": "Title is required.",
|
||||
"clientRequired": "Client is required.",
|
||||
"testerRequired": "Tester is required.",
|
||||
"stateRequired": "State is required.",
|
||||
"summaryRequired": "Summary is required."
|
||||
},
|
||||
"popup": {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
export class Project {
|
||||
id: string;
|
||||
|
@ -7,6 +8,7 @@ export class Project {
|
|||
createdAt: Date;
|
||||
tester: string;
|
||||
summary: string;
|
||||
state: ReportState;
|
||||
projectPentests?: Array<ProjectPentests>;
|
||||
testingProgress?: number;
|
||||
createdBy: string;
|
||||
|
@ -16,6 +18,7 @@ export class Project {
|
|||
title: string,
|
||||
createdAt: Date,
|
||||
tester: string,
|
||||
state: ReportState,
|
||||
projectPentests?: Array<ProjectPentests>,
|
||||
testingProgress?: number,
|
||||
summary?: string,
|
||||
|
@ -28,14 +31,28 @@ export class Project {
|
|||
this.projectPentests = projectPentests;
|
||||
this.testingProgress = testingProgress;
|
||||
this.summary = summary;
|
||||
this.state = state;
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
}
|
||||
|
||||
export function transformProjectToRequestBody(project: ProjectDialogBody | Project): ProjectDialogBody {
|
||||
const transformedProject = {
|
||||
...project,
|
||||
title: project.title,
|
||||
client: project.client,
|
||||
tester: project.tester,
|
||||
state: typeof project.state === 'number' ? ReportState[project.state] : project.state,
|
||||
summary: project.summary,
|
||||
} as unknown as ProjectDialogBody;
|
||||
return transformedProject;
|
||||
}
|
||||
|
||||
export interface ProjectDialogBody {
|
||||
title: string;
|
||||
client: string;
|
||||
tester: string;
|
||||
state: ReportState;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
export enum ReportState {
|
||||
NEW,
|
||||
NEEDS_MORE_INFO,
|
||||
// Report states depending on customer feedback
|
||||
PRE_SUBMISSION,
|
||||
PENDING,
|
||||
TRIAGED,
|
||||
RETESTING,
|
||||
// Report states for closed submissions
|
||||
RESOLVED,
|
||||
INFORMATIVE,
|
||||
DUPLICATE,
|
||||
NOT_APPLICABLE,
|
||||
SPAM,
|
||||
OUT_OF_SCOPE,
|
||||
ACCEPTED_RISK
|
||||
}
|
||||
|
||||
export const reportStateTexts: Array<ReportStateText> = [
|
||||
{value: ReportState.NEW, translationText: 'state.new'},
|
||||
{value: ReportState.NEEDS_MORE_INFO, translationText: 'state.needs_more_info'},
|
||||
// Report states depending on customer feedback
|
||||
{value: ReportState.PRE_SUBMISSION, translationText: 'state.pre_submission'},
|
||||
{value: ReportState.PENDING, translationText: 'state.pending'},
|
||||
{value: ReportState.TRIAGED, translationText: 'state.triaged'},
|
||||
{value: ReportState.RETESTING, translationText: 'state.retesting'},
|
||||
// Report states for closed submissions
|
||||
{value: ReportState.RESOLVED, translationText: 'state.resolved'},
|
||||
{value: ReportState.INFORMATIVE, translationText: 'state.informative'},
|
||||
{value: ReportState.DUPLICATE, translationText: 'state.duplicate'},
|
||||
{value: ReportState.NOT_APPLICABLE, translationText: 'state.not_applicable'},
|
||||
{value: ReportState.SPAM, translationText: 'state.spam'},
|
||||
{value: ReportState.OUT_OF_SCOPE, translationText: 'state.out_of_scope'},
|
||||
{value: ReportState.ACCEPTED_RISK, translationText: 'state.accepted_risk'}
|
||||
];
|
||||
|
||||
export interface ReportStateText {
|
||||
value: ReportState;
|
||||
translationText: string;
|
||||
}
|
|
@ -32,6 +32,7 @@ import Mock = jest.Mock;
|
|||
import {Finding} from '@shared/models/finding.model';
|
||||
import {Severity} from '@shared/models/severity.enum';
|
||||
import {Comment} from '@shared/models/comment.model';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -41,6 +42,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@ import {Project, ProjectPentests} from '@shared/models/project.model';
|
|||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||
import {ObjectiveChartModule} from '@shared/modules/objective-chart/objective-chart.module';
|
||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
describe('ExportReportDialogComponent', () => {
|
||||
let component: ExportReportDialogComponent;
|
||||
|
@ -102,6 +103,7 @@ const mockProject: Project = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
projectPentests: mockedPentests,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
|
|
|
@ -30,6 +30,7 @@ import {Category} from '@shared/models/category.model';
|
|||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||
import {PROJECT_STATE_NAME, ProjectState, ProjectStateModel} from '@shared/stores/project-state/project-state';
|
||||
import {NgxsModule, Store} from '@ngxs/store';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||
selectedProject: {
|
||||
|
@ -39,6 +40,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
|||
createdAt: new Date('2019-01-10T09:00:00'),
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
},
|
||||
|
@ -97,13 +99,13 @@ describe('FindingDialogComponent', () => {
|
|||
{provide: NotificationService, useValue: new NotificationServiceMock()},
|
||||
{provide: DialogService, useClass: DialogServiceMock},
|
||||
{provide: NbDialogRef, useValue: dialogSpy},
|
||||
{provide: NB_DIALOG_CONFIG, useValue: mockedCommentDialogData}
|
||||
{provide: NB_DIALOG_CONFIG, useValue: mockedFindingDialogData}
|
||||
]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.overrideProvider(NB_DIALOG_CONFIG, {useValue: mockedCommentDialogData});
|
||||
TestBed.overrideProvider(NB_DIALOG_CONFIG, {useValue: mockedFindingDialogData});
|
||||
fixture = TestBed.createComponent(FindingDialogComponent);
|
||||
store = TestBed.inject(Store);
|
||||
store.reset({
|
||||
|
@ -138,7 +140,7 @@ export const mockFinding: Finding = {
|
|||
mitigation: 'Mitigation Test'
|
||||
};
|
||||
|
||||
export const mockedCommentDialogData = {
|
||||
export const mockedFindingDialogData = {
|
||||
form: {
|
||||
findingTitle: {
|
||||
fieldName: 'findingTitle',
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
<nb-card #dialog accent="{{dialogData?.options[0].accentColor}}" class="project-dialog">
|
||||
<nb-card-header fxLayoutAlign="start center" class="dialog-header">
|
||||
{{ dialogData?.options[0].headerLabelKey | translate }}
|
||||
<!-- Pents State Dropdown -->
|
||||
<div class="state-dialog">
|
||||
<nb-select class="states"
|
||||
type="state-select"
|
||||
[disabled]="!dialogData.options[0].additionalData"
|
||||
[(selected)]="formArray[4].controlsConfig[0].value"
|
||||
shape="round" status="{{getReportStateFillStatus(formArray[4].controlsConfig[0].value)}}" filled>
|
||||
<nb-option *ngFor="let reportState of reportStateTexts" [value]="reportState.value">
|
||||
{{ reportState.translationText | translate }}
|
||||
</nb-option>
|
||||
</nb-select>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<form *ngIf="formArray" [formGroup]="projectFormGroup" fxLayout="column" fxLayoutGap="1rem"
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
@import '../../../assets/@theme/styles/themes';
|
||||
|
||||
.project-dialog {
|
||||
width: 34rem !important;
|
||||
width: 36rem !important;
|
||||
height: 43.5rem;
|
||||
|
||||
.project-dialog-header {
|
||||
height: 8vh;
|
||||
height: 10vh;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
|
@ -21,19 +21,19 @@
|
|||
}
|
||||
|
||||
.form-field {
|
||||
width: 26.75rem;
|
||||
width: 32.75rem;
|
||||
// width: 30rem !important;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
width: 26.75rem !important;
|
||||
width: 32.75rem !important;
|
||||
// width: 30rem !important;
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.form-textarea:disabled {
|
||||
width: 26.75rem !important;
|
||||
width: 32.75rem !important;
|
||||
// width: 30rem !important;
|
||||
background-color: nb-theme(color-basic-transparent-focus);
|
||||
height: 8rem;
|
||||
|
@ -43,4 +43,14 @@
|
|||
float: left;
|
||||
color: nb-theme(color-danger-default);
|
||||
}
|
||||
|
||||
.state-dialog {
|
||||
margin-left: auto;
|
||||
margin-right: 0;
|
||||
// padding: 0.5rem 0 0.75rem;
|
||||
|
||||
.states {
|
||||
width: 14rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
NbDialogRef,
|
||||
NbFormFieldModule,
|
||||
NbInputModule,
|
||||
NbLayoutModule
|
||||
NbLayoutModule, NbSelectModule
|
||||
} from '@nebular/theme';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
|
@ -24,6 +24,8 @@ import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.
|
|||
import {ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {Project} from '@shared/models/project.model';
|
||||
import Mock = jest.Mock;
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
import {GenericFormFieldConfig} from '@shared/models/generic-dialog-data';
|
||||
|
||||
describe('ProjectDialogComponent', () => {
|
||||
let component: ProjectDialogComponent;
|
||||
|
@ -43,6 +45,7 @@ describe('ProjectDialogComponent', () => {
|
|||
NbButtonModule,
|
||||
FlexLayoutModule,
|
||||
NbInputModule,
|
||||
NbSelectModule,
|
||||
NbFormFieldModule,
|
||||
ReactiveFormsModule,
|
||||
BrowserAnimationsModule,
|
||||
|
@ -70,7 +73,8 @@ describe('ProjectDialogComponent', () => {
|
|||
TestBed.overrideProvider(NB_DIALOG_CONFIG, {useValue: mockedProjectDialogData});
|
||||
fixture = TestBed.createComponent(ProjectDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
// ToDo: fix detectChanges() when controlsConfig is defined
|
||||
// fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
|
@ -91,7 +95,8 @@ export const mockProject: Project = {
|
|||
title: 'Test Project',
|
||||
client: 'Testclient',
|
||||
tester: 'Testpentester',
|
||||
summary: '',
|
||||
summary: 'Test',
|
||||
state: ReportState.NEW,
|
||||
createdAt: new Date(),
|
||||
testingProgress: 0,
|
||||
createdBy: 'UID-11-12-13'
|
||||
|
@ -137,13 +142,27 @@ export const mockedProjectDialogData = {
|
|||
errors: [
|
||||
{errorCode: 'required', translationKey: 'project.validationMessage.testerRequired'}
|
||||
]
|
||||
}
|
||||
},
|
||||
pentestState: {
|
||||
fieldName: 'pentestState',
|
||||
type: 'state-select',
|
||||
labelKey: 'project.state.label',
|
||||
placeholder: 'project.state',
|
||||
controlsConfig: [
|
||||
{value: mockProject ? mockProject.state : ReportState.NEW, disabled: false},
|
||||
[Validators.required]
|
||||
],
|
||||
errors: [
|
||||
{errorCode: 'required', translationKey: 'project.validationMessage.stateRequired'}
|
||||
]
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
headerLabelKey: 'project.edit.header',
|
||||
buttonKey: 'global.action.update',
|
||||
accentColor: 'warning'
|
||||
accentColor: 'warning',
|
||||
additionalData: mockProject
|
||||
},
|
||||
]
|
||||
};
|
||||
|
|
|
@ -7,6 +7,9 @@ import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
|
|||
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||
import {ProjectService} from '@shared/services/api/project.service';
|
||||
import {NotificationService, PopupType} from '@shared/services/toaster-service/notification.service';
|
||||
import * as FA from '@fortawesome/free-solid-svg-icons';
|
||||
import {ReportState, reportStateTexts} from '@shared/models/state.enum';
|
||||
import {Project, transformProjectToRequestBody} from '@shared/models/project.model';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
|
@ -21,6 +24,11 @@ export class ProjectDialogComponent implements OnInit {
|
|||
|
||||
dialogData: GenericDialogData;
|
||||
|
||||
// HTML only
|
||||
readonly fa = FA;
|
||||
state: ReportState = ReportState.NEW;
|
||||
readonly reportStateTexts = reportStateTexts;
|
||||
|
||||
constructor(
|
||||
@Inject(NB_DIALOG_CONFIG) private data: GenericDialogData,
|
||||
private fb: FormBuilder,
|
||||
|
@ -63,6 +71,43 @@ export class ProjectDialogComponent implements OnInit {
|
|||
return this.projectFormGroup.valid && this.projectDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML only
|
||||
* @return the correct nb-status for current report state of the project
|
||||
*/
|
||||
getReportStateFillStatus(value: number): string {
|
||||
let reportStateFillStatus;
|
||||
switch (value) {
|
||||
case 6:
|
||||
case 7: {
|
||||
reportStateFillStatus = 'success';
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
reportStateFillStatus = 'info';
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
case 11:
|
||||
case 12: {
|
||||
reportStateFillStatus = 'warning';
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 10: {
|
||||
reportStateFillStatus = 'danger';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
reportStateFillStatus = 'basic';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return reportStateFillStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if project data is different from initial value
|
||||
*/
|
||||
|
@ -98,9 +143,12 @@ export class ProjectDialogComponent implements OnInit {
|
|||
title: value.projectTitle,
|
||||
client: value.projectClient,
|
||||
tester: value.projectTester,
|
||||
state: this.formArray[4].controlsConfig[0].value,
|
||||
summary: value.projectSummary
|
||||
};
|
||||
this.projectService.saveProject(dialogRes).pipe(
|
||||
this.projectService.saveProject(
|
||||
transformProjectToRequestBody(dialogRes)
|
||||
).pipe(
|
||||
untilDestroyed(this)
|
||||
).subscribe(
|
||||
{
|
||||
|
@ -122,15 +170,19 @@ export class ProjectDialogComponent implements OnInit {
|
|||
title: value.projectTitle,
|
||||
client: value.projectClient,
|
||||
tester: value.projectTester,
|
||||
state: this.formArray[4].controlsConfig[0].value,
|
||||
summary: value.projectSummary
|
||||
};
|
||||
this.projectService.updateProject(this.dialogData.options[0].additionalData.id, dialogRes).pipe(
|
||||
this.projectService.updateProject(
|
||||
this.dialogData.options[0].additionalData.id,
|
||||
transformProjectToRequestBody(dialogRes)
|
||||
).pipe(
|
||||
untilDestroyed(this)
|
||||
).subscribe(
|
||||
{
|
||||
next: () => {
|
||||
next: (project: Project) => {
|
||||
this.notificationService.showPopup('project.popup.update.success', PopupType.SUCCESS);
|
||||
this.dialogRef.close();
|
||||
this.dialogRef.close(project);
|
||||
},
|
||||
error: err => {
|
||||
console.error(err);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ProjectDialogComponent} from '@shared/modules/project-dialog/project-dialog.component';
|
||||
import {NbButtonModule, NbCardModule, NbFormFieldModule, NbInputModule} from '@nebular/theme';
|
||||
import {NbButtonModule, NbCardModule, NbFormFieldModule, NbInputModule, NbSelectModule} from '@nebular/theme';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
|
@ -24,6 +24,7 @@ import {ReactiveFormsModule} from '@angular/forms';
|
|||
FontAwesomeModule,
|
||||
TranslateModule,
|
||||
ReactiveFormsModule,
|
||||
NbSelectModule,
|
||||
],
|
||||
providers: [
|
||||
ProjectDialogService,
|
||||
|
|
|
@ -6,6 +6,7 @@ import {Project} from '@shared/models/project.model';
|
|||
import {ProjectDialogComponent} from '@shared/modules/project-dialog/project-dialog.component';
|
||||
import {Validators} from '@angular/forms';
|
||||
import {GenericDialogData} from '@shared/models/generic-dialog-data';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectDialogService {
|
||||
|
@ -33,6 +34,11 @@ export class ProjectDialogService {
|
|||
config?: Partial<NbDialogConfig<Partial<any> | string>>): Observable<any> {
|
||||
let dialogOptions: Partial<NbDialogConfig<Partial<any> | string>>;
|
||||
let dialogData: GenericDialogData;
|
||||
let state;
|
||||
// transform severity of finding if existing
|
||||
if (project) {
|
||||
state = typeof project.state !== 'number' ? ReportState[project.state] : project.state;
|
||||
}
|
||||
// Setup ProjectDialogData
|
||||
dialogData = {
|
||||
form: {
|
||||
|
@ -87,7 +93,20 @@ export class ProjectDialogService {
|
|||
errors: [
|
||||
{errorCode: 'required', translationKey: 'project.validationMessage.summaryRequired'}
|
||||
]
|
||||
}
|
||||
},
|
||||
pentestState: {
|
||||
fieldName: 'pentestState',
|
||||
type: 'state-select',
|
||||
labelKey: 'project.state.label',
|
||||
placeholder: 'project.state',
|
||||
controlsConfig: [
|
||||
{value: project ? state : ReportState.NEW, disabled: false},
|
||||
[Validators.required]
|
||||
],
|
||||
errors: [
|
||||
{errorCode: 'required', translationKey: 'project.validationMessage.stateRequired'}
|
||||
]
|
||||
},
|
||||
},
|
||||
options: []
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@ import {KeycloakService} from 'keycloak-angular';
|
|||
import {Project, ProjectDialogBody} from '@shared/models/project.model';
|
||||
import {environment} from '../../../environments/environment';
|
||||
import {throwError} from 'rxjs';
|
||||
import {ReportState} from '@shared/models/state.enum';
|
||||
|
||||
describe('ProjectService', () => {
|
||||
let service: ProjectService;
|
||||
|
@ -42,6 +43,7 @@ describe('ProjectService', () => {
|
|||
createdAt: dummyDate,
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
};
|
||||
|
@ -83,6 +85,7 @@ describe('ProjectService', () => {
|
|||
client: 'E Corp',
|
||||
title: 'Some Mock API (v1.0) Scanning',
|
||||
tester: 'Novatester',
|
||||
state: ReportState.NEW,
|
||||
summary: ''
|
||||
};
|
||||
|
||||
|
@ -93,6 +96,7 @@ describe('ProjectService', () => {
|
|||
createdAt: dummyDate,
|
||||
tester: 'Novatester',
|
||||
summary: '',
|
||||
state: ReportState.NEW,
|
||||
testingProgress: 0,
|
||||
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||
};
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<ng-container [ngSwitch]="reportState">
|
||||
<nb-tag-list>
|
||||
<!--Success Tags-->
|
||||
<nb-tag *ngSwitchCase="state.RESOLVED" status="success" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<nb-tag *ngSwitchCase=" state.INFORMATIVE" status="success" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<!--Info Tags-->
|
||||
<nb-tag *ngSwitchCase="state.NEW" status="info" appearance="filled"
|
||||
text=" {{getTranslationKey() | translate}}"></nb-tag>
|
||||
<!--Warning Tags-->
|
||||
<nb-tag *ngSwitchCase="state.DUPLICATE" status="warning" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<nb-tag *ngSwitchCase="state.NOT_APPLICABLE" status="warning" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<nb-tag *ngSwitchCase="state.OUT_OF_SCOPE" status="warning" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<nb-tag *ngSwitchCase="state.ACCEPTED_RISK" status="warning" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<!--Danger Tags-->
|
||||
<nb-tag *ngSwitchCase="state.NEEDS_MORE_INFO" status="danger" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<nb-tag *ngSwitchCase="state.SPAM" status="danger" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
<!--Default Tags-->
|
||||
<nb-tag *ngSwitchDefault status="basic" appearance="filled"
|
||||
text="{{getTranslationKey() | translate}}"></nb-tag>
|
||||
</nb-tag-list>
|
||||
</ng-container>
|
|
@ -0,0 +1,45 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ReportStateTagComponent } from './report-state-tag.component';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {NbCardModule, NbTagModule} from '@nebular/theme';
|
||||
import {MockModule} from 'ng-mocks';
|
||||
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
|
||||
import {HttpLoaderFactory} from '../../../app/common-app.module';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('ReportStateTagComponent', () => {
|
||||
let component: ReportStateTagComponent;
|
||||
let fixture: ComponentFixture<ReportStateTagComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
ReportStateTagComponent
|
||||
],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
NbCardModule,
|
||||
MockModule(NbTagModule),
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: HttpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ReportStateTagComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import {ChangeDetectionStrategy, Component, Input, OnChanges, OnInit} from '@angular/core';
|
||||
import {ReportState, ReportStateText, reportStateTexts} from '@shared/models/state.enum';
|
||||
import {Severity} from '@shared/models/severity.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-report-state-tag',
|
||||
templateUrl: './report-state-tag.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ReportStateTagComponent implements OnChanges {
|
||||
|
||||
@Input() currentReportState: ReportState = ReportState.NEW;
|
||||
|
||||
// HTML only
|
||||
state = ReportState;
|
||||
reportState: any = 0;
|
||||
readonly reportStateTexts: Array<ReportStateText> = reportStateTexts;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.reportState = typeof this.currentReportState !== 'number' ? ReportState[this.currentReportState] : this.currentReportState;
|
||||
}
|
||||
|
||||
getTranslationKey(): string {
|
||||
const index = this.reportStateTexts.findIndex(statusText => statusText.value === this.reportState);
|
||||
return this.reportStateTexts[index].translationText;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {NbTagModule} from '@nebular/theme';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {ReportStateTagComponent} from '@shared/widgets/report-state-tag/report-state-tag.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ReportStateTagComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
NbTagModule,
|
||||
TranslateModule
|
||||
],
|
||||
exports: [
|
||||
ReportStateTagComponent
|
||||
]
|
||||
})
|
||||
export class ReportStateTagModule { }
|
|
@ -8,13 +8,13 @@ import {TranslateModule} from '@ngx-translate/core';
|
|||
declarations: [
|
||||
SeverityTagComponent
|
||||
],
|
||||
exports: [
|
||||
SeverityTagComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
NbTagModule,
|
||||
TranslateModule
|
||||
],
|
||||
exports: [
|
||||
SeverityTagComponent
|
||||
]
|
||||
})
|
||||
export class SeverityTagModule { }
|
||||
|
|
|
@ -21,6 +21,7 @@ data class Project(
|
|||
val createdAt: String = Instant.now().toString(),
|
||||
val tester: String,
|
||||
val summary: String? = null,
|
||||
val state: PentestState,
|
||||
var projectPentests: List<ProjectPentest> = emptyList(),
|
||||
val createdBy: String
|
||||
)
|
||||
|
@ -33,6 +34,7 @@ fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Projec
|
|||
createdAt = projectEntity.data.createdAt,
|
||||
tester = body.tester,
|
||||
summary = body.summary,
|
||||
state = body.state,
|
||||
projectPentests = projectEntity.data.projectPentests,
|
||||
createdBy = projectEntity.data.createdBy
|
||||
)
|
||||
|
@ -46,6 +48,7 @@ fun Project.toProjectResponseBody(): ResponseBody {
|
|||
"createdAt" to createdAt,
|
||||
"tester" to tester,
|
||||
"summary" to summary,
|
||||
"state" to state,
|
||||
/* ToDo: Calculate percentage in BE type: float */
|
||||
"testingProgress" to calculateProgress(),
|
||||
"createdBy" to createdBy
|
||||
|
@ -119,6 +122,7 @@ data class ProjectRequestBody(
|
|||
val client: String,
|
||||
val title: String,
|
||||
val tester: String,
|
||||
val state: PentestState,
|
||||
val summary: String?
|
||||
)
|
||||
|
||||
|
@ -132,6 +136,7 @@ fun ProjectRequestBody.isValid(): Boolean {
|
|||
this.client.isBlank() -> false
|
||||
this.title.isBlank() -> false
|
||||
this.tester.isBlank() -> false
|
||||
this.state.toString().isBlank() -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +149,7 @@ fun ProjectRequestBody.toProject(): Project {
|
|||
createdAt = Instant.now().toString(),
|
||||
tester = this.tester,
|
||||
summary = this.summary,
|
||||
state = this.state,
|
||||
// ToDo: Should be changed to SUB from Token after adding AUTH Header
|
||||
createdBy = UUID.randomUUID().toString()
|
||||
)
|
||||
|
|
|
@ -19,6 +19,7 @@ fun ProjectEntity.toProject() : Project {
|
|||
this.data.createdAt,
|
||||
this.data.tester,
|
||||
this.data.summary,
|
||||
this.data.state,
|
||||
this.data.projectPentests,
|
||||
this.data.createdBy
|
||||
)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.securityc4po.api.project
|
||||
|
||||
enum class PentestState {
|
||||
NEW,
|
||||
NEEDS_MORE_INFO,
|
||||
// Report states depending on customer feedback
|
||||
PRE_SUBMISSION,
|
||||
PENDING,
|
||||
TRIAGED,
|
||||
RETESTING,
|
||||
// Report states for closed submissions
|
||||
RESOLVED,
|
||||
INFORMATIVE,
|
||||
DUPLICATE,
|
||||
NOT_APPLICABLE,
|
||||
SPAM,
|
||||
OUT_OF_SCOPE,
|
||||
ACCEPTED_RISK
|
||||
}
|
|
@ -6,6 +6,7 @@ import com.securityc4po.api.BaseDocumentationIntTest
|
|||
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
||||
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
||||
import com.securityc4po.api.project.PentestState
|
||||
import com.securityc4po.api.project.Project
|
||||
import com.securityc4po.api.project.ProjectEntity
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
|
@ -256,6 +257,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
projectPentests = emptyList(),
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
// Pentests
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.securityc4po.api.BaseIntTest
|
|||
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
||||
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
||||
import com.securityc4po.api.project.PentestState
|
||||
import com.securityc4po.api.project.Project
|
||||
import com.securityc4po.api.project.ProjectEntity
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
|
@ -171,6 +172,7 @@ class PentestControllerIntTest : BaseIntTest() {
|
|||
title = "Some Mock API (v1.0) Scanning",
|
||||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
// pentests
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.securityc4po.api.pentest.Pentest
|
|||
import com.securityc4po.api.pentest.PentestCategory
|
||||
import com.securityc4po.api.pentest.PentestEntity
|
||||
import com.securityc4po.api.pentest.PentestStatus
|
||||
import com.securityc4po.api.project.PentestState
|
||||
import com.securityc4po.api.project.Project
|
||||
import com.securityc4po.api.project.ProjectEntity
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
|
@ -282,6 +283,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
projectPentests = emptyList(),
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
// Pentests
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.securityc4po.api.pentest.Pentest
|
|||
import com.securityc4po.api.pentest.PentestCategory
|
||||
import com.securityc4po.api.pentest.PentestEntity
|
||||
import com.securityc4po.api.pentest.PentestStatus
|
||||
import com.securityc4po.api.project.PentestState
|
||||
import com.securityc4po.api.project.Project
|
||||
import com.securityc4po.api.project.ProjectEntity
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
|
@ -179,6 +180,7 @@ class CommentControllerIntTest : BaseIntTest() {
|
|||
title = "Some Mock API (v1.0) Scanning",
|
||||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
// pentests
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.securityc4po.api.pentest.Pentest
|
|||
import com.securityc4po.api.pentest.PentestCategory
|
||||
import com.securityc4po.api.pentest.PentestEntity
|
||||
import com.securityc4po.api.pentest.PentestStatus
|
||||
import com.securityc4po.api.project.PentestState
|
||||
import com.securityc4po.api.project.Project
|
||||
import com.securityc4po.api.project.ProjectEntity
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
|
@ -340,6 +341,7 @@ class FindingControllerDocumentationTest: BaseDocumentationIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
projectPentests = emptyList(),
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
// Pentests
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.securityc4po.api.pentest.Pentest
|
|||
import com.securityc4po.api.pentest.PentestCategory
|
||||
import com.securityc4po.api.pentest.PentestEntity
|
||||
import com.securityc4po.api.pentest.PentestStatus
|
||||
import com.securityc4po.api.project.PentestState
|
||||
import com.securityc4po.api.project.Project
|
||||
import com.securityc4po.api.project.ProjectEntity
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
|
@ -207,6 +208,7 @@ class FindingControllerIntTest: BaseIntTest() {
|
|||
title = "Some Mock API (v1.0) Scanning",
|
||||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
// pentests
|
||||
|
|
|
@ -75,6 +75,8 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
.description("The user that is assigned as a tester in the project"),
|
||||
PayloadDocumentation.fieldWithPath("[].summary").type(JsonFieldType.STRING)
|
||||
.description("The summary of the requested project"),
|
||||
PayloadDocumentation.fieldWithPath("[].state").type(JsonFieldType.STRING)
|
||||
.description("The state of the requested project pentest"),
|
||||
PayloadDocumentation.fieldWithPath("[].createdBy").type(JsonFieldType.STRING)
|
||||
.description("The id of the user that created the project"),
|
||||
PayloadDocumentation.fieldWithPath("[].testingProgress").type(JsonFieldType.NUMBER)
|
||||
|
@ -92,6 +94,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
tester = "Novatester",
|
||||
summary = "Lorem Ipsum",
|
||||
projectPentests = emptyList<ProjectPentest>(),
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
val projectTwo = Project(
|
||||
|
@ -102,6 +105,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
tester = "Elliot",
|
||||
summary = "Lorem Ipsum",
|
||||
projectPentests = emptyList<ProjectPentest>(),
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
|
||||
|
@ -144,6 +148,8 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
.description("The date where the project was created at"),
|
||||
PayloadDocumentation.fieldWithPath("tester").type(JsonFieldType.STRING)
|
||||
.description("The user that is assigned as a tester in the project"),
|
||||
PayloadDocumentation.fieldWithPath("state").type(JsonFieldType.STRING)
|
||||
.description("The state of the requested project pentest"),
|
||||
PayloadDocumentation.fieldWithPath("createdBy").type(JsonFieldType.STRING)
|
||||
.description("The id of the user that created the project"),
|
||||
PayloadDocumentation.fieldWithPath("testingProgress").type(JsonFieldType.NUMBER)
|
||||
|
@ -157,6 +163,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
client = "Novatec",
|
||||
title = "log4j Pentest",
|
||||
tester = "Stipe",
|
||||
state = PentestState.NEW,
|
||||
summary = ""
|
||||
)
|
||||
}
|
||||
|
@ -230,6 +237,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
projectPentests = emptyList<ProjectPentest>(),
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
}
|
||||
|
@ -269,6 +277,8 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
.description("The updated user that is assigned as a tester in the project"),
|
||||
PayloadDocumentation.fieldWithPath("summary").type(JsonFieldType.STRING)
|
||||
.description("The summary of the requested project"),
|
||||
PayloadDocumentation.fieldWithPath("state").type(JsonFieldType.STRING)
|
||||
.description("The state of the requested project pentest"),
|
||||
PayloadDocumentation.fieldWithPath("createdBy").type(JsonFieldType.STRING)
|
||||
.description("The id of the user that created the project"),
|
||||
PayloadDocumentation.fieldWithPath("testingProgress").type(JsonFieldType.NUMBER)
|
||||
|
@ -282,6 +292,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
client = "Novatec_updated",
|
||||
title = "log4j Pentest_updated",
|
||||
tester = "Stipe_updated",
|
||||
state = PentestState.NEW,
|
||||
summary = ""
|
||||
)
|
||||
|
||||
|
@ -292,6 +303,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Stipe_updated",
|
||||
summary = "",
|
||||
state = PentestState.NEW,
|
||||
projectPentests = emptyList<ProjectPentest>(),
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
|
@ -306,6 +318,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
summary = "Lorem Ipsum",
|
||||
state = PentestState.NEW,
|
||||
projectPentests = emptyList<ProjectPentest>(),
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
|
@ -316,6 +329,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Elliot",
|
||||
summary = "Lorem Ipsum",
|
||||
state = PentestState.NEW,
|
||||
projectPentests = emptyList<ProjectPentest>(),
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
|
|
|
@ -72,6 +72,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
summary = "Lorem Ipsum",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
val projectTwo = Project(
|
||||
|
@ -81,6 +82,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Elliot",
|
||||
summary = "Lorem Ipsum",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
|
||||
|
@ -116,6 +118,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
createdAt = "2021-04-10T18:05:00Z",
|
||||
tester = "Stipe",
|
||||
summary = "",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "a8891ad2-5cf5-4519-a89e-9ef8eec9e10c"
|
||||
)
|
||||
}
|
||||
|
@ -149,6 +152,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
title = "CashMyData (iOS)",
|
||||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Elliot",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
}
|
||||
|
@ -175,6 +179,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
title = "log4j Pentest_updated",
|
||||
createdAt = "2021-04-10T18:05:00Z",
|
||||
tester = "Stipe_updated",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "a8891ad2-5cf5-4519-a89e-9ef8eec9e10c"
|
||||
)
|
||||
}
|
||||
|
@ -188,6 +193,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
summary = "Lorem Ipsum",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
val projectTwo = Project(
|
||||
|
@ -197,6 +203,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Elliot",
|
||||
summary = "Lorem Ipsum",
|
||||
state = PentestState.NEW,
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
// persist test data in database
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"$oid": "6405d84a13ae975803a098fa"
|
||||
},
|
||||
"lastModified": {
|
||||
"$date": "2023-03-24T12:18:06.619Z"
|
||||
"$date": "2023-04-04T13:39:00.146Z"
|
||||
},
|
||||
"data": {
|
||||
"_id": "575dd9d4-cb3c-4df3-981e-8a18bf8dc1d2",
|
||||
|
@ -12,6 +12,7 @@
|
|||
"createdAt": "2023-03-06T12:10:50.835664Z",
|
||||
"tester": "Jojo",
|
||||
"summary": "This report includes an Executeive Summary, the rules in regards to the scope of the pentest and the choosen approach of the pentester.\nDio Stonemask Inc. contracted Jojo to perform a Penetration Test to identify security weaknesses,\ndetermine the impact to Dio Stonemask Inc., document all findings in a clear and repeatable manner,\nand provide remediation recommendations",
|
||||
"state": "TRIAGED",
|
||||
"projectPentests": [
|
||||
{
|
||||
"pentestId": "54f3ce12-784a-4e44-b9b3-0a986119ec50",
|
||||
|
@ -250,7 +251,7 @@
|
|||
"$oid": "6405e92813ae975803a09905"
|
||||
},
|
||||
"lastModified": {
|
||||
"$date": "2023-03-06T13:22:48.564Z"
|
||||
"$date": "2023-03-29T19:04:32.771Z"
|
||||
},
|
||||
"data": {
|
||||
"_id": "d6e83738-4251-44ac-ad40-21b360780c98",
|
||||
|
@ -258,8 +259,14 @@
|
|||
"title": "CashMyData (iOS)",
|
||||
"createdAt": "2023-03-06T13:22:48.564351Z",
|
||||
"tester": "Elliot",
|
||||
"projectPentests": [],
|
||||
"createdBy": "5f104d76-bd8d-4258-852a-d000c7f0666d"
|
||||
"projectPentests": [
|
||||
{
|
||||
"pentestId": "a666322d-688c-45b2-bf34-dd7020ee71ac",
|
||||
"status": "COMPLETED"
|
||||
}
|
||||
],
|
||||
"createdBy": "5f104d76-bd8d-4258-852a-d000c7f0666d",
|
||||
"state": "NEW"
|
||||
},
|
||||
"_class": "com.securityc4po.api.project.ProjectEntity"
|
||||
}]
|
Loading…
Reference in New Issue