diff --git a/security-c4po-angular/src/app/header/header.component.html b/security-c4po-angular/src/app/header/header.component.html index 27306f8..c27a203 100644 --- a/security-c4po-angular/src/app/header/header.component.html +++ b/security-c4po-angular/src/app/header/header.component.html @@ -6,7 +6,7 @@
-

{{SECURITYC4PO_TITLE}}

+

{{SECURITYC4PO_TITLE}}

diff --git a/security-c4po-angular/src/app/header/header.component.scss b/security-c4po-angular/src/app/header/header.component.scss index 2a80347..37c4ceb 100644 --- a/security-c4po-angular/src/app/header/header.component.scss +++ b/security-c4po-angular/src/app/header/header.component.scss @@ -36,6 +36,9 @@ .logo-container { font-style: oblique; color: #e74c3c; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } nb-action { diff --git a/security-c4po-angular/src/app/header/header.component.ts b/security-c4po-angular/src/app/header/header.component.ts index c0e6694..598411b 100644 --- a/security-c4po-angular/src/app/header/header.component.ts +++ b/security-c4po-angular/src/app/header/header.component.ts @@ -34,7 +34,12 @@ export class HeaderComponent implements OnInit { // User Menu Properties userPictureOnly = false; user: BehaviorSubject = new BehaviorSubject(null); - userMenu: NbMenuItem[] = [{title: '', pathMatch: 'prefix'}]; + userMenu: NbMenuItem[] = [ + { + title: '', + pathMatch: 'prefix' + } + ]; readonly FALLBACK_IMG = 'assets/images/demo/anon-user-icon.png'; constructor( @@ -67,7 +72,7 @@ export class HeaderComponent implements OnInit { console.error(err); } }); - // Handle user profile manu selection + // Handle user profile menu selection this.menuService.onItemClick() .pipe( untilDestroyed(this) diff --git a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.scss b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.scss index 4635d8a..66248f6 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.scss +++ b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.scss @@ -1,5 +1,5 @@ @import '../../../assets/@theme/styles/themes'; .pentest-categories { - width: 22vw; + width: 20rem; } diff --git a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts index c873b90..696d82a 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts +++ b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts @@ -9,18 +9,18 @@ import {TranslateService} from '@ngx-translate/core'; import {ProjectState} from '@shared/stores/project-state/project-state'; import {catchError, switchMap, tap} from 'rxjs/operators'; import {Pentest, transformPentestsToObjectiveEntries} from '@shared/models/pentest.model'; +import {UntilDestroy} from '@ngneat/until-destroy'; @Component({ selector: 'app-objective-categories', templateUrl: './objective-categories.component.html', styleUrls: ['./objective-categories.component.scss'] }) -export class ObjectiveCategoriesComponent implements OnInit, OnDestroy { +@UntilDestroy() +export class ObjectiveCategoriesComponent implements OnInit { categories: NbMenuItem[] = []; selectedCategory: Category = 0; - private destroy$ = new Subject(); - constructor(private store: Store, private menuService: NbMenuService, private translateService: TranslateService) { @@ -49,13 +49,15 @@ export class ObjectiveCategoriesComponent implements OnInit, OnDestroy { untilDestroyed(this) ) .subscribe((menuBag) => { - this.selectedCategory = menuBag.item.data; - this.categories.forEach(category => { - category.selected = false; - }); - if (this.selectedCategory >= 0) { - menuBag.item.selected = true; - this.store.dispatch(new ChangeCategory(this.selectedCategory)); + if (menuBag.tag === 'menu') { + this.selectedCategory = menuBag.item.data; + this.categories.forEach(category => { + category.selected = false; + }); + if (this.selectedCategory >= 0) { + menuBag.item.selected = true; + this.store.dispatch(new ChangeCategory(this.selectedCategory)); + } } }); } @@ -86,9 +88,4 @@ export class ObjectiveCategoriesComponent implements OnInit, OnDestroy { } } } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } } diff --git a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.html b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.html index 3c8a36c..e0e692c 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.html +++ b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.html @@ -9,20 +9,23 @@
- -

{{selectedProject$.getValue().title}}

+
+ +

{{selectedProject$.getValue().title}}

+ +
- + + - @@ -36,5 +39,11 @@ + + + + + +
diff --git a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.scss b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.scss index ab15660..a39c1cc 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.scss +++ b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.scss @@ -1,16 +1,30 @@ +@import '../../../assets/@theme/styles/_text-overflow.scss'; + .pentest-header { - width: calc(100vw - 14%); + width: 100vw; .back-button-container { .back-element-icon { } } + .header-info { + position: absolute; + margin-left: 10rem; + margin-right: 10rem; + text-align: center; + + .state-tag { + } + + .project-title { + @include multiLineEllipsis($font-size: 1.5rem, $font-weight: bold, $line-height: 2rem, $lines-to-show: 1, $max-width: 36rem); + } + } + .button-container { - display: flex; - align-content: flex-end; - // ToDo: Fix so that longer / shorter name won't change needed margin - // margin-right: 2.25rem; + position: absolute; + right: 2rem; .element-icon { } diff --git a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.spec.ts b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.spec.ts index e3d01c1..428e25a 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.spec.ts +++ b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.spec.ts @@ -11,7 +11,7 @@ import {RouterTestingModule} from '@angular/router/testing'; import {NgxsModule, Store} from '@ngxs/store'; import {PROJECT_STATE_NAME, ProjectState, ProjectStateModel} from '@shared/stores/project-state/project-state'; import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; -import {NbActionsModule, NbIconModule} from '@nebular/theme'; +import {NbActionsModule, NbIconModule, NbMenuService} from '@nebular/theme'; import {ProjectService} from '@shared/services/api/project.service'; import {ProjectServiceMock} from '@shared/services/api/project.service.mock'; import {ProjectDialogService} from '@shared/modules/project-dialog/service/project-dialog.service'; @@ -27,6 +27,7 @@ import {ExportReportDialogServiceMock} from '@shared/modules/export-report-dialo import {ReportState} from '@shared/models/state.enum'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -35,6 +36,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }, @@ -80,6 +82,7 @@ describe('ObjectiveHeaderComponent', () => { NgxsModule.forRoot([ProjectState]) ], providers: [ + NbMenuService, {provide: ProjectService, useValue: new ProjectServiceMock()}, {provide: ProjectDialogService, useClass: ProjectDialogServiceMock}, {provide: ExportReportDialogService, useClass: ExportReportDialogServiceMock}, diff --git a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.ts b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.ts index 973acd9..46fb507 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.ts +++ b/security-c4po-angular/src/app/objective-overview/objective-header/objective-header.component.ts @@ -16,6 +16,9 @@ import {ProjectDialogService} from '@shared/modules/project-dialog/service/proje import {InitProjectState} from '@shared/stores/project-state/project-state.actions'; import {ExportReportDialogService} from '@shared/modules/export-report-dialog/service/export-report-dialog.service'; import {ExportReportDialogComponent} from '@shared/modules/export-report-dialog/export-report-dialog.component'; +import {NbMenuItem} from '@nebular/theme/components/menu/menu.service'; +import {NbMenuService} from '@nebular/theme'; +import {TranslateService} from '@ngx-translate/core'; @UntilDestroy() @Component({ @@ -27,6 +30,23 @@ export class ObjectiveHeaderComponent implements OnInit { readonly fa = FA; selectedProject$: BehaviorSubject = new BehaviorSubject(null); + // Mobile menu properties + objectiveActionItems: NbMenuItem[] = [ + { + title: 'global.action.edit', + badge: { + status: 'warning' + } + }, + { + title: 'global.action.report', + badge: { + status: 'info' + } + }, + ]; + readonly BARS_IMG = 'assets/images/icons/bars.svg'; + readonly ELLIPSIS_IMG = 'assets/images/icons/ellipsis.svg'; constructor(private store: Store, private readonly notificationService: NotificationService, @@ -34,7 +54,10 @@ export class ObjectiveHeaderComponent implements OnInit { private projectDialogService: ProjectDialogService, private projectService: ProjectService, private exportReportDialogService: ExportReportDialogService, - private readonly router: Router) { + private readonly router: Router, + private translateService: TranslateService, + private menuService: NbMenuService + ) { } ngOnInit(): void { @@ -52,6 +75,33 @@ export class ObjectiveHeaderComponent implements OnInit { console.error(err); } }); + + // Handle user profile menu action selection + this.menuService.onItemClick() + .pipe( + untilDestroyed(this) + ) + .subscribe((menuBag) => { + if (menuBag.item.badge && menuBag.item.badge.status === 'warning') { + this.onClickEditPentestProject(); + } else if (menuBag.item.badge && menuBag.item.badge.status === 'info') { + this.onClickGeneratePentestReport(); + } + }); + // Setup stream to translate menu action item + this.translateService.stream('global.action.edit') + .pipe( + untilDestroyed(this) + ).subscribe((text: string) => { + this.objectiveActionItems[0].title = text; + }); + // Setup stream to translate menu action item + this.translateService.stream('global.action.report') + .pipe( + untilDestroyed(this) + ).subscribe((text: string) => { + this.objectiveActionItems[1].title = text; + }); } onClickRouteBack(): void { @@ -78,13 +128,15 @@ export class ObjectiveHeaderComponent implements OnInit { untilDestroyed(this) ).subscribe({ next: (project) => { - this.store.dispatch(new InitProjectState( - project, - [], - [] - )).pipe( - untilDestroyed(this) - ).subscribe(); + if (project) { + this.store.dispatch(new InitProjectState( + project, + [], + [] + )).pipe( + untilDestroyed(this) + ).subscribe(); + } } }); } diff --git a/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts b/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts index 817fc97..b8b1f0d 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts +++ b/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts @@ -11,7 +11,7 @@ import { NbListModule, NbButtonModule, NbTooltipModule, - NbActionsModule + NbActionsModule, NbUserModule, NbContextMenuModule } from '@nebular/theme'; import {TranslateModule} from '@ngx-translate/core'; import {StatusTagModule} from '@shared/widgets/status-tag/status-tag.module'; @@ -26,6 +26,7 @@ import {ExportReportDialogModule} from '@shared/modules/export-report-dialog/exp 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'; +import {VersionTagModule} from '@shared/widgets/version-tag/version-tag.module'; @NgModule({ declarations: [ @@ -33,33 +34,36 @@ import {ReportStateTagModule} from '@shared/widgets/report-state-tag/report-stat ObjectiveCategoriesComponent, ObjectiveTableComponent, ], - imports: [ - CommonModule, - CommonAppModule, - NbLayoutModule, - NbCardModule, - NbButtonModule, - // nbTooltip crashes app right now if used in component, - // workaround: use title in html for now - NbTooltipModule, - NbTreeGridModule, - TranslateModule, - StatusTagModule, - RouterModule, - FormsModule, - NbListModule, - FontAwesomeModule, - FlexLayoutModule, - NbActionsModule, - ExportReportDialogModule, - ProjectDialogModule, - ObjectiveOverviewRoutingModule, - // Table Widgets - FindigWidgetModule, - CommentWidgetModule, - NbMenuModule, - ReportStateTagModule - ], + imports: [ + CommonModule, + CommonAppModule, + NbLayoutModule, + NbCardModule, + NbButtonModule, + // nbTooltip crashes app right now if used in component, + // workaround: use title in html for now + NbTooltipModule, + NbTreeGridModule, + TranslateModule, + StatusTagModule, + RouterModule, + FormsModule, + NbListModule, + FontAwesomeModule, + FlexLayoutModule, + NbActionsModule, + ExportReportDialogModule, + ProjectDialogModule, + ObjectiveOverviewRoutingModule, + // Table Widgets + FindigWidgetModule, + CommentWidgetModule, + NbMenuModule, + ReportStateTagModule, + VersionTagModule, + NbUserModule, + NbContextMenuModule + ], exports: [ ObjectiveHeaderComponent, ObjectiveCategoriesComponent, diff --git a/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.html b/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.html index d4fab07..148af87 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.html +++ b/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.html @@ -1,4 +1,4 @@ - +
- +
diff --git a/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.scss b/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.scss index 124f0c9..dad7048 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.scss +++ b/security-c4po-angular/src/app/objective-overview/objective-table/objective-table.component.scss @@ -1,7 +1,11 @@ @import '../../../assets/@theme/styles/themes'; .pentest-table { - width: calc(78vw - 18%); + // width: calc(78vw - 18%); + // width: 100%; + // width: calc(100% - 20rem); + margin-right: 2rem; + padding-right: 2rem; .pentest-cell { // Add style here diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-comments/pentest-comments.component.spec.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-comments/pentest-comments.component.spec.ts index 93011d3..86becfa 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-comments/pentest-comments.component.spec.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-comments/pentest-comments.component.spec.ts @@ -25,6 +25,7 @@ import {CommentDialogServiceMock} from '@shared/modules/comment-dialog/service/c import {ReportState} from '@shared/models/state.enum'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -33,6 +34,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }, diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.spec.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.spec.ts index 1529afe..a38bb50 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.spec.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.spec.ts @@ -16,6 +16,7 @@ import {NotificationServiceMock} from '@shared/services/toaster-service/notifica import {ReportState} from '@shared/models/state.enum'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -24,6 +25,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }, diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts index c5613b4..aacacab 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts @@ -25,6 +25,7 @@ import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service. import {ReportState} from '@shared/models/state.enum'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -33,6 +34,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }, 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 0e8ccc0..6908617 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 @@ -16,6 +16,7 @@ import {PentestStatus} from '@shared/models/pentest-status.model'; import {ReportState} from '@shared/models/state.enum'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -24,6 +25,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }, 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 fa5dd94..521fe6f 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 @@ -1,4 +1,4 @@ -
+
-

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

+
+ {{selectedProjectTitle$.getValue()}} + {{" / " + pentest$.getValue().refNumber}} +
+
+ {{pentest$.getValue().refNumber}} +
@@ -21,7 +27,7 @@
+ -
-
- - -
-

{{project?.title}}

- -
-
- -

- {{'project.client' | translate}}: -

- - {{project?.client}} - - -

- {{'project.tester' | translate}}: -

- - {{project?.tester}} - - -

- {{'project.createdAt' | translate}}: -

- - {{project?.createdAt | dateTimeFormat}} - -
- -
-
- - - - {{'popup.info' | translate}} {{'global.no.progress' | translate}} - -
- - - -
-
-
+
+
+
@@ -125,7 +63,6 @@
-
diff --git a/security-c4po-angular/src/app/project-overview/project-overview.component.scss b/security-c4po-angular/src/app/project-overview/project-overview.component.scss index 7fa0d50..bc79217 100644 --- a/security-c4po-angular/src/app/project-overview/project-overview.component.scss +++ b/security-c4po-angular/src/app/project-overview/project-overview.component.scss @@ -1,22 +1,29 @@ @import '../../assets/@theme/styles/themes'; @import '../../assets/@theme/styles/variables'; +@import '../../assets/@theme/styles/_text-overflow.scss'; .pentest-overview { width: 100vw; - height: 80vh; - // ToDo: Disable and fix scrolling - overflow: hidden; + height: 85vh; + overflow: hidden !important; .pentest-overview-header { width: 100vw; + height: 5rem; .header-filer { - .project-filter-input { width: 24rem; + border-color: nb-theme(color-control-default); - .search-prefix-icon { - color:nb-theme(color-info-default); + + .search-field:active { + color: nb-theme(color-info-default); + // opacity: initial; + } + + .search-prefix-icon:active { + color: nb-theme(color-info-default); } } @@ -40,7 +47,7 @@ position: fixed; right: 1.5rem; - .add-project-button { + .add-project-button { // align-content: flex-end; margin: 6rem 2rem 6rem 0; @@ -53,63 +60,26 @@ .pentest-overview-column { width: 100vw; + // ToDo: Adjust this property when adding footer + height: calc(100% - 15rem) !important; + max-height: 100vh !important; + margin-top: 1.25rem; + // Scrollbar + overflow-y: scroll !important; + overflow-x: hidden; + scroll-behavior: smooth; - .project-card { - max-width: 22rem; - width: 22rem; - min-width: 20rem; - max-height: 100%; - height: 100%; - min-height: 100%; + .project-grid { + display: grid; + /* define the number of grid columns */ + grid-template-columns: repeat( auto-fill, minmax(24rem, 1fr) ); - .project-header { - max-height: 8rem; - height: 8rem; - min-height: 6rem; - - .header-title { - width: 12.5rem; - } - - .state-tag { - width: 1rem; + .project { + padding-bottom: 1.25rem; + height: max-content; } } - .project-subheader { - font-size: 1.25rem; - font-weight: bold; - } - - .project-paragraph { - font-size: 1.15rem; - font-style: italic; - } - - .project-progress { - max-width: 65%; - width: 65%; - min-width: 65%; - } - - .project-button { - height: 1.425rem; - } - } - - .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 - /* - margin-top: +0.625rem; - transform: scale(1.025); - */ - } - - .project-link:hover { - cursor: pointer !important; - } .error-text { font-size: 1.25rem; font-weight: bold; diff --git a/security-c4po-angular/src/app/project-overview/project-overview.component.spec.ts b/security-c4po-angular/src/app/project-overview/project-overview.component.spec.ts index 64771fc..78c6daf 100644 --- a/security-c4po-angular/src/app/project-overview/project-overview.component.spec.ts +++ b/security-c4po-angular/src/app/project-overview/project-overview.component.spec.ts @@ -26,7 +26,7 @@ import {DialogService} from '@shared/services/dialog-service/dialog.service'; import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock'; import {ProjectDialogService} from '@shared/modules/project-dialog/service/project-dialog.service'; import {ProjectDialogServiceMock} from '@shared/modules/project-dialog/service/project-dialog.service.mock'; -import {MockComponent, MockPipe} from 'ng-mocks'; +import {MockComponent} from 'ng-mocks'; describe('ProjectOverviewComponent', () => { let component: ProjectOverviewComponent; @@ -36,19 +36,17 @@ describe('ProjectOverviewComponent', () => { await TestBed.configureTestingModule({ declarations: [ ProjectOverviewComponent, - MockComponent(LoadingSpinnerComponent), - MockPipe(DateTimeFormatPipe) + MockComponent(LoadingSpinnerComponent) ], imports: [ CommonModule, - ProjectOverviewRoutingModule, NbCardModule, NbButtonModule, FlexLayoutModule, BrowserAnimationsModule, FontAwesomeModule, TranslateModule, - NbProgressBarModule, + ProjectOverviewRoutingModule, NbSpinnerModule, HttpClientTestingModule, ThemeModule.forRoot(), diff --git a/security-c4po-angular/src/app/project-overview/project-overview.component.ts b/security-c4po-angular/src/app/project-overview/project-overview.component.ts index 44c1834..d93ff10 100644 --- a/security-c4po-angular/src/app/project-overview/project-overview.component.ts +++ b/security-c4po-angular/src/app/project-overview/project-overview.component.ts @@ -5,16 +5,17 @@ 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, startWith, tap} from 'rxjs/operators'; +import {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'; 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'; +import {ProjectState} from '@shared/stores/project-state/project-state'; +import {Pentest} from '@shared/models/pentest.model'; +import {Route} from '@shared/models/route.enum'; +import {SetAvailableProjects} from '@shared/stores/project-state/project-state.actions'; @UntilDestroy() @Component({ @@ -33,6 +34,7 @@ export class ProjectOverviewComponent implements OnInit { // Search projectSearch: FormControl; protected filter$: Observable; + allProjectsCount$: BehaviorSubject = new BehaviorSubject({allProjectsCount: 0}); constructor( private readonly notificationService: NotificationService, @@ -44,9 +46,24 @@ export class ProjectOverviewComponent implements OnInit { } ngOnInit(): void { + // Load all available projects this.loadProjects(); + // Subscribe to project store + this.store.select(ProjectState.allProjects).pipe( + untilDestroyed(this) + ).subscribe({ + next: (projects: Project[]) => { + if (projects.length === 0) { + this.loadProjects(); + } else { + } + }, + error: err => { + console.error(err); + } + }); // Setup Search - this.projectSearch = new FormControl({value: '', disabled: !this.allProjects$.getValue()}); + this.projectSearch = new FormControl({value: '', disabled: this.allProjects$.getValue() === []}); this.setFilterObserverForProjects(); } @@ -58,8 +75,16 @@ export class ProjectOverviewComponent implements OnInit { ) .subscribe({ next: (projects: Project[]) => { - this.projects$.next(projects); - this.allProjects$.next(projects); + if (projects) { + this.projects$.next(projects); + this.allProjects$.next(projects); + this.allProjectsCount$.next({allProjectsCount: projects.length}); + this.store.dispatch(new SetAvailableProjects(projects)); + } else { + this.projects$.next([]); + this.allProjects$.next([]); + this.allProjectsCount$.next({allProjectsCount: 0}); + } this.loading$.next(false); }, error: err => { @@ -89,122 +114,11 @@ export class ProjectOverviewComponent implements OnInit { }); } - onClickEditProject(project: Project): void { - this.projectDialogService.openProjectDialog( - ProjectDialogComponent, - project, - { - closeOnEsc: false, - hasScroll: false, - autoFocus: true, - closeOnBackdropClick: false - } - ).pipe( - untilDestroyed(this) - ).subscribe({ - next: () => { - this.loadProjects(); - } - }); - } - - onClickDeleteProject(project: Project): void { - // Set dialog message - const message = { - title: 'project.delete.title', - key: 'project.delete.key', - data: {name: project.title}, - } as any; - // Check if project is empty - if (project.testingProgress === 0) { - this.dialogService.openConfirmDialog( - message - ).onClose.pipe( - filter((confirm) => !!confirm), - untilDestroyed(this) - ).subscribe({ - next: () => { - this.deleteProject(project); - } - }); - } else { - const secMessage = { - title: 'project.delete.title', - key: 'project.delete.sec.key', - confirmString: project.title.toString(), - inputPlaceholderKey: 'project.delete.confirmStringPlaceholder', - data: {name: project.title, confirmString: project.title.toString()}, - } as any; - // Set confirm string - // message.data.confirmString = project.title; - this.dialogService.openSecurityConfirmDialog( - secMessage - ).onClose.pipe( - filter((confirm) => !!confirm), - untilDestroyed(this) - ).subscribe({ - next: () => { - this.deleteProject(project); - } - }); - } - } - - onClickRouteToProject(project): void { - this.router.navigate([Route.OBJECTIVE_OVERVIEW]).then(() => { - this.store.dispatch(new InitProjectState( - project, - [], - [] - )).pipe(untilDestroyed(this)).subscribe(); - }, err => { - console.error(err); - }); - } - // HTML only isLoading(): Observable { 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()); @@ -234,31 +148,4 @@ export class ProjectOverviewComponent implements OnInit { } ); } - - private deleteProject(project: Project): void { - this.projectService.deleteProjectById(project.id).pipe( - untilDestroyed(this) - ).subscribe({ - next: () => { - this.loadProjects(); - this.notificationService.showPopup('project.popup.delete.success', PopupType.SUCCESS); - }, error: error => { - this.notificationService.showPopup('project.popup.delete.failed', PopupType.FAILURE); - this.onRequestFailed(project); - console.error(error); - } - }); - } - - private onRequestFailed(retryParameter: any): void { - this.dialogService.openRetryDialog({key: 'global.retry.dialog', data: null}).onClose - .pipe( - untilDestroyed(this) - ) - .subscribe((ref) => { - if (ref.retry) { - this.deleteProject(retryParameter); - } - }); - } } diff --git a/security-c4po-angular/src/app/project-overview/project-overview.module.ts b/security-c4po-angular/src/app/project-overview/project-overview.module.ts index 5b0106d..df56a9f 100644 --- a/security-c4po-angular/src/app/project-overview/project-overview.module.ts +++ b/security-c4po-angular/src/app/project-overview/project-overview.module.ts @@ -14,7 +14,6 @@ import { import {FlexLayoutModule} from '@angular/flex-layout'; import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; import {TranslateModule} from '@ngx-translate/core'; -import {DateTimeFormatPipe} from '@shared/pipes/date-time-format.pipe'; import {ProjectDialogModule} from '@shared/modules/project-dialog/project-dialog.module'; import {CommonAppModule} from '../common-app.module'; import {ConfirmDialogModule} from '@shared/modules/confirm-dialog/confirm-dialog.module'; @@ -22,11 +21,11 @@ import {SecurityConfirmDialogModule} from '@shared/modules/security-confirm-dial import {RouterModule} from '@angular/router'; import {ReportStateTagModule} from '@shared/widgets/report-state-tag/report-state-tag.module'; import {ReactiveFormsModule} from '@angular/forms'; +import {ProjectWidgetModule} from '@shared/widgets/project-widget/project-widget.module'; @NgModule({ declarations: [ - ProjectOverviewComponent, - DateTimeFormatPipe + ProjectOverviewComponent ], imports: [ CommonModule, @@ -50,7 +49,8 @@ import {ReactiveFormsModule} from '@angular/forms'; NbInputModule, NbFormFieldModule, ReactiveFormsModule, - NbSelectModule + NbSelectModule, + ProjectWidgetModule ] }) export class ProjectOverviewModule { diff --git a/security-c4po-angular/src/app/project-overview/project/project.component.html b/security-c4po-angular/src/app/project-overview/project/project.component.html index c36a079..f183b4b 100644 --- a/security-c4po-angular/src/app/project-overview/project/project.component.html +++ b/security-c4po-angular/src/app/project-overview/project/project.component.html @@ -11,7 +11,7 @@
- + diff --git a/security-c4po-angular/src/app/project-overview/project/project.component.scss b/security-c4po-angular/src/app/project-overview/project/project.component.scss index b709f62..dd921db 100644 --- a/security-c4po-angular/src/app/project-overview/project/project.component.scss +++ b/security-c4po-angular/src/app/project-overview/project/project.component.scss @@ -21,6 +21,7 @@ .table-wrapper{ padding-right: 0 !important; + border-style: none; .table-column { overflow: auto !important; diff --git a/security-c4po-angular/src/app/project-overview/project/project.component.spec.ts b/security-c4po-angular/src/app/project-overview/project/project.component.spec.ts index 6f237b5..9de276a 100644 --- a/security-c4po-angular/src/app/project-overview/project/project.component.spec.ts +++ b/security-c4po-angular/src/app/project-overview/project/project.component.spec.ts @@ -29,6 +29,7 @@ import {ExportReportDialogServiceMock} from '@shared/modules/export-report-dialo import {ReportState} from '@shared/models/state.enum'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -37,6 +38,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }, diff --git a/security-c4po-angular/src/assets/@theme/styles/_text-overflow.scss b/security-c4po-angular/src/assets/@theme/styles/_text-overflow.scss new file mode 100644 index 0000000..f9563fc --- /dev/null +++ b/security-c4po-angular/src/assets/@theme/styles/_text-overflow.scss @@ -0,0 +1,16 @@ +/* mixin for multiline */ +@mixin multiLineEllipsis($font-size, $font-weight, $line-height, $lines-to-show, $max-width) { + // Fallback for non-webkit + display: block; + display: -webkit-box; + max-width: $max-width; + // Fallback for non-webkit + height: calc(#{$font-size} * #{$line-height} * #{$lines-to-show}); + font-size: $font-size; + font-weight: $font-weight; + line-height: $line-height; + -webkit-line-clamp: $lines-to-show; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/security-c4po-angular/src/assets/i18n/de-DE.json b/security-c4po-angular/src/assets/i18n/de-DE.json index e887b71..badab48 100644 --- a/security-c4po-angular/src/assets/i18n/de-DE.json +++ b/security-c4po-angular/src/assets/i18n/de-DE.json @@ -22,6 +22,7 @@ "password": "Passwort", "no.progress": "Kein Fortschritt", "project": "Projekt", + "version": "Version", "validationMessage": { "inputNotMatching": "Eingabe stimmt nicht überein!" }, @@ -98,7 +99,7 @@ "no.projects": "Keine Projekte verfügbar" }, "filter": { - "placeholder": "Projekt suchen" + "placeholder": "Alle {{allProjectsCount}} Projekt(e) durchsuchen.." }, "create": { "header": "Neues Projekt erstellen" diff --git a/security-c4po-angular/src/assets/i18n/en-US.json b/security-c4po-angular/src/assets/i18n/en-US.json index c588327..37ecbd1 100644 --- a/security-c4po-angular/src/assets/i18n/en-US.json +++ b/security-c4po-angular/src/assets/i18n/en-US.json @@ -22,6 +22,7 @@ "password": "Password", "no.progress": "No progress", "project": "Project", + "version": "Version", "validationMessage": { "inputNotMatching": "Input does not match!" }, @@ -98,7 +99,7 @@ "no.projects": "No projects available" }, "filter": { - "placeholder": "Search for project" + "placeholder": "Search through all {{allProjectsCount}} project(s).." }, "create": { "header": "Create New Project" diff --git a/security-c4po-angular/src/assets/images/icons/bars.svg b/security-c4po-angular/src/assets/images/icons/bars.svg new file mode 100644 index 0000000..6e7823e --- /dev/null +++ b/security-c4po-angular/src/assets/images/icons/bars.svg @@ -0,0 +1,64 @@ + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/security-c4po-angular/src/assets/images/icons/ellipsis.svg b/security-c4po-angular/src/assets/images/icons/ellipsis.svg new file mode 100644 index 0000000..21e12ed --- /dev/null +++ b/security-c4po-angular/src/assets/images/icons/ellipsis.svg @@ -0,0 +1,26 @@ + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/security-c4po-angular/src/shared/models/project.model.ts b/security-c4po-angular/src/shared/models/project.model.ts index a05fec9..48c0460 100644 --- a/security-c4po-angular/src/shared/models/project.model.ts +++ b/security-c4po-angular/src/shared/models/project.model.ts @@ -9,6 +9,7 @@ export class Project { tester: string; summary: string; state: ReportState; + version: string; projectPentests?: Array; testingProgress?: number; createdBy: string; @@ -19,6 +20,7 @@ export class Project { createdAt: Date, tester: string, state: ReportState, + version: string, projectPentests?: Array, testingProgress?: number, summary?: string, @@ -32,6 +34,7 @@ export class Project { this.testingProgress = testingProgress; this.summary = summary; this.state = state; + this.version = version; this.createdBy = createdBy; } } diff --git a/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts b/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts index 7bdbd98..f996b20 100644 --- a/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts +++ b/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts @@ -35,6 +35,7 @@ import {Comment} from '@shared/models/comment.model'; import {ReportState} from '@shared/models/state.enum'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: { id: '56c47c56-3bcd-45f1-a05b-c197dbd33111', client: 'E Corp', @@ -43,6 +44,7 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }, diff --git a/security-c4po-angular/src/shared/modules/export-report-dialog/export-report-dialog.component.html b/security-c4po-angular/src/shared/modules/export-report-dialog/export-report-dialog.component.html index 9debb2f..79b2bb7 100644 --- a/security-c4po-angular/src/shared/modules/export-report-dialog/export-report-dialog.component.html +++ b/security-c4po-angular/src/shared/modules/export-report-dialog/export-report-dialog.component.html @@ -1,15 +1,17 @@ - - {{ dialogData?.options[0].headerLabelKey | translate }} + + {{ dialogData?.options[0].headerLabelKey | translate }} - + +
- - {{dialogData.options[0].additionalData.title}} - +
+ {{dialogData.options[0].additionalData.title}} + +
{ tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }; @@ -97,6 +98,7 @@ describe('ProjectService', () => { tester: 'Novatester', summary: '', state: ReportState.NEW, + version: '1.0', testingProgress: 0, createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110' }; 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 2da4083..9491430 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 @@ -14,6 +14,13 @@ export class InitProjectState { } } +export class SetAvailableProjects { + static readonly type = '[ProjectState] SetAvailableProjects'; + + constructor(public projects: Project[]) { + } +} + export class ChangeProject { static readonly type = '[ProjectState] ChangeProject'; diff --git a/security-c4po-angular/src/shared/stores/project-state/project-state.spec.ts b/security-c4po-angular/src/shared/stores/project-state/project-state.spec.ts index 6a2df91..1fe04d3 100644 --- a/security-c4po-angular/src/shared/stores/project-state/project-state.spec.ts +++ b/security-c4po-angular/src/shared/stores/project-state/project-state.spec.ts @@ -8,6 +8,7 @@ import {PROJECT_STATE_NAME, ProjectState, ProjectStateModel} from '@shared/store import {Category} from '@shared/models/category.model'; const INITIAL_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: null, disabledCategories: [], selectedCategory: Category.INFORMATION_GATHERING, @@ -16,6 +17,7 @@ const INITIAL_PROJECT_STATE_SESSION: ProjectStateModel = { }; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { + allProjects: [], selectedProject: null, disabledCategories: [], selectedCategory: Category.INFORMATION_GATHERING, 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 86ccde2..9d4c583 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 @@ -5,7 +5,7 @@ import { ChangeCategory, ChangePentest, ChangeProject, - InitProjectState, + InitProjectState, SetAvailableProjects, UpdatePentestComments, UpdatePentestFindings, UpdatePentestStatus, UpdatePentestTime @@ -17,6 +17,7 @@ import {PentestStatus} from '@shared/models/pentest-status.model'; export const PROJECT_STATE_NAME = 'project'; export interface ProjectStateModel { + allProjects: Project[]; selectedProject: Project; // Manages Categories disabledCategories: Array; @@ -33,6 +34,7 @@ export interface FindingMap { @State({ name: PROJECT_STATE_NAME, defaults: { + allProjects: [], selectedProject: null, disabledCategories: [], selectedCategory: Category.INFORMATION_GATHERING, @@ -42,6 +44,11 @@ export interface FindingMap { }) @Injectable() export class ProjectState { + @Selector() + static allProjects(state: ProjectStateModel): Project[] { + return state.allProjects; + } + @Selector() static project(state: ProjectStateModel): Project { return state.selectedProject; @@ -60,6 +67,7 @@ export class ProjectState { @Action(InitProjectState) initProjectState(ctx: StateContext, action: InitProjectState): void { ctx.setState({ + allProjects: ctx.getState().allProjects, selectedProject: action.project, disabledCategories: action.disabledCategories, selectedCategory: Category.INFORMATION_GATHERING, @@ -68,6 +76,15 @@ export class ProjectState { }); } + @Action(SetAvailableProjects) + setAllAvailableProjects(ctx: StateContext, {projects}: SetAvailableProjects): void { + const state = ctx.getState(); + // ToDo: Add logic to change selectedCategory if disabled + ctx.patchState({ + allProjects: projects + }); + } + @Action(ChangeProject) changeProject(ctx: StateContext, {project}: ChangeProject): void { const state = ctx.getState(); diff --git a/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.html b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.html new file mode 100644 index 0000000..982a1d4 --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.html @@ -0,0 +1,64 @@ + + +
+

{{project?.title}}

+ + + +
+
+ +

+ {{'project.client' | translate}}: +

+ + {{project?.client}} + + +

+ {{'project.tester' | translate}}: +

+ + {{project?.tester}} + + +

+ {{'project.createdAt' | translate}}: +

+ + {{project?.createdAt | dateTimeFormat}} + +
+ +
+
+ + + + {{'popup.info' | translate}} {{'global.no.progress' | translate}} + +
+ + + +
+
+
diff --git a/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.scss b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.scss new file mode 100644 index 0000000..9ced337 --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.scss @@ -0,0 +1,68 @@ +@import '../../../assets/@theme/styles/themes'; +@import '../../../assets/@theme/styles/variables'; +@import '../../../assets/@theme/styles/_text-overflow.scss'; + +.project-card { + max-width: 24rem; + width: 24rem; + min-width: 24rem; + max-height: 100%; + height: 100%; + min-height: 100%; + + .project-header { + max-height: 8rem; + height: 8rem; + min-height: 6rem; + + .header-title { + @include multiLineEllipsis($font-size: 1.5rem, $font-weight: bold, $line-height: 1.5rem, $lines-to-show: 2, $max-width: 14rem); + } + + .state-tag { + width: 1rem; + // Align status + margin-left: 1rem; + margin-right: 1rem; + } + } + + .project-subheader { + font-size: 1.25rem; + font-weight: bold; + } + + .project-paragraph { + font-size: 1.15rem; + font-style: italic; + } + + .project-progress { + max-width: 65%; + width: 65%; + min-width: 65%; + } + + .project-button { + height: 1.425rem; + } + + &.accent { + // Changes accent width only + // border-width: 0.5rem 0 0 0rem; + border-width: 0.5rem 0 0 0.25rem; + } +} + +.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 + margin-top: +0.625rem; + transform: scale(1.025); + border-width: 0.5rem 0 0 0.45rem; +} + +.project-link:hover { + cursor: pointer !important; +} diff --git a/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.spec.ts b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.spec.ts new file mode 100644 index 0000000..221479f --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.spec.ts @@ -0,0 +1,78 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProjectWidgetComponent } from './project-widget.component'; +import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; +import {HttpLoaderFactory} from '../../../app/common-app.module'; +import {HttpClient} from '@angular/common/http'; +import {RouterTestingModule} from '@angular/router/testing'; +import {NbButtonModule, NbCardModule, NbProgressBarModule} from '@nebular/theme'; +import {MockPipe} from 'ng-mocks'; +import {DateTimeFormatPipe} from '@shared/pipes/date-time-format.pipe'; +import {ProjectService} from '@shared/services/api/project.service'; +import {ProjectServiceMock} from '@shared/services/api/project.service.mock'; +import {ProjectDialogService} from '@shared/modules/project-dialog/service/project-dialog.service'; +import {ProjectDialogServiceMock} from '@shared/modules/project-dialog/service/project-dialog.service.mock'; +import {DialogService} from '@shared/services/dialog-service/dialog.service'; +import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock'; +import {NotificationService} from '@shared/services/toaster-service/notification.service'; +import {NotificationServiceMock} from '@shared/services/toaster-service/notification.service.mock'; +import {FlexLayoutModule} from '@angular/flex-layout'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {NgxsModule} from '@ngxs/store'; +import {SessionState} from '@shared/stores/session-state/session-state'; +import {KeycloakService} from 'keycloak-angular'; +import {ThemeModule} from '@assets/@theme/theme.module'; + +describe('ProjectWidgetComponent', () => { + let component: ProjectWidgetComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + ProjectWidgetComponent, + MockPipe(DateTimeFormatPipe) + ], + imports: [ + ThemeModule.forRoot(), + NbProgressBarModule, + NbCardModule, + NbButtonModule, + FlexLayoutModule, + BrowserAnimationsModule, + FontAwesomeModule, + HttpClientTestingModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }), + RouterTestingModule.withRoutes([]), + NgxsModule.forRoot([SessionState]) + ], + providers: [ + KeycloakService, + {provide: ProjectService, useValue: new ProjectServiceMock()}, + {provide: ProjectDialogService, useClass: ProjectDialogServiceMock}, + {provide: DialogService, useClass: DialogServiceMock}, + {provide: NotificationService, useClass: NotificationServiceMock} + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ProjectWidgetComponent); + component = fixture.componentInstance; + // ToDo: fix detectChanges() when project input is defined + // fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.ts b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.ts new file mode 100644 index 0000000..af35f63 --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.component.ts @@ -0,0 +1,182 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {Project} from '@shared/models/project.model'; +import * as FA from '@fortawesome/free-solid-svg-icons'; +import {ReportState} from '@shared/models/state.enum'; +import {Route} from '@shared/models/route.enum'; +import {ChangePentest, InitProjectState, SetAvailableProjects} from '@shared/stores/project-state/project-state.actions'; +import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy'; +import {ProjectDialogComponent} from '@shared/modules/project-dialog/project-dialog.component'; +import {filter, tap} from 'rxjs/operators'; +import {NotificationService, PopupType} from '@shared/services/toaster-service/notification.service'; +import {Store} from '@ngxs/store'; +import {Router} from '@angular/router'; +import {ProjectService} from '@shared/services/api/project.service'; +import {DialogService} from '@shared/services/dialog-service/dialog.service'; +import {ProjectDialogService} from '@shared/modules/project-dialog/service/project-dialog.service'; + +@Component({ + selector: 'app-project-widget', + templateUrl: './project-widget.component.html', + styleUrls: ['./project-widget.component.scss'] +}) +@UntilDestroy() +export class ProjectWidgetComponent implements OnInit { + + @Input() project: Project; + + // HTML only + readonly fa = FA; + + constructor( + private readonly notificationService: NotificationService, + private store: Store, + private router: Router, + private projectService: ProjectService, + private dialogService: DialogService, + private projectDialogService: ProjectDialogService + ) { + } + + ngOnInit(): void { + } + + onClickRouteToProject(project): void { + this.router.navigate([Route.OBJECTIVE_OVERVIEW]).then(() => { + this.store.dispatch(new InitProjectState( + project, + [], + [] + )).pipe(untilDestroyed(this)).subscribe(); + }, err => { + console.error(err); + }); + } + + /** + * 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; + } + + onClickEditProject(project: Project): void { + this.projectDialogService.openProjectDialog( + ProjectDialogComponent, + project, + { + closeOnEsc: false, + hasScroll: false, + autoFocus: true, + closeOnBackdropClick: false + } + ).pipe( + untilDestroyed(this) + ).subscribe({ + next: () => { + // ToDo: Find way to edit / delete the single project from project store instead + this.store.dispatch(new SetAvailableProjects([])); + } + }); + } + + onClickDeleteProject(project: Project): void { + // Set dialog message + const message = { + title: 'project.delete.title', + key: 'project.delete.key', + data: {name: project.title}, + } as any; + // Check if project is empty + if (project.testingProgress === 0) { + this.dialogService.openConfirmDialog( + message + ).onClose.pipe( + filter((confirm) => !!confirm), + untilDestroyed(this) + ).subscribe({ + next: () => { + this.deleteProject(project); + } + }); + } else { + const secMessage = { + title: 'project.delete.title', + key: 'project.delete.sec.key', + confirmString: project.title.toString(), + inputPlaceholderKey: 'project.delete.confirmStringPlaceholder', + data: {name: project.title, confirmString: project.title.toString()}, + } as any; + // Set confirm string + // message.data.confirmString = project.title; + this.dialogService.openSecurityConfirmDialog( + secMessage + ).onClose.pipe( + filter((confirm) => !!confirm), + untilDestroyed(this) + ).subscribe({ + next: () => { + this.deleteProject(project); + } + }); + } + } + + private deleteProject(project: Project): void { + this.projectService.deleteProjectById(project.id).pipe( + untilDestroyed(this) + ).subscribe({ + next: () => { + // ToDo: Find way to edit / delete the single project from project store instead + this.store.dispatch(new SetAvailableProjects([])); + this.notificationService.showPopup('project.popup.delete.success', PopupType.SUCCESS); + }, error: error => { + this.notificationService.showPopup('project.popup.delete.failed', PopupType.FAILURE); + this.onRequestFailed(project); + console.error(error); + } + }); + } + + private onRequestFailed(retryParameter: any): void { + this.dialogService.openRetryDialog({key: 'global.retry.dialog', data: null}).onClose + .pipe( + untilDestroyed(this) + ) + .subscribe((ref) => { + if (ref.retry) { + this.deleteProject(retryParameter); + } + }); + } +} diff --git a/security-c4po-angular/src/shared/widgets/project-widget/project-widget.module.ts b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.module.ts new file mode 100644 index 0000000..57da05a --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/project-widget/project-widget.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import {ProjectWidgetComponent} from '@shared/widgets/project-widget/project-widget.component'; +import {ReportStateTagModule} from '@shared/widgets/report-state-tag/report-state-tag.module'; +import {NbButtonModule, NbCardModule, NbProgressBarModule} from '@nebular/theme'; +import {TranslateModule} from '@ngx-translate/core'; +import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; +import {DateTimeFormatPipe} from '@shared/pipes/date-time-format.pipe'; +import {FlexLayoutModule} from '@angular/flex-layout'; + +@NgModule({ + declarations: [ + ProjectWidgetComponent, + DateTimeFormatPipe + ], + imports: [ + CommonModule, + ReportStateTagModule, + NbCardModule, + NbButtonModule, + NbProgressBarModule, + TranslateModule, + FlexLayoutModule, + FontAwesomeModule + ], + exports: [ + DateTimeFormatPipe, + ProjectWidgetComponent + ] +}) +export class ProjectWidgetModule { } diff --git a/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.html b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.html new file mode 100644 index 0000000..8f7841d --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.html @@ -0,0 +1,3 @@ + + + diff --git a/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.scss b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.scss new file mode 100644 index 0000000..7e3b4a5 --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.scss @@ -0,0 +1,3 @@ +.version-tag { + font-family: Courier, serif !important; +} diff --git a/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.spec.ts b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.spec.ts new file mode 100644 index 0000000..6fe56e3 --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.spec.ts @@ -0,0 +1,44 @@ +import {ComponentFixture, TestBed} from '@angular/core/testing'; + +import {VersionTagComponent} from './version-tag.component'; +import {MockModule} from 'ng-mocks'; +import {NbTagModule} from '@nebular/theme'; +import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; +import {HttpLoaderFactory} from '../../../app/common-app.module'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; + +describe('VersionTagComponent', () => { + let component: VersionTagComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + VersionTagComponent + ], + imports: [ + HttpClientTestingModule, + MockModule(NbTagModule), + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }) + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(VersionTagComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.ts b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.ts new file mode 100644 index 0000000..b401cf7 --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.component.ts @@ -0,0 +1,17 @@ +import {Component, Input, OnInit} from '@angular/core'; + +@Component({ + selector: 'app-version-tag', + templateUrl: './version-tag.component.html', + styleUrls: ['./version-tag.component.scss'] +}) +export class VersionTagComponent implements OnInit { + + @Input() version = ''; + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/security-c4po-angular/src/shared/widgets/version-tag/version-tag.module.ts b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.module.ts new file mode 100644 index 0000000..35f033d --- /dev/null +++ b/security-c4po-angular/src/shared/widgets/version-tag/version-tag.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import {VersionTagComponent} from '@shared/widgets/version-tag/version-tag.component'; +import {NbTagModule} from '@nebular/theme'; +import {TranslateModule} from '@ngx-translate/core'; +import {FlexLayoutModule} from '@angular/flex-layout'; + +@NgModule({ + declarations: [ + VersionTagComponent + ], + imports: [ + CommonModule, + NbTagModule, + TranslateModule, + FlexLayoutModule + ], + exports: [ + VersionTagComponent + ] +}) +export class VersionTagModule { } diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/Project.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/Project.kt index 94ec5f1..5675365 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/Project.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/Project.kt @@ -22,6 +22,7 @@ data class Project( val tester: String, val summary: String? = null, val state: PentestState, + val version: String, var projectPentests: List = emptyList(), val createdBy: String ) @@ -35,6 +36,7 @@ fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Projec tester = body.tester, summary = body.summary, state = body.state, + version = projectEntity.data.version, projectPentests = projectEntity.data.projectPentests, createdBy = projectEntity.data.createdBy ) @@ -49,6 +51,7 @@ fun Project.toProjectResponseBody(): ResponseBody { "tester" to tester, "summary" to summary, "state" to state, + "version" to version, /* ToDo: Calculate percentage in BE type: float */ "testingProgress" to calculateProgress(), "createdBy" to createdBy @@ -64,6 +67,7 @@ fun Project.toProjectCompletedPentestResponseBody(): ResponseBody { "createdAt" to createdAt, "tester" to tester, "summary" to summary, + "version" to version, "projectPentests" to projectPentests.filter { pentest -> pentest.status == PentestStatus.COMPLETED }, "createdBy" to createdBy ) @@ -78,6 +82,7 @@ fun Project.toProjectEvaluatedPentestResponseBody(): ResponseBody { "createdAt" to createdAt, "tester" to tester, "summary" to summary, + "version" to version, "projectPentests" to projectPentests, "createdBy" to createdBy ) @@ -150,6 +155,8 @@ fun ProjectRequestBody.toProject(): Project { tester = this.tester, summary = this.summary, state = this.state, + // ToDo: Update version in backend automatically + version = "1.0", // ToDo: Should be changed to SUB from Token after adding AUTH Header createdBy = UUID.randomUUID().toString() ) diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectEntity.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectEntity.kt index 371ef4b..166ab05 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectEntity.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectEntity.kt @@ -20,6 +20,7 @@ fun ProjectEntity.toProject() : Project { this.data.tester, this.data.summary, this.data.state, + this.data.version, this.data.projectPentests, this.data.createdBy ) diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt index 2354e85..7df58f6 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt @@ -258,6 +258,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() { tester = "Novatester", projectPentests = emptyList(), state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) // Pentests diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt index f460976..e9bd7ec 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt @@ -173,6 +173,7 @@ class PentestControllerIntTest : BaseIntTest() { createdAt = "2021-01-10T18:05:00Z", tester = "Novatester", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) // pentests diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt index 6362791..1bb1176 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt @@ -284,6 +284,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { tester = "Novatester", projectPentests = emptyList(), state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) // Pentests diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt index 1d37b99..c492d63 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt @@ -181,6 +181,7 @@ class CommentControllerIntTest : BaseIntTest() { createdAt = "2021-01-10T18:05:00Z", tester = "Novatester", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) // pentests diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerDocumentationTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerDocumentationTest.kt index 493c9c8..9691a1a 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerDocumentationTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerDocumentationTest.kt @@ -342,6 +342,7 @@ class FindingControllerDocumentationTest: BaseDocumentationIntTest() { tester = "Novatester", projectPentests = emptyList(), state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) // Pentests diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerIntTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerIntTest.kt index 10e8dd2..02a86ee 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerIntTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/finding/FindingControllerIntTest.kt @@ -209,6 +209,7 @@ class FindingControllerIntTest: BaseIntTest() { createdAt = "2021-01-10T18:05:00Z", tester = "Novatester", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) // pentests diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerDocumentationTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerDocumentationTest.kt index 844a6a5..aab37a1 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerDocumentationTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerDocumentationTest.kt @@ -77,6 +77,8 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { .description("The summary of the requested project"), PayloadDocumentation.fieldWithPath("[].state").type(JsonFieldType.STRING) .description("The state of the requested project pentest"), + PayloadDocumentation.fieldWithPath("[].version").type(JsonFieldType.STRING) + .description("The version of the requested project"), PayloadDocumentation.fieldWithPath("[].createdBy").type(JsonFieldType.STRING) .description("The id of the user that created the project"), PayloadDocumentation.fieldWithPath("[].testingProgress").type(JsonFieldType.NUMBER) @@ -95,6 +97,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { summary = "Lorem Ipsum", projectPentests = emptyList(), state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) val projectTwo = Project( @@ -106,6 +109,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { summary = "Lorem Ipsum", projectPentests = emptyList(), state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) @@ -150,6 +154,8 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { .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("version").type(JsonFieldType.STRING) + .description("The version of the requested project"), PayloadDocumentation.fieldWithPath("createdBy").type(JsonFieldType.STRING) .description("The id of the user that created the project"), PayloadDocumentation.fieldWithPath("testingProgress").type(JsonFieldType.NUMBER) @@ -238,6 +244,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { tester = "Novatester", projectPentests = emptyList(), state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) } @@ -279,6 +286,8 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { .description("The summary of the requested project"), PayloadDocumentation.fieldWithPath("state").type(JsonFieldType.STRING) .description("The state of the requested project pentest"), + PayloadDocumentation.fieldWithPath("version").type(JsonFieldType.STRING) + .description("The version of the requested project"), PayloadDocumentation.fieldWithPath("createdBy").type(JsonFieldType.STRING) .description("The id of the user that created the project"), PayloadDocumentation.fieldWithPath("testingProgress").type(JsonFieldType.NUMBER) @@ -304,6 +313,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { tester = "Stipe_updated", summary = "", state = PentestState.NEW, + version = "1.0", projectPentests = emptyList(), createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) @@ -319,6 +329,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { tester = "Novatester", summary = "Lorem Ipsum", state = PentestState.NEW, + version = "1.0", projectPentests = emptyList(), createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) @@ -330,6 +341,7 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { tester = "Elliot", summary = "Lorem Ipsum", state = PentestState.NEW, + version = "1.0", projectPentests = emptyList(), createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerIntTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerIntTest.kt index 7eebf2c..d963430 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerIntTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/project/ProjectControllerIntTest.kt @@ -73,6 +73,7 @@ class ProjectControllerIntTest : BaseIntTest() { tester = "Novatester", summary = "Lorem Ipsum", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) val projectTwo = Project( @@ -83,6 +84,7 @@ class ProjectControllerIntTest : BaseIntTest() { tester = "Elliot", summary = "Lorem Ipsum", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) @@ -119,6 +121,7 @@ class ProjectControllerIntTest : BaseIntTest() { tester = "Stipe", summary = "", state = PentestState.NEW, + version = "1.0", createdBy = "a8891ad2-5cf5-4519-a89e-9ef8eec9e10c" ) } @@ -153,6 +156,7 @@ class ProjectControllerIntTest : BaseIntTest() { createdAt = "2021-01-10T18:05:00Z", tester = "Elliot", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) } @@ -180,6 +184,7 @@ class ProjectControllerIntTest : BaseIntTest() { createdAt = "2021-04-10T18:05:00Z", tester = "Stipe_updated", state = PentestState.NEW, + version = "1.0", createdBy = "a8891ad2-5cf5-4519-a89e-9ef8eec9e10c" ) } @@ -194,6 +199,7 @@ class ProjectControllerIntTest : BaseIntTest() { tester = "Novatester", summary = "Lorem Ipsum", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) val projectTwo = Project( @@ -204,6 +210,7 @@ class ProjectControllerIntTest : BaseIntTest() { tester = "Elliot", summary = "Lorem Ipsum", state = PentestState.NEW, + version = "1.0", createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032" ) // persist test data in database diff --git a/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/ProjectReport.kt b/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/ProjectReport.kt index bfd8e1d..d86a20a 100644 --- a/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/ProjectReport.kt +++ b/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/ProjectReport.kt @@ -9,6 +9,7 @@ data class ProjectReport( val createdAt: Date, val tester: String, val summary: String? = null, + val version: String, var projectPentestReport: MutableList = mutableListOf(), val createdBy: String ) diff --git a/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/api/Project.kt b/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/api/Project.kt index 1bd0810..0630e56 100644 --- a/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/api/Project.kt +++ b/security-c4po-reporting/src/main/kotlin/com/securityc4po/reporting/remote/model/api/Project.kt @@ -5,7 +5,6 @@ import com.securityc4po.reporting.remote.model.ProjectReport import java.time.Instant import java.util.Date - data class Project( val id: String, val client: String, @@ -13,6 +12,7 @@ data class Project( val createdAt: String, val tester: String, val summary: String? = "", + val version: String, var projectPentests: List? = emptyList(), val createdBy: String ) @@ -26,6 +26,7 @@ fun Project.toProjectReport(): ProjectReport { createdAt = Date.from(Instant.now()), tester = this.tester, summary = this.summary, + version = this.version, projectPentestReport = mutableListOf(), createdBy = this.createdBy ) diff --git a/security-c4po-reporting/src/main/resources/jasper/reports/c4po_cover.jrxml b/security-c4po-reporting/src/main/resources/jasper/reports/c4po_cover.jrxml index e5467eb..8194e3f 100644 --- a/security-c4po-reporting/src/main/resources/jasper/reports/c4po_cover.jrxml +++ b/security-c4po-reporting/src/main/resources/jasper/reports/c4po_cover.jrxml @@ -39,6 +39,7 @@ + @@ -149,23 +150,23 @@ - + - - - - - - - + + + + + + + diff --git a/security-c4po-reporting/src/main/resources/jasper/reports/c4po_executive_summary.jrxml b/security-c4po-reporting/src/main/resources/jasper/reports/c4po_executive_summary.jrxml index 19c4a4c..6218f71 100644 --- a/security-c4po-reporting/src/main/resources/jasper/reports/c4po_executive_summary.jrxml +++ b/security-c4po-reporting/src/main/resources/jasper/reports/c4po_executive_summary.jrxml @@ -105,6 +105,7 @@ + diff --git a/security-c4po-reporting/src/main/resources/jasper/reports/c4po_state_of_confidentiality.jrxml b/security-c4po-reporting/src/main/resources/jasper/reports/c4po_state_of_confidentiality.jrxml index cd51756..c715812 100644 --- a/security-c4po-reporting/src/main/resources/jasper/reports/c4po_state_of_confidentiality.jrxml +++ b/security-c4po-reporting/src/main/resources/jasper/reports/c4po_state_of_confidentiality.jrxml @@ -33,6 +33,7 @@ + @@ -70,7 +71,7 @@ - +