fix: error messages for user profile dialog
This commit is contained in:
parent
ed7c70c62d
commit
65985e5ef3
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function sortDescending(nameOne: string, nameTwo: string): number {
|
||||||
|
return nameOne === nameTwo ? 0 : (nameOne > nameTwo ? 1 : -1);
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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]}
|
||||||
);
|
);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
Loading…
Reference in New Issue