fix: error messages for user profile dialog

This commit is contained in:
Marcel Haag 2023-04-21 19:40:15 +02:00 committed by Cel
parent ed7c70c62d
commit 65985e5ef3
20 changed files with 187 additions and 124 deletions

View File

@ -1,5 +1,5 @@
<div class="pentest-table"> <div class="pentest-table">
<table [nbTreeGrid]="dataSource" nbsort> <table [nbTreeGrid]="dataSource">
<!--ToDo: Add the click event to every td manually except the actions column actions--> <!--ToDo: Add the click event to every td manually except the actions column actions-->
<tr nbTreeGridHeaderRow *nbTreeGridHeaderRowDef="columns"></tr> <tr nbTreeGridHeaderRow *nbTreeGridHeaderRowDef="columns"></tr>
<tr nbTreeGridRow *nbTreeGridRowDef="let pentest; columns: columns" <tr nbTreeGridRow *nbTreeGridRowDef="let pentest; columns: columns"
@ -45,7 +45,7 @@
{{ 'pentest.findings&comments' | translate }} {{ 'pentest.findings&comments' | translate }}
</th> </th>
<td nbTreeGridCell *nbTreeGridCellDef="let pentest" (click)="onClickRouteToObjectivePentest(pentest.data)"> <td nbTreeGridCell *nbTreeGridCellDef="let pentest" (click)="onClickRouteToObjectivePentest(pentest.data)">
<div fxLayout="row" fxLayoutGap="0.5rem" fxLayoutAlign="start start"> <div fxLayout="row" fxLayoutGap="0.5rem" fxLayoutAlign="center center">
<app-findig-widget [numberOfFindings]="pentest.data['findingIds'] ? pentest.data['findingIds'].length : 0"></app-findig-widget> <app-findig-widget [numberOfFindings]="pentest.data['findingIds'] ? pentest.data['findingIds'].length : 0"></app-findig-widget>
<span> / </span> <span> / </span>
<app-comment-widget [numberOfComments]="pentest.data['commentIds'] ? pentest.data['commentIds'].length : 0"></app-comment-widget> <app-comment-widget [numberOfComments]="pentest.data['commentIds'] ? pentest.data['commentIds'].length : 0"></app-comment-widget>

View File

@ -1,5 +1,5 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {NbGetters, NbSortDirection, NbSortRequest, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@nebular/theme'; import {NbGetters, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@nebular/theme';
import {ObjectiveEntry, Pentest, transformPentestsToObjectiveEntries} from '@shared/models/pentest.model'; import {ObjectiveEntry, Pentest, transformPentestsToObjectiveEntries} from '@shared/models/pentest.model';
import {PentestService} from '@shared/services/api/pentest.service'; import {PentestService} from '@shared/services/api/pentest.service';
import {Store} from '@ngxs/store'; import {Store} from '@ngxs/store';
@ -15,6 +15,7 @@ import * as FA from '@fortawesome/free-solid-svg-icons';
import {DialogService} from '@shared/services/dialog-service/dialog.service'; import {DialogService} from '@shared/services/dialog-service/dialog.service';
import {NotificationService, PopupType} from '@shared/services/toaster-service/notification.service'; import {NotificationService, PopupType} from '@shared/services/toaster-service/notification.service';
import {Project} from '@shared/models/project.model'; import {Project} from '@shared/models/project.model';
import {sortDescending} from '@shared/functions/sort-names.function';
@UntilDestroy() @UntilDestroy()
@Component({ @Component({
@ -25,7 +26,6 @@ import {Project} from '@shared/models/project.model';
export class ObjectiveTableComponent implements OnInit { export class ObjectiveTableComponent implements OnInit {
// HTML only // HTML only
readonly fa = FA; readonly fa = FA;
// use ban and check
loading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true); loading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
columns: Array<ObjectiveColumns> = [ columns: Array<ObjectiveColumns> = [
@ -71,12 +71,6 @@ export class ObjectiveTableComponent implements OnInit {
} }
}); });
this.loadPentestData(); this.loadPentestData();
this.changeSortTable()
this.dataSource.sort({column: ObjectiveColumns.TEST_ID, direction: NbSortDirection.DESCENDING});
}
private changeSortTable(): void {
this.dataSource.sort({column: ObjectiveColumns.TEST_ID, direction: NbSortDirection.DESCENDING});
} }
loadPentestData(): void { loadPentestData(): void {
@ -87,8 +81,12 @@ export class ObjectiveTableComponent implements OnInit {
untilDestroyed(this) untilDestroyed(this)
).subscribe({ ).subscribe({
next: (pentests: Pentest[]) => { next: (pentests: Pentest[]) => {
this.pentests$.next(pentests); // Sort data without before adding as table data source
this.data = transformPentestsToObjectiveEntries(pentests); const sortedPentests = pentests.sort((a: Pentest, b: Pentest) =>
sortDescending(a.refNumber.toLowerCase(), b.refNumber.toLowerCase())
);
this.pentests$.next(sortedPentests);
this.data = transformPentestsToObjectiveEntries(sortedPentests);
this.dataSource.setData(this.data, this.getters); this.dataSource.setData(this.data, this.getters);
this.loading$.next(false); this.loading$.next(false);
}, },

View File

@ -11,7 +11,12 @@
{{ 'comment.title' | translate }} {{ 'comment.title' | translate }}
</th> </th>
<td nbTreeGridCell *nbTreeGridCellDef="let comment"> <td nbTreeGridCell *nbTreeGridCellDef="let comment">
{{ comment.data['title'] }} <span *ngIf=" comment.data['title'].length < 200; else cutTitle">
{{ comment.data['title'] }}
</span>
<ng-template #cutTitle>
{{ comment.data['title'].slice(0, 200) + '...' }}
</ng-template>
</td> </td>
</ng-container> </ng-container>
<!-- Description --> <!-- Description -->
@ -20,7 +25,12 @@
{{ 'comment.description' | translate }} {{ 'comment.description' | translate }}
</th> </th>
<td nbTreeGridCell *nbTreeGridCellDef="let comment"> <td nbTreeGridCell *nbTreeGridCellDef="let comment">
{{ comment.data['description'] }} <span *ngIf=" comment.data['description'].length < 200; else cutDescription">
{{ comment.data['description'] }}
</span>
<ng-template #cutDescription>
{{ comment.data['description'].slice(0, 200) + '...' }}
</ng-template>
</td> </td>
</ng-container> </ng-container>
<!-- Actions --> <!-- Actions -->

View File

@ -1,4 +1,5 @@
@import '../../../../assets/@theme/styles/themes'; @import '../../../../assets/@theme/styles/themes';
@import '../../../../assets/@theme/styles/_text-overflow.scss';
.comment-table { .comment-table {
margin-right: 2rem; margin-right: 2rem;

View File

@ -11,7 +11,12 @@
{{ 'finding.title' | translate }} {{ 'finding.title' | translate }}
</th> </th>
<td nbTreeGridCell *nbTreeGridCellDef="let finding"> <td nbTreeGridCell *nbTreeGridCellDef="let finding">
{{ finding.data['title'] }} <span *ngIf=" finding.data['title'].length < 200; else cutTitle">
{{ finding.data['title'] }}
</span>
<ng-template #cutTitle>
{{ finding.data['title'].slice(0, 200) + '...' }}
</ng-template>
</td> </td>
</ng-container> </ng-container>
<!-- Severity --> <!-- Severity -->
@ -31,7 +36,12 @@
{{ 'finding.description' | translate }} {{ 'finding.description' | translate }}
</th> </th>
<td nbTreeGridCell *nbTreeGridCellDef="let finding"> <td nbTreeGridCell *nbTreeGridCellDef="let finding">
{{ finding.data['description'] }} <span *ngIf=" finding.data['description'].length < 200; else cutDescription">
{{ finding.data['description'] }}
</span>
<ng-template #cutDescription>
{{ finding.data['description'].slice(0, 200) + '...' }}
</ng-template>
</td> </td>
</ng-container> </ng-container>
<!-- Impact --> <!-- Impact -->
@ -40,7 +50,12 @@
{{ 'finding.impact' | translate }} {{ 'finding.impact' | translate }}
</th> </th>
<td nbTreeGridCell *nbTreeGridCellDef="let finding"> <td nbTreeGridCell *nbTreeGridCellDef="let finding">
{{ finding.data['impact'] }} <span *ngIf=" finding.data['impact'].length < 200; else cutImpact">
{{ finding.data['impact'] }}
</span>
<ng-template #cutImpact>
{{ finding.data['impact'].slice(0, 200) + '...' }}
</ng-template>
</td> </td>
</ng-container> </ng-container>
<!-- Actions --> <!-- Actions -->

View File

@ -1,4 +1,5 @@
@import '../../../../assets/@theme/styles/themes'; @import '../../../../assets/@theme/styles/themes';
@import '../../../../assets/@theme/styles/_text-overflow.scss';
.finding-table { .finding-table {
margin-right: 2rem; margin-right: 2rem;

View File

@ -78,6 +78,7 @@
"languageLabel": "Sprache ändern:", "languageLabel": "Sprache ändern:",
"password": { "password": {
"title": "Passwort ändern:", "title": "Passwort ändern:",
"button" : "Passwort ändern",
"old": "Altes Passwort", "old": "Altes Passwort",
"new": "Neues Passwort", "new": "Neues Passwort",
"confirmNew": "Neues Passwort bestätigen", "confirmNew": "Neues Passwort bestätigen",

View File

@ -78,6 +78,7 @@
"languageLabel": "Change language:", "languageLabel": "Change language:",
"password": { "password": {
"title": "Change password:", "title": "Change password:",
"button" : "Change password",
"old": "Old password", "old": "Old password",
"new": "New password", "new": "New password",
"confirmNew": "Confirm new password", "confirmNew": "Confirm new password",

View File

@ -0,0 +1,3 @@
export function sortDescending(nameOne: string, nameTwo: string): number {
return nameOne === nameTwo ? 0 : (nameOne > nameTwo ? 1 : -1);
}

View File

@ -11,16 +11,15 @@
class="form-field" class="form-field"
[status]="passwordFormGroup.get('oldPassword').dirty ? (passwordFormGroup.get('oldPassword').invalid ? 'danger' : 'basic') : 'basic'" [status]="passwordFormGroup.get('oldPassword').dirty ? (passwordFormGroup.get('oldPassword').invalid ? 'danger' : 'basic') : 'basic'"
placeholder="{{'******'}}"> placeholder="{{'******'}}">
<button nbSuffix nbButton ghost class="form-field-button" (click)="toggleShowOldPassword()"> <button nbSuffix nbButton ghost class="form-field-button" (click)="toggleShowOldPassword()" [disabled]="passwordFormGroup.get('oldPassword').disabled">
<fa-icon [icon]="showOldPassword ? fa.faEye : fa.faEyeSlash"></fa-icon> <fa-icon [icon]="showOldPassword ? fa.faEye : fa.faEyeSlash"></fa-icon>
</button> </button>
<!-- FIXME: when the bug (https://github.com/angular/components/issues/7739) is fixed --> <ng-container *ngIf="oldPasswordCtrl.dirty">
<ng-template *ngIf="passwordFormGroup.get('oldPassword').dirty">
<span class="error-text" <span class="error-text"
*ngIf="passwordFormGroup.get('oldPassword')?.hasError('required')"> *ngIf="oldPasswordCtrl.hasError('required')">
{{'profile.password.validationMessage.passwordRequired' | translate}} {{'profile.password.validationMessage.passwordRequired' | translate}}
</span> </span>
</ng-template> </ng-container>
</nb-form-field> </nb-form-field>
<div fxLayout="column" fxLayoutGap="2rem"> <div fxLayout="column" fxLayoutGap="2rem">
<!--New password--> <!--New password-->
@ -34,18 +33,17 @@
class="form-field" class="form-field"
[status]="passwordFormGroup.get('newPassword').dirty ? (passwordFormGroup.get('newPassword').invalid ? 'danger' : 'basic') : 'basic'" [status]="passwordFormGroup.get('newPassword').dirty ? (passwordFormGroup.get('newPassword').invalid ? 'danger' : 'basic') : 'basic'"
placeholder="{{'******'}}"> placeholder="{{'******'}}">
<button nbSuffix nbButton ghost class="form-field-button" (click)="toggleShowNewPasswords()"> <button nbSuffix nbButton ghost class="form-field-button" (click)="toggleShowNewPasswords()" [disabled]="passwordFormGroup.get('newPassword').disabled">
<fa-icon [icon]="showNewPasswords ? fa.faEye : fa.faEyeSlash"></fa-icon> <fa-icon [icon]="showNewPasswords ? fa.faEye : fa.faEyeSlash"></fa-icon>
</button> </button>
<!-- FIXME: when the bug (https://github.com/angular/components/issues/7739) is fixed --> <ng-container *ngIf="newPasswordCtrl.dirty">
<ng-template *ngIf="passwordFormGroup.get('newPassword').dirty">
<span class="error-text" <span class="error-text"
*ngIf="passwordFormGroup.get('newPassword')?.hasError('required')"> *ngIf="newPasswordCtrl.hasError('required')">
{{'profile.password.validationMessage.passwordRequired' | translate}} {{'profile.password.validationMessage.passwordRequired' | translate}}
</span> </span>
</ng-template> </ng-container>
</nb-form-field> </nb-form-field>
<!--New password--> <!--Confirm new password-->
<nb-form-field class="password-form-field"> <nb-form-field class="password-form-field">
<label for="confirmNewPassword" class="label"> <label for="confirmNewPassword" class="label">
{{'profile.password.confirmNew' | translate}} {{'profile.password.confirmNew' | translate}}
@ -56,13 +54,12 @@
class="form-field" class="form-field"
[status]="passwordFormGroup.get('confirmNewPassword').dirty ? (passwordFormGroup.get('confirmNewPassword').invalid ? 'danger' : 'basic') : 'basic'" [status]="passwordFormGroup.get('confirmNewPassword').dirty ? (passwordFormGroup.get('confirmNewPassword').invalid ? 'danger' : 'basic') : 'basic'"
placeholder="{{'******'}}"> placeholder="{{'******'}}">
<!-- FIXME: when the bug (https://github.com/angular/components/issues/7739) is fixed --> <ng-container *ngIf="confirmNewPasswordCtrl.dirty">
<ng-template *ngIf="passwordFormGroup.get('confirmNewPassword').dirty">
<span class="error-text" <span class="error-text"
*ngIf="passwordFormGroup.get('confirmNewPassword')?.hasError('required')"> *ngIf="confirmNewPasswordCtrl.hasError('required')">
{{'profile.password.validationMessage.passwordRequired' | translate}} {{'profile.password.validationMessage.passwordRequired' | translate}}
</span> </span>
</ng-template> </ng-container>
</nb-form-field> </nb-form-field>
</div> </div>
</div> </div>

View File

@ -16,6 +16,10 @@
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.form-field:disabled {
background-color: nb-theme(color-basic-transparent-focus);
}
.form-field-button { .form-field-button {
margin-top: 1rem; margin-top: 1rem;
margin-right: 1rem; margin-right: 1rem;

View File

@ -1,5 +1,14 @@
import {Component, EventEmitter, forwardRef, Input, OnInit, Output} from '@angular/core'; import {Component, EventEmitter, forwardRef, Input, OnInit, Output} from '@angular/core';
import {AbstractControl, FormBuilder, FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors} from '@angular/forms'; import {
AbstractControl,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validators
} from '@angular/forms';
import { import {
OldPasswordEmptyError, OldPasswordEmptyError,
PasswordErrorStateMatcher PasswordErrorStateMatcher
@ -61,9 +70,9 @@ export class PasswordInputFromComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.passwordFormGroup = this.fb.group( this.passwordFormGroup = this.fb.group(
{ {
oldPassword: ['', [PasswordInputFromComponent.containsBlankSpaceValidator]], oldPassword: [{value: '', disabled: true}, [Validators.required, PasswordInputFromComponent.containsBlankSpaceValidator]],
newPassword: ['', [PasswordInputFromComponent.containsBlankSpaceValidator]], newPassword: [{value: '', disabled: true}, [Validators.required, PasswordInputFromComponent.containsBlankSpaceValidator]],
confirmNewPassword: '' confirmNewPassword: [{value: '', disabled: true}, [Validators.required]]
}, },
{validators: [passwordValidator]} {validators: [passwordValidator]}
); );

View File

@ -36,21 +36,15 @@
type="text" required type="text" required
id="firstName" nbInput id="firstName" nbInput
fullWidth fullWidth
class="form-field" class="form-field name"
[status]="userFormGroup.get('firstName').dirty ? (userFormGroup.get('firstName').invalid ? 'danger' : 'basic') : 'basic'" [status]="userFormGroup.get('firstName').dirty ? (userFormGroup.get('firstName').invalid ? 'danger' : 'basic') : 'basic'"
placeholder="{{'profile.firstName.placeholder' | translate}} *"> placeholder="{{'profile.firstName.placeholder' | translate}} *">
<!-- FIXME: when the bug (https://github.com/angular/components/issues/7739) is fixed --> <ng-container *ngIf="userFirstNameControl.dirty">
<ng-template *ngIf="userFormGroup.get('firstName').dirty">
<span class="error-text" <span class="error-text"
*ngIf="userFormGroup.get('firstName')?.hasError('required')"> *ngIf="userFirstNameControl.hasError('required')">
{{'WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW'}}
{{'profile.validationMessage.firstNameRequired' | translate}} {{'profile.validationMessage.firstNameRequired' | translate}}
</span> </span>
<span class="error-text" </ng-container>
*ngIf="userFormGroup.get('firstName').invalid">
{{'Firstname is invalid' | translate}}
</span>
</ng-template>
</nb-form-field> </nb-form-field>
<!--Lastname--> <!--Lastname-->
<nb-form-field class="user-form-field"> <nb-form-field class="user-form-field">
@ -61,60 +55,66 @@
type="text" required type="text" required
id="lastName" nbInput id="lastName" nbInput
fullWidth fullWidth
class="form-field" class="form-field name"
[status]="userFormGroup.get('lastName').dirty ? (userFormGroup.get('lastName').invalid ? 'danger' : 'basic') : 'basic'" [status]="userFormGroup.get('lastName').dirty ? (userFormGroup.get('lastName').invalid ? 'danger' : 'basic') : 'basic'"
placeholder="{{'profile.lastName.placeholder' | translate}} *"> placeholder="{{'profile.lastName.placeholder' | translate}} *">
<!-- FIXME: when the bug (https://github.com/angular/components/issues/7739) is fixed --> <ng-container *ngIf="userLastNameControl.dirty">
<ng-template *ngIf="userFormGroup.get('lastName').dirty">
<span class="error-text" <span class="error-text"
*ngIf="userFormGroup.get('lastName')?.hasError('required')"> *ngIf="userLastNameControl.hasError('required')">
{{'profile.validationMessage.lastNameRequired' | translate}} {{'profile.validationMessage.lastNameRequired' | translate}}
</span> </span>
<span class="error-text" </ng-container>
*ngIf="userFormGroup.get('lastName').invalid">
{{'Lastname is invalid' | translate}}
</span>
</ng-template>
</nb-form-field> </nb-form-field>
</div> </div>
<!--ToDo: Email?--> <!--ToDo: Email?-->
</form> </form>
</div> </div>
</div> </div>
<div class="language-settings"> <div fxLayout="row" fxLayoutGap="2rem" fxLayoutAlign="start center">
<!--Language Radio Selection--> <div class="user-password-change">
<label class="language-selection-label"> <!--User password change-->
{{ 'profile.languageLabel' | translate }} <label class="password-selection-label">
</label> {{'profile.password.title' | translate}}
<div fxLayout="row" fxLayoutGap="1rem" fxLayoutAlign="start center"> </label>
<nb-radio-group name="language" [formControl]="profileLanguageControl" <div class="password-form">
class="language-radio-buttons languageContainer" status="info"> <button nbButton size="small"
<nb-radio value="{{profileLanguages.ENGLISH}}" (click)="onClickLanguage(profileLanguages.ENGLISH)"> class="password-btn"
<img src="../../assets/images/flags/{{profileLanguages.ENGLISH}}.svg" class="flag" width="25rem" status="info"
height="16rem" hero outline
alt=""> (click)="changePasswordInKeycloak()">
</nb-radio> <fa-icon [icon]="fa.faPassport" class="btn-icon"></fa-icon>
<nb-radio value="{{profileLanguages.GERMAN}}" (click)="onClickLanguage(profileLanguages.GERMAN)"> {{'profile.password.button' | translate}}
<img src="../../assets/images/flags/{{profileLanguages.GERMAN}}.svg" class="flag" width="25rem" </button>
height="16rem" <!--ToDo: Remove unused feature-->
alt=""> <!--<form class="password-form" [formGroup]="passwordFormGroup">
</nb-radio> <app-password-input-from
</nb-radio-group> formControlName="passwordInput"
[userServiceError$]="userServiceError"
(passwordStrong)="passwordStrong = $event">
</app-password-input-from>
</form>-->
</div>
</div> </div>
</div> <div class="language-settings">
<!--ToDo: Add accordion for keycloak values--> <!--Language radio selection-->
<div class="user-password-change"> <label class="language-selection-label">
<label class="password-selection-label"> {{ 'profile.languageLabel' | translate }}
{{'profile.password.title' | translate}} </label>
</label> <div fxLayout="row" fxLayoutGap="1rem" fxLayoutAlign="start center">
<div> <nb-radio-group name="language" [formControl]="profileLanguageControl"
<form class="password-form" [formGroup]="passwordFormGroup"> class="language-radio-buttons languageContainer" status="info">
<app-password-input-from <nb-radio value="{{profileLanguages.ENGLISH}}" (click)="onClickLanguage(profileLanguages.ENGLISH)">
formControlName="passwordInput" <img src="../../assets/images/flags/{{profileLanguages.ENGLISH}}.svg" class="flag" width="25rem"
[userServiceError$]="userServiceError" height="16rem"
(passwordStrong)="passwordStrong = $event"> alt="">
</app-password-input-from> </nb-radio>
</form> <nb-radio value="{{profileLanguages.GERMAN}}" (click)="onClickLanguage(profileLanguages.GERMAN)">
<img src="../../assets/images/flags/{{profileLanguages.GERMAN}}.svg" class="flag" width="25rem"
height="16rem"
alt="">
</nb-radio>
</nb-radio-group>
</div>
</div> </div>
</div> </div>
</nb-card-body> </nb-card-body>

View File

@ -3,7 +3,7 @@
.profile-setting-dialog { .profile-setting-dialog {
width: 45.25rem !important; width: 45.25rem !important;
height: 46rem; height: 32rem;
.dialog-header { .dialog-header {
height: 8vh; height: 8vh;
@ -54,6 +54,10 @@
width: 30rem; width: 30rem;
} }
.name {
width: 14.5rem !important;
}
.error-text { .error-text {
float: left; float: left;
color: nb-theme(color-danger-default); color: nb-theme(color-danger-default);
@ -62,7 +66,7 @@
} }
.language-settings{ .language-settings{
padding-top: 2rem; padding-top: 2.5rem;
.language-selection-label { .language-selection-label {
font-weight: bold; font-weight: bold;
@ -76,6 +80,7 @@
} }
.languageContainer { .languageContainer {
padding-top: 1rem;
display: flex; display: flex;
max-width: 8rem; max-width: 8rem;
min-width: 8rem; min-width: 8rem;
@ -88,11 +93,22 @@
} }
.user-password-change{ .user-password-change{
padding-top: 1rem; padding-top: 2rem;
.password-selection-label { .password-selection-label {
font-weight: bold; font-weight: bold;
} }
.password-form {
padding-top: 1rem;
.password-btn {
.btn-icon {
padding-bottom: 0.15rem;
padding-right: 0.5rem;
}
}
}
} }
} }
} }

View File

@ -92,8 +92,8 @@ export class ProfileSettingsComponent implements OnInit {
setupUserFormGroup(): void { setupUserFormGroup(): void {
this.userFormGroup = this.fb.group({ this.userFormGroup = this.fb.group({
username: [{value: '', disabled: true}, [Validators.required, Validators.pattern(Patterns.NO_WHITESPACES)]], username: [{value: '', disabled: true}, [Validators.required, Validators.pattern(Patterns.NO_WHITESPACES)]],
firstName: [{value: '', disabled: false}, [Validators.required, Validators.pattern(Patterns.NO_WHITESPACES)]], firstName: [{value: '', disabled: true}, [Validators.required, Validators.pattern(Patterns.NO_WHITESPACES)]],
lastName: [{value: '', disabled: false}, [Validators.required, Validators.pattern(Patterns.NO_WHITESPACES)]], lastName: [{value: '', disabled: true}, [Validators.required, Validators.pattern(Patterns.NO_WHITESPACES)]],
eMail: [{value: '', disabled: true}, [Validators.required, Validators.email, Validators.pattern(Patterns.NO_WHITESPACES)]], eMail: [{value: '', disabled: true}, [Validators.required, Validators.email, Validators.pattern(Patterns.NO_WHITESPACES)]],
avatarUploader: null avatarUploader: null
}); });
@ -112,6 +112,13 @@ export class ProfileSettingsComponent implements OnInit {
this.userPasswordInputControl = this.passwordFormGroup.get('passwordInput'); this.userPasswordInputControl = this.passwordFormGroup.get('passwordInput');
} }
changePasswordInKeycloak(): void {
this.userService.redirectToChangePasswordAction().then(r => {
// tslint:disable-next-line:no-console
console.info('Redirecting to Keycloak for password change...');
});
}
onClickLanguage(language: string): void { onClickLanguage(language: string): void {
this.translateService.use(language); this.translateService.use(language);
} }
@ -149,22 +156,20 @@ export class ProfileSettingsComponent implements OnInit {
} }
private handleUserUpdate(user: User): Observable<void> { private handleUserUpdate(user: User): Observable<void> {
/* return this.userService.updateUser(user, this.timeOfChange) return this.userService.changeUserProperties(user)
.pipe( .pipe(
tap({ tap({
next: resultingUser => { next: resultingUser => {
this.store.dispatch(new UpdateUserSettings(resultingUser)); this.store.dispatch(new UpdateUserSettings(resultingUser));
this.store.dispatch(new UpdateUser(resultingUser, true)); this.store.dispatch(new UpdateUser(resultingUser, true));
}, },
error: error => { error: error => {
console.error(error); console.error(error);
this.onFailedUpdate(error); }
} }),
}), mapTo(void 0),
mapTo(void 0), untilDestroyed(this)
untilDestroyed(this) );
);*/
return of();
} }
private handlePasswordChange(): void { private handlePasswordChange(): void {

View File

@ -61,7 +61,6 @@ export class PentestService {
templatePentests[i]?.childEntries?.forEach((childEntry: Pentest) => { templatePentests[i]?.childEntries?.forEach((childEntry: Pentest) => {
// ToDo: Add only child entrys that are not included in response aka available pentests // ToDo: Add only child entrys that are not included in response aka available pentests
if (!availablePentests.map(it => it.refNumber).includes(childEntry.refNumber)) { if (!availablePentests.map(it => it.refNumber).includes(childEntry.refNumber)) {
console.log('Child entry from template: ', childEntry);
availablePentests[indexOfPentestWithChildEntries].childEntries.push(childEntry); availablePentests[indexOfPentestWithChildEntries].childEntries.push(childEntry);
} else { } else {
// Removes the pentest from availablePentests and add it as a child entry // Removes the pentest from availablePentests and add it as a child entry

View File

@ -7,12 +7,15 @@ import {KeycloakService} from 'keycloak-angular';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
import {environment} from '../../../environments/environment'; import {environment} from '../../../environments/environment';
import {Route} from '@shared/models/route.enum'; import {Route} from '@shared/models/route.enum';
import {Project} from '@shared/models/project.model';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class UserService { export class UserService {
private keycloakBaseURL = `${environment.keycloakURL}/`;
constructor(private http: HttpClient, constructor(private http: HttpClient,
private keycloakService: KeycloakService, private keycloakService: KeycloakService,
private store: Store) { private store: Store) {
@ -56,25 +59,22 @@ export class UserService {
// https://stackoverflow.com/questions/33910615/is-there-an-api-call-for-changing-user-password-on-keycloak // https://stackoverflow.com/questions/33910615/is-there-an-api-call-for-changing-user-password-on-keycloak
// ToDo: https://www.keycloak.org/docs/latest/server_development/ // ToDo: https://www.keycloak.org/docs/latest/server_development/
public changeUserProperties(): Observable<any> { public changeUserProperties(user: User): Observable<any> {
// ToDo: There is a kc_action parameter available in keycloak to let application force required actions. // ToDo: There is a kc_action parameter available in keycloak to let application force required actions.
console.warn(user);
/*../realms/myrealm/protocol/openid-connect/auth /*../realms/myrealm/protocol/openid-connect/auth
?response_type=code ?response_type=code
&client_id=myclient &client_id=myclient
&redirect_uri=https://myclient.com &redirect_uri=https://myclient.com
&kc_action=update_profile*/ &kc_action=update_profile*/
return of(); return of(user);
} }
// ToDo: https://keycloak.discourse.group/t/integrate-change-password-from-account-console-into-own-webapp/12300 public redirectToChangePasswordAction(): Promise<void> {
public changePassword(): Observable<any> { // https://keycloak.discourse.group/t/integrate-change-password-from-account-console-into-own-webapp/12300
// ToDo: To force (or allow) a password update, use kc_action=UPDATE_PASSWORD return this.keycloakService.login({
/*../realms/myrealm/protocol/openid-connect/auth action: 'UPDATE_PASSWORD',
?response_type=code });
&client_id=myclient
&redirect_uri=https://myclient.com
&kc_action=update_profile*/
return of();
} }
private getToken(): Observable<string> { private getToken(): Observable<string> {

View File

@ -4,6 +4,7 @@
<span class="comments-count">{{numberOfComments}}</span> <span class="comments-count">{{numberOfComments}}</span>
</ng-container> </ng-container>
<ng-template #noComments> <ng-template #noComments>
{{'-'}} <fa-icon [icon]="fa.faComment" size="lg" class="comment-icon"></fa-icon>
<span class="comments-count">{{0}}</span>
</ng-template> </ng-template>
</div> </div>

View File

@ -4,6 +4,7 @@
<span class="findings-count">{{numberOfFindings}}</span> <span class="findings-count">{{numberOfFindings}}</span>
</ng-container> </ng-container>
<ng-template #noFindings> <ng-template #noFindings>
{{'-'}} <fa-icon [icon]="fa.faExclamationCircle" size="lg" class="finding-icon"></fa-icon>
<span class="findings-count">{{0}}</span>
</ng-template> </ng-template>
</div> </div>

View File

@ -37,8 +37,9 @@
<div class="project-progress"> <div class="project-progress">
<nb-progress-bar *ngIf="project.testingProgress > 0; else altProgressBar" <nb-progress-bar *ngIf="project.testingProgress > 0; else altProgressBar"
status="warning" status="warning"
title="{{project.testingProgress + '%'}}"
[value]="project.testingProgress" [value]="project.testingProgress"
[displayValue]="true"> [displayValue]="project.testingProgress > 25">
</nb-progress-bar> </nb-progress-bar>
<ng-template #altProgressBar> <ng-template #altProgressBar>
{{'popup.info' | translate}} {{'global.no.progress' | translate}} {{'popup.info' | translate}} {{'global.no.progress' | translate}}