diff --git a/pom.xml b/pom.xml index b15f6e3bdf7d0cad23c52ff816572a214033536b..548cf6c6dc9af2f14b2b703ce8b21b16bfa96974 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.source>1.8</maven.compiler.source> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <edelivery.ssl-auth.version>1.12</edelivery.ssl-auth.version> + <edelivery.ssl-auth.version>1.13-SNAPSHOT</edelivery.ssl-auth.version> <edelivery.dynamic-discovery-client.version>2.0</edelivery.dynamic-discovery-client.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index 58b03916039388575e70139a191ab755cb276207..bc2b620b765a34bfa7686187cb0c2eeba5092799 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -1,5 +1,4 @@ import 'hammerjs'; -import {AccessTokenGenerationDialogComponent} from "./common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component"; import {AccessTokenPanelComponent} from "./user-settings/user-access-tokens/access-token-panel/access-token-panel.component"; import {AdminDomainComponent} from "./system-settings/admin-domain/admin-domain.component"; import {AdminDomainService} from "./system-settings/admin-domain/admin-domain.service"; @@ -107,9 +106,7 @@ import {ToolbarComponent} from "./window/toolbar/toolbar.component"; import {UserAccessTokensComponent} from "./user-settings/user-access-tokens/user-access-tokens.component"; import {UserCertificatePanelComponent} from "./user-settings/user-certificates/user-certificate-panel/user-certificate-panel.component"; import {UserCertificatesComponent} from "./user-settings/user-certificates/user-certificates.component"; -import {UserComponent} from './system-settings/user/user.component'; -import {UserDetailsDialogComponent} from './system-settings/user/user-details-dialog/user-details-dialog.component'; -import {UserDetailsService} from './system-settings/user/user-details-dialog/user-details.service'; +import {UserDetailsService} from './system-settings/user/user-details.service'; import {UserProfileComponent} from "./user-settings/user-profile/user-profile.component"; import {UserService} from './system-settings/user/user.service'; import {routing} from './app.routes'; @@ -150,7 +147,6 @@ import {HttpErrorHandlerService} from "./common/error/http-error-handler.service @NgModule({ declarations: [ - AccessTokenGenerationDialogComponent, AccessTokenPanelComponent, AdminDomainComponent, AdminKeystoreComponent, @@ -232,8 +228,6 @@ import {HttpErrorHandlerService} from "./common/error/http-error-handler.service UserAccessTokensComponent, UserCertificatePanelComponent, UserCertificatesComponent, - UserComponent, - UserDetailsDialogComponent, UserProfileComponent, UserProfilePanelComponent, ], diff --git a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.css b/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.css deleted file mode 100644 index 56d452bb78d4e03eb3cf39af8e6f71eb35ef3afd..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.css +++ /dev/null @@ -1,3 +0,0 @@ -.empty-field-label { - color: gray; -} diff --git a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.html b/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.html deleted file mode 100644 index 5a8c968898ff874c77656cf377028e3fccf4bb81..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.html +++ /dev/null @@ -1,64 +0,0 @@ -<h2 mat-dialog-title>{{formTitle}}</h2> -<mat-dialog-content style="width:700px"> - <div *ngIf="message" - [ngClass]="{ 'alert-message': message, 'alert-message-success': messageType === 'success', 'alert-message-error':messageType === 'error' }" - id="alertmessage_id"> - <span class="alert-message-close-button" (click)="clearAlert()">×</span> - {{message}} - </div> - <form [formGroup]="dialogForm"> - <mat-card> - <mat-card-content fxLayout="column"> - <mat-form-field style="width:100%"> - <input matInput placeholder="Generate access token for Username" formControlName="username" id="un_id" readonly="true"> - </mat-form-field> - <mat-form-field style="width:100%"> - <input matInput placeholder="Generate access token for User with email" formControlName="email" id="em_id" - [ngClass]="{ 'empty-field-label': isEmptyEmailAddress }" readonly="true" > - </mat-form-field> - </mat-card-content> - </mat-card> - <mat-card> - <mat-card-content> - <mat-form-field *ngIf="!securityService.getCurrentUser()?.casAuthenticated" style="width:100%"> - <input matInput [placeholder]="getPasswordTitle" [type]="hideCurrPwdFiled ? 'password' : 'text'" - formControlName="current-password" required id="cp_id"> - <mat-icon matSuffix - (click)="hideCurrPwdFiled = !hideCurrPwdFiled">{{hideCurrPwdFiled ? 'visibility_off' : 'visibility'}}</mat-icon> - <smp-field-error *ngIf="passwordError('current-password', 'required')">Password is required</smp-field-error > - </mat-form-field> - - <mat-card-actions> - <button id="regenerateAccessTokenButton" mat-raised-button color="primary" (click)="regenerateAccessToken()" - [disabled]="!dialogForm.valid"> - <mat-icon>check_circle</mat-icon> - <span>Regenerate access token</span> - </button> - <mat-label *ngIf="adminUser" style="color: red;font-weight: bold"> - Token will be generated immediately. - </mat-label> - </mat-card-actions> - - <mat-form-field style="width:100%"> - <input matInput placeholder="Access token id" formControlName="accessTokenId" id="at_id" readonly="true"> - </mat-form-field> - - <mat-form-field style="width:100%"> - <input matInput placeholder="Valid until" - value="{{dialogForm.get('accessTokenExpireOn').value | date:dateTimeFormat}}" - id="expireOn_id" - readonly="true"> - </mat-form-field> - </mat-card-content> - </mat-card> - </form> -</mat-dialog-content> -<div class="required-fields">* required fields</div> - -<mat-dialog-actions> - <button id="closeDialogButton" mat-raised-button color="primary" (click)="closeDialog()"> - <mat-icon>cancel</mat-icon> - <span>Close</span> - </button> -</mat-dialog-actions> - diff --git a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.spec.ts b/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.spec.ts deleted file mode 100644 index d71a0fdc74e1e58fc014cb70720ef59668b23ced..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PasswordChangeDialogComponent } from './password-change-dialog.component'; - -describe('PasswordChangeDialogComponent', () => { - let component: PasswordChangeDialogComponent; - let fixture: ComponentFixture<PasswordChangeDialogComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ PasswordChangeDialogComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(PasswordChangeDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.ts b/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.ts deleted file mode 100644 index a1d14ea1918d66e92ab9ae2e38076053f354cf4e..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component.ts +++ /dev/null @@ -1,142 +0,0 @@ -import {Component, Inject} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; -import {UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators} from "@angular/forms"; -import {User} from "../../../security/user.model"; -import {GlobalLookups} from "../../global-lookups"; -import {UserDetailsService} from "../../../system-settings/user/user-details-dialog/user-details.service"; -import {AccessTokenRo} from "./access-token-ro.model"; -import {SecurityService} from "../../../security/security.service"; -import {SmpConstants} from "../../../smp.constants"; -import {EntityStatus} from "../../enums/entity-status.enum"; - -@Component({ - selector: 'smp-access-token-generation-dialog', - templateUrl: './access-token-generation-dialog.component.html', - styleUrls: ['./access-token-generation-dialog.component.css'] -}) -export class AccessTokenGenerationDialogComponent { - - dateTimeFormat: string = SmpConstants.DATE_TIME_FORMAT; - formTitle = "Access token generation dialog"; - dialogForm: UntypedFormGroup; - hideCurrPwdFiled: boolean = true; - hideNewPwdFiled: boolean = true; - hideConfPwdFiled: boolean = true; - tokenChanged: boolean = false; - adminUser: boolean = false; - current: User; - message: string; - messageType: string = "alert-error"; - - - constructor( - public dialogRef: MatDialogRef<AccessTokenGenerationDialogComponent>, - @Inject(MAT_DIALOG_DATA) public data: any, - private lookups: GlobalLookups, - private userDetailsService: UserDetailsService, - public securityService: SecurityService, - private fb: UntypedFormBuilder - ) { - dialogRef.disableClose = true;//disable default close operation - - this.current = {...data.user} - this.adminUser = data.adminUser - - - this.dialogForm = fb.group({ - 'email': new UntypedFormControl({value: null, readonly: true}, null), - 'username': new UntypedFormControl({value: null, readonly: true}, null), - 'accessTokenId': new UntypedFormControl({value: null, readonly: true}, null), - 'accessTokenExpireOn': new UntypedFormControl({value: null, readonly: true}, null), - 'current-password': new UntypedFormControl({value: null, readonly: false}, this.securityService.getCurrentUser().casAuthenticated?null:[Validators.required]), - }); - - this.dialogForm.controls['email'].setValue(this.isEmptyEmailAddress ? "Empty email address!" : this.current.emailAddress); - this.dialogForm.controls['username'].setValue(this.current.username); - this.dialogForm.controls['accessTokenId'].setValue(this.current.accessTokenId); - this.dialogForm.controls['accessTokenExpireOn'].setValue(this.current.accessTokenExpireOn); - this.dialogForm.controls['current-password'].setValue(''); - this.tokenChanged = false; - } - - public passwordError = (controlName: string, errorName: string) => { - return this.dialogForm.controls[controlName].hasError(errorName); - } - - get isEmptyEmailAddress() { - return !this.current.emailAddress; - } - - get getPasswordTitle(): string{ - return this.adminUser?"Admin password for user ["+this.securityService.getCurrentUser().username+"]":"Current password"; - } - - regenerateAccessToken() { - this.clearAlert(); - - if (this.adminUser) { -// update password - this.userDetailsService.regenerateAccessTokenAdmin(this.securityService.getCurrentUser().userId, - this.dialogForm.controls['current-password'].value, - this.current.userId - ).subscribe((response: AccessTokenRo) => { - this.showSuccessMessage("Token with id: " + response.identifier + " and value: " + response.value + " was generated!") - this.current.accessTokenId = response.identifier; - this.current.accessTokenExpireOn = response.expireOn; - // set to current form - this.dialogForm.controls['accessTokenId'].setValue(this.current.accessTokenId); - this.dialogForm.controls['accessTokenExpireOn'].setValue(this.current.accessTokenExpireOn); - this.tokenChanged = true; - }, - (err) => { - this.showErrorMessage(err.error.errorDescription); - } - ); - } else { - // update access token for currently logged-in user - this.userDetailsService.regenerateAccessToken(this.current.userId, - this.dialogForm.controls['current-password'].value).subscribe((response: AccessTokenRo) => { - this.showSuccessMessage("Token with id: " + response.identifier + " and value: " + response.value + " was generated!") - this.current.accessTokenId = response.identifier; - this.current.accessTokenExpireOn = response.expireOn; - // set to current form - this.dialogForm.controls['accessTokenId'].setValue(this.current.accessTokenId); - this.dialogForm.controls['accessTokenExpireOn'].setValue(this.current.accessTokenExpireOn); - // save new values - const user = {...this.current, status: EntityStatus.UPDATED}; - //this.securityService.updateUserDetails(user); - this.tokenChanged = true; - }, - (err) => { - this.showErrorMessage(err.error.errorDescription); - } - ); - } - } - - showSuccessMessage(value: string) { - this.message = value; - this.messageType = "success"; - } - - showErrorMessage(value: string) { - this.message = value; - this.messageType = "error"; - } - - clearAlert() { - this.message = null; - this.messageType = null; - } - - public getCurrent() { - if (this.tokenChanged) { - return this.current; - } - return null; - } - - closeDialog() { - this.dialogRef.close(this.getCurrent()) - } -} diff --git a/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts b/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts index 239e92587065fdd62f6273a30eb2910c12d9c0a4..031cd48f5cdc116c256fab2bc152bff6f1a5aad7 100644 --- a/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts +++ b/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts @@ -2,7 +2,7 @@ import {Component, Inject} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; import {SmpConstants} from "../../../smp.constants"; -import {AccessTokenRo} from "../access-token-generation-dialog/access-token-ro.model"; +import {AccessTokenRo} from "../../model/access-token-ro.model"; import {UserService} from "../../../system-settings/user/user.service"; import {CredentialRo} from "../../../security/credential.model"; import {CertificateRo} from "../../../system-settings/user/certificate-ro.model"; diff --git a/smp-angular/src/app/common/dialogs/password-change-dialog/password-change-dialog.component.ts b/smp-angular/src/app/common/dialogs/password-change-dialog/password-change-dialog.component.ts index 8ae82bb7c34af75f5598e2f155617e7602ef8c17..4d04629980f02979cc88e1a5c2dadd7070ee4d3f 100644 --- a/smp-angular/src/app/common/dialogs/password-change-dialog/password-change-dialog.component.ts +++ b/smp-angular/src/app/common/dialogs/password-change-dialog/password-change-dialog.component.ts @@ -3,7 +3,7 @@ import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog import {AbstractControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, ValidatorFn, Validators} from "@angular/forms"; import {User} from "../../../security/user.model"; import {GlobalLookups} from "../../global-lookups"; -import {UserDetailsService} from "../../../system-settings/user/user-details-dialog/user-details.service"; +import {UserDetailsService} from "../../../system-settings/user/user-details.service"; import {AlertMessageService} from "../../alert-message/alert-message.service"; import {SecurityService} from "../../../security/security.service"; import {InformationDialogComponent} from "../information-dialog/information-dialog.component"; diff --git a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-ro.model.ts b/smp-angular/src/app/common/model/access-token-ro.model.ts similarity index 69% rename from smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-ro.model.ts rename to smp-angular/src/app/common/model/access-token-ro.model.ts index 71bffc523ae6c0832041eae5ad18bacc430e6cca..8c492875d50a2e5273d6db64d7a02c69436babbd 100644 --- a/smp-angular/src/app/common/dialogs/access-token-generation-dialog/access-token-ro.model.ts +++ b/smp-angular/src/app/common/model/access-token-ro.model.ts @@ -1,4 +1,4 @@ -import {CredentialRo} from "../../../security/credential.model"; +import {CredentialRo} from "../../security/credential.model"; export interface AccessTokenRo { identifier: string; diff --git a/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts b/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts index 81b37e8d15b36b95b11079a7e0fe2abbfe9852b4..8a05ea83b76e7a0eb19f176182b2f25f47888ee3 100644 --- a/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts +++ b/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts @@ -34,6 +34,7 @@ export class ResourceDialogComponent { participantSchemePattern = '^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+$'; participantSchemeMessage:string; + submitInProgress:boolean = false; @ViewChild('identifierValue', {static: false}) identifierValue: ElementRef; constructor(@Inject(MAT_DIALOG_DATA) public data: any, @@ -129,7 +130,7 @@ export class ResourceDialogComponent { } get submitButtonEnabled(): boolean { - return this.resourceForm.valid && this.resourceForm.dirty; + return this.resourceForm.valid && this.resourceForm.dirty && !this.submitInProgress; } public onSaveButtonClicked() { @@ -144,23 +145,29 @@ export class ResourceDialogComponent { public createResource(resource: ResourceRo) { + this.submitInProgress = true; this.editGroupService.createResourceForGroup(this.resource, this.group, this.domain).subscribe((result: ResourceRo) => { if (!!result) { this.closeDialog(); } + this.submitInProgress = false; }, (error) => { this.alertService.error(error.error?.errorDescription) + this.submitInProgress = false; }); } public saveResource(resource: ResourceRo) { + this.submitInProgress = true; this.editGroupService.updateResourceForGroup(this.resource, this.group, this.domain).subscribe((result: ResourceRo) => { if (!!result) { this.closeDialog(); } + this.submitInProgress = false; }, (error) => { this.alertService.error(error.error?.errorDescription) + this.submitInProgress = false; }); } diff --git a/smp-angular/src/app/login/login.component.ts b/smp-angular/src/app/login/login.component.ts index 4ced0f78bd77398c533a3fd5163c8def0260f41a..d779d4397a41a007f51ed441eba70cdd57859113 100644 --- a/smp-angular/src/app/login/login.component.ts +++ b/smp-angular/src/app/login/login.component.ts @@ -11,9 +11,9 @@ import {Subscription} from 'rxjs'; import {ExpiredPasswordDialogComponent} from '../common/dialogs/expired-password-dialog/expired-password-dialog.component'; import {GlobalLookups} from "../common/global-lookups"; import {PasswordChangeDialogComponent} from "../common/dialogs/password-change-dialog/password-change-dialog.component"; -import {UserDetailsDialogMode} from "../system-settings/user/user-details-dialog/user-details-dialog.component"; import {InformationDialogComponent} from "../common/dialogs/information-dialog/information-dialog.component"; import {DatePipe, formatDate} from "@angular/common"; +import {EntityStatus} from "../common/enums/entity-status.enum"; @Component({ moduleId: module.id, @@ -124,7 +124,7 @@ export class LoginComponent implements OnInit, OnDestroy { ...config, data: { ...config.data, - mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE) + mode: config.data.mode || (config.data.edit ? EntityStatus.PERSISTED :EntityStatus.NEW) } } : config; diff --git a/smp-angular/src/app/system-settings/admin-keystore/admin-keystore.component.ts b/smp-angular/src/app/system-settings/admin-keystore/admin-keystore.component.ts index c388fb6d852f2b21bb60232c6875b61509622405..f541e8628cebd321c3280717859a7d696db5d766 100644 --- a/smp-angular/src/app/system-settings/admin-keystore/admin-keystore.component.ts +++ b/smp-angular/src/app/system-settings/admin-keystore/admin-keystore.component.ts @@ -99,6 +99,8 @@ export class AdminKeystoreComponent implements OnInit, OnDestroy, AfterViewInit, this.selected = null; this.dataSource.data = this.keystoreCertificates; + // show the last page + this.paginator.lastPage(); } diff --git a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts index e7cf2b36668112f48cb2e44e99f6403dd0374e88..3fbc5629ebef853fb35372c8dbf7d9ef60f26a48 100644 --- a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts +++ b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts @@ -14,9 +14,9 @@ import {SecurityService} from "../../security/security.service"; import { PasswordChangeDialogComponent } from "../../common/dialogs/password-change-dialog/password-change-dialog.component"; -import {UserDetailsDialogMode} from "../user/user-details-dialog/user-details-dialog.component"; import {ApplicationRoleEnum} from "../../common/enums/application-role.enum"; import {HttpErrorHandlerService} from "../../common/error/http-error-handler.service"; +import {EntityStatus} from "../../common/enums/entity-status.enum"; @Component({ @@ -239,7 +239,7 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { ...config, data: { ...config.data, - mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE) + mode: config.data.mode || (config.data.edit ? EntityStatus.PERSISTED : EntityStatus.NEW) } } : config; diff --git a/smp-angular/src/app/system-settings/user/user-controller.ts b/smp-angular/src/app/system-settings/user/user-controller.ts index 42fbe26c657a41a4c4618051f9a0ee096a14cd4d..5871567f18fa4a7ef0401230fa6308b4d921e254 100644 --- a/smp-angular/src/app/system-settings/user/user-controller.ts +++ b/smp-angular/src/app/system-settings/user/user-controller.ts @@ -1,6 +1,5 @@ import {SearchTableController} from '../../common/search-table/search-table-controller'; import {MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog'; -import {UserDetailsDialogComponent, UserDetailsDialogMode} from './user-details-dialog/user-details-dialog.component'; import {UserRo} from './user-ro.model'; import {EntityStatus} from '../../common/enums/entity-status.enum'; import {GlobalLookups} from "../../common/global-lookups"; @@ -10,7 +9,6 @@ import {SmpConstants} from "../../smp.constants"; import {HttpClient} from "@angular/common/http"; import {CertificateRo} from "./certificate-ro.model"; import {PasswordChangeDialogComponent} from "../../common/dialogs/password-change-dialog/password-change-dialog.component"; -import {AccessTokenGenerationDialogComponent} from "../../common/dialogs/access-token-generation-dialog/access-token-generation-dialog.component"; import {ApplicationRoleEnum} from "../../common/enums/application-role.enum"; @@ -26,10 +24,11 @@ export class UserController implements SearchTableController { } public showDetails(row: any) { - let dialogRef: MatDialogRef<UserDetailsDialogComponent> = this.dialog.open(UserDetailsDialogComponent); + /*let dialogRef: MatDialogRef<UserDetailsDialogComponent> = this.dialog.open(UserDetailsDialogComponent); dialogRef.afterClosed().subscribe(result => { //Todo: }); + */ } public edit(row: any) { @@ -38,25 +37,21 @@ export class UserController implements SearchTableController { public delete(row: any) { } - public newDialog(config?: MatDialogConfig): MatDialogRef<UserDetailsDialogComponent> { - return this.dialog.open(UserDetailsDialogComponent, this.convertWithMode(config)); + public newDialog(config?: MatDialogConfig): MatDialogRef<any> { + //return this.dialog.open(UserDetailsDialogComponent, this.convertWithMode(config)); + return null; } public changePasswordDialog(config?: MatDialogConfig): MatDialogRef<PasswordChangeDialogComponent> { return this.dialog.open(PasswordChangeDialogComponent, this.convertWithMode(config)); } - public generateAccessTokenDialog(config?: MatDialogConfig): MatDialogRef<AccessTokenGenerationDialogComponent> { - return this.dialog.open(AccessTokenGenerationDialogComponent, this.convertWithMode(config)); - } - private convertWithMode(config) { return (config && config.data) ? { ...config, data: { ...config.data, - mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE) } } : config; diff --git a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.css b/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.css deleted file mode 100644 index def6ba61f19991b6e165714de2503c4c80043c5e..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.css +++ /dev/null @@ -1,45 +0,0 @@ -#custom-file-upload { - display: none; -} - -.custom-file-upload { - display: inline-block; - cursor: pointer; -} - -.user-panel { - -} - -.user-toggle { - width: 220px; -} - -.role-field { - width: 220px; -} -.username { - width: calc(100% - 250px); -} - -.email { - width: calc(100% - 250px); -} - -.role, .certificate-subject, .certificate-issuer, .certificate-serial-number, .certificate-id { - width: 100%; -} - -.certificate-valid-from, .certificate-valid-to { - width: 40%; -} - -.required-fields { - text-align: right; - font-size: 70% -} - -.has-error { - color: red; - font-size: 70%; -} diff --git a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.html b/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.html deleted file mode 100644 index 7d410bbd20b4565abd52e9001425e95eec6ba20f..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.html +++ /dev/null @@ -1,225 +0,0 @@ -<h2 mat-dialog-title>{{mode}}</h2> -<mat-dialog-content style="width:950px"> - - - <mat-card> - <mat-card-content> - <div style="display: flex;flex-flow: row;flex-wrap: wrap; justify-content: space-between;"> - - <mat-form-field class="username"> - <input matInput placeholder="Username" [formControl]="userForm.controls['username']" - id="username_id" maxlength="255" required> - <div *ngIf="userForm.controls['username'].hasError('required') && userForm.controls['username'].touched" - class="has-error">You should type an username - </div> - <div *ngIf="userForm.controls['username'].hasError('pattern') && userForm.controls['username'].touched" - class="has-error">Username is case insensitive and can only contain alphanumeric characters (letters - a-zA-Z, numbers 0-9) and must - have from 4 to 32 characters! - </div> - <div - *ngIf="(!editMode && userForm.controls['username'].touched || editMode) && userForm.controls['username'].hasError('notInList')" - class="has-error"> - Username already exists! - </div> - </mat-form-field> - - <div class="user-toggle"> - <mat-slide-toggle *ngIf="!isPreferencesMode()" - mat-no-ink class="mat-primary" [formControl]="userForm.controls['active']" id="active_id"> - Active - </mat-slide-toggle> - </div> - </div> - <div style="display: flex;flex-flow: row;flex-wrap: wrap; justify-content: space-between;"> - <mat-form-field class="emailAddress" class="email"> - <input matInput placeholder="Email address" name="emailAddress" - [formControl]="userForm.controls['emailAddress']" - id="emailAddress_id" maxlength="255"> - <div - *ngIf="userForm.controls['emailAddress'].hasError('pattern') && userForm.controls['emailAddress'].touched" - class="has-error">Email is invalid! - </div> - </mat-form-field> - <mat-form-field class="role-field"> - <mat-select matInput placeholder="Role" class="role" [formControl]="userForm.controls['role']" - id="role_id" required> - <mat-option *ngFor="let item of existingRoles" [value]="item">{{item}}</mat-option> - </mat-select> - <div *ngIf="userForm.controls['role'].hasError('required') && userForm.controls['role'].touched" - class="has-error">You need to choose at least one role for this user - </div> - </mat-form-field> - </div> - </mat-card-content> - </mat-card> - <div style="display: flex;flex-flow: row;"> - <mat-card style="flex-grow: 1"> - <mat-card-title>UI authentication credentials</mat-card-title> - <mat-card-content *ngIf="isUserAuthPasswdEnabled()"> - <fieldset style="border: solid gray 1px;"> - <legend>Username/password credentials</legend> - <div style="display: flex;flex-flow: row wrap;"> - <mat-form-field style="flex-grow: 2"> - <input matInput placeholder="Username" [value]="userForm.controls['username'].value" - id="username-password_id" maxlength="255" disabled readonly> - </mat-form-field> - <mat-form-field *ngIf="!!userForm.get('passwordExpireOn').value" style="flex-grow: 1"> - <input matInput placeholder="Valid until" - value="{{userForm.get('passwordExpireOn').value | date:dateTimeFormat}}" - maxlength="255" disabled> - </mat-form-field> - <mat-form-field *ngIf="!userForm.get('passwordExpireOn').value" style="flex-grow: 1"> - <input matInput placeholder="Valid until" style="color: red" - matTooltip="Default password set by system admin! User must change password immediately!" - value="Default or null password" - maxlength="255" disabled> - </mat-form-field> - </div> - <div style="display: flex;flex-flow: row wrap;"> - <mat-form-field style="flex-grow: 2"> - <input matInput placeholder="Seq. failed attempts" - [value]="userForm.controls['sequentialLoginFailureCount'].value" - id="sequentialLoginFailureCount_id" maxlength="255" disabled readonly> - </mat-form-field> - <mat-form-field style="flex-grow: 1"> - <input matInput placeholder="Last failed attempt" - value="{{!userForm.get('lastFailedLoginAttempt').value?nullValue:userForm.get('lastFailedLoginAttempt').value | date:dateTimeFormat}}" - maxlength="255" disabled> - </mat-form-field> - </div> - <div style="display: flex;flex-flow: row wrap;"> - <mat-form-field style="flex-grow: 1"> - <input matInput placeholder="Suspended until" - value="{{!userForm.get('suspendedUtil').value?nullValue:userForm.get('suspendedUtil').value | date:dateTimeFormat}}" - maxlength="255" disabled> - </mat-form-field> - </div> - <button mat-flat-button color="primary" style="width: 100%" id="changePassword_id" - (click)="changeCurrentUserPassword()" [disabled]="!editMode"> - <span>Set/change password</span> - </button> - <div *ngIf="!editMode" - style="color: red"> Password can be set after the user is saved! - </div> - </fieldset> - </mat-card-content> - <mat-card-content *ngIf="isUserAuthSSOEnabled()"> - <fieldset style="border: solid gray 1px;"> - <legend>CAS authentication</legend> - <mat-form-field style="width: 100%"> - <input matInput placeholder="Cas identifier" [formControl]="userForm.controls['username']" - id="cas-user_id" maxlength="255" disabled readonly> - </mat-form-field> - <button mat-flat-button color="primary" style="width: 100%" id="openCASData" - [disabled]="!this.current?.casUserDataUrl" - (click)="openCurrentCasUserData()"> - <span>Open CAS user data</span> - </button> - - </fieldset> - </mat-card-content> - </mat-card> - <mat-card style="flex-grow: 2"> - <mat-card-title>Web service credentials</mat-card-title> - <mat-card-content *ngIf="isWebServiceUserTokenAuthPasswdEnabled()"> - <fieldset style="border: solid gray 1px;"> - <legend>Access token credentials</legend> - <div style="display: flex;flex-flow: row wrap;"> - <mat-form-field style="flex-grow: 2"> - <input matInput placeholder="Access token ID" [formControl]="userForm.controls['accessTokenId']" - maxlength="255" disabled> - </mat-form-field> - <mat-form-field *ngIf="!!userForm.get('accessTokenExpireOn').value" style="flex-grow: 1"> - <input matInput placeholder="Valid until" - value="{{userForm.get('accessTokenExpireOn').value | date:dateTimeFormat}}" - maxlength="255" disabled> - </mat-form-field> - <mat-form-field *ngIf="!userForm.get('accessTokenExpireOn').value" style="flex-grow: 1"> - <input matInput placeholder="Valid until" style="color: red" - matTooltip="Default access token set by system admin! User must regenerate access token immediately!" - value="Default or null access token" - maxlength="255" disabled> - </mat-form-field> - </div> - <div style="display: flex;flex-flow: row wrap;"> - <mat-form-field style="flex-grow: 2"> - <input matInput placeholder="Seq. failed attempts" - [value]="userForm.controls['sequentialTokenLoginFailureCount'].value" - id="sequentialTokenLoginFailureCount_id" maxlength="255" disabled readonly> - </mat-form-field> - <mat-form-field style="flex-grow: 1"> - <input matInput placeholder="Last failed attempt" - value="{{!userForm.get('lastTokenFailedLoginAttempt').value?nullValue:userForm.get('lastTokenFailedLoginAttempt').value | date:dateTimeFormat}}" - maxlength="255" disabled> - </mat-form-field> - </div> - <div style="display: flex;flex-flow: row wrap;"> - <mat-form-field style="flex-grow: 1"> - <input matInput placeholder="Suspended until" - value="{{!userForm.get('tokenSuspendedUtil').value?nullValue:userForm.get('tokenSuspendedUtil').value | date:dateTimeFormat}}" - maxlength="255" disabled> - </mat-form-field> - </div> - <button id="regenerateAccessTokenButton" mat-flat-button color="primary" style="width: 100%" [disabled]="!editMode" - (click)="regenerateAccessToken()"> - <span>Regenerate access token</span> - </button> - <div *ngIf="!editMode" - style="color: red"> Access token can be set after the user is saved! - </div> - </fieldset> - </mat-card-content> - - <mat-card-content *ngIf="isWebServiceUserCertificateAuthEnabled()"> - <fieldset style="border: solid gray 1px;"> - <legend>Certificate authentication</legend> - <mat-form-field class="certificate-id"> - <input matInput placeholder="SMP certificate ID" [formControl]="userForm.controls['certificateId']" - id="certificateId_id" - resizeable="true"> - </mat-form-field> - <div - *ngIf="isCertificateInvalid" - [style.color]="'red'"> - {{certificateValidationMessage}} - </div> - - <div style="display: flex; flex-flow: row;align-items: stretch;"> - <label class="custom-file-upload" style="flex-grow: 1"> - <input #fileInput type="file" id="custom-file-upload" accept=".cer,.crt,.pem,.der" - (change)="uploadCertificate($event)"> - <button id="importCertificateButton" mat-flat-button color="primary" (click)="fileInput.click()" - >Import - </button> - </label> - - <button mat-flat-button color="primary" style="flex-grow: 1" - [disabled]="!userForm.get('certificateId').value" - (click)="onShowCertificateDataRow()"> - <span>Show details</span> - </button> - <button mat-flat-button color="primary" style="flex-grow: 1" - [disabled]="!userForm.get('certificateId').value" - (click)="clearCertificate()"> - <span>Clear</span> - </button> - </div> - </fieldset> - </mat-card-content> - </mat-card> - </div> -</mat-dialog-content> -<div class="required-fields">* required fields</div> -<mat-dialog-actions> - <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="submitForm()" - [disabled]="!userForm.valid "> - <mat-icon>check_circle</mat-icon> - <span>OK</span> - </button> - <button mat-raised-button color="primary" mat-dialog-close> - <mat-icon>cancel</mat-icon> - <span>Cancel</span> - </button> -</mat-dialog-actions> - diff --git a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.spec.ts b/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.spec.ts deleted file mode 100644 index 00187815861780b58826057aabf33a59363d2538..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import {UserDetailsDialogComponent} from './user-details-dialog.component'; - -describe('UserDetailsDialogComponent', () => { - let component: UserDetailsDialogComponent; - let fixture: ComponentFixture<UserDetailsDialogComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ UserDetailsDialogComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UserDetailsDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.ts b/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.ts deleted file mode 100644 index e1ea10baac69e4ffc4b3319e25a7ec11ce5534cd..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details-dialog.component.ts +++ /dev/null @@ -1,421 +0,0 @@ -import {Component, Inject, ViewChild} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog'; - -import { - AbstractControl, - UntypedFormBuilder, - UntypedFormControl, - UntypedFormGroup, - ValidationErrors, - ValidatorFn, - Validators -} from '@angular/forms'; -import {Role} from '../../../security/role.model'; -import {UserRo} from '../user-ro.model'; -import {EntityStatus} from '../../../common/enums/entity-status.enum'; -import {AlertMessageService} from '../../../common/alert-message/alert-message.service'; -import {CertificateService} from '../certificate.service'; -import {CertificateRo} from "../certificate-ro.model"; -import {DatePipe} from "../../../custom-date/date.pipe"; -import {GlobalLookups} from "../../../common/global-lookups"; -import {UserDetailsService} from "./user-details.service"; -import {SecurityService} from "../../../security/security.service"; -import {UserController} from "../user-controller"; -import {HttpClient} from "@angular/common/http"; -import {CertificateDialogComponent} from "../../../common/dialogs/certificate-dialog/certificate-dialog.component"; -import {SmpConstants} from "../../../smp.constants"; -import {ApplicationRoleEnum} from "../../../common/enums/application-role.enum"; - -@Component({ - selector: 'user-details-dialog', - templateUrl: './user-details-dialog.component.html', - styleUrls: ['user-details-dialog.component.css'], -}) -export class UserDetailsDialogComponent { - - @ViewChild('fileInput') private fileInput; - - readonly emailPattern = '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}'; - readonly dateFormat: string = 'yyyy-MM-dd HH:mm:ssZ'; - readonly usernamePattern = '^[a-zA-Z0-9]{4,32}$'; - readonly dateTimeFormat: string = SmpConstants.DATE_TIME_FORMAT; - readonly nullValue: string = SmpConstants.NULL_VALUE; - mode: UserDetailsDialogMode; - editMode: boolean; - userId: string; - userRoles = []; - certificateValidationMessage: string = null; - isCertificateInvalid: boolean = true; - existingRoles = []; - userForm: UntypedFormGroup; - current: UserRo; - tempStoreForCertificate: CertificateRo = this.newCertificateRo(); - tempStoreForUser: UserRo = this.newUserRo(); - newCertFile: File = null; - userController: UserController; - - - private certificateValidator: ValidatorFn = (control: UntypedFormGroup): ValidationErrors | null => { - const certificateId = control.get('certificateId'); - const subject = control.get('subject'); - const validFrom = control.get('validFrom'); - const validTo = control.get('validTo'); - const issuer = control.get('issuer'); - const serialNumber = control.get('serialNumber'); - return certificateId && subject && validFrom && validTo && issuer && serialNumber - && !!certificateId.value - && !(subject.value && validFrom.value && validTo.value && issuer.value && serialNumber.value) - && !this.isCertificateInvalid - ? {certificateDetailsRequired: true} : null; - }; - - private certificateExistValidator: ValidatorFn = (control: UntypedFormGroup): ValidationErrors | null => { - const certificateId = control.get('certificateId'); - // get all persisted - const listIds = this.lookups.cachedServiceGroupOwnerList.map(a => a.certificate ? a.certificate.certificateId : "NoId"); - - return certificateId && certificateId.value - && listIds.includes(certificateId.value) && this.current.certificate && certificateId.value !== this.current.certificate.certificateId ? {certificateIdExists: true} : null; - }; - - - notInList(list: string[]) { - return (c: AbstractControl): { [key: string]: any } => { - if (c.value && list.includes(c.value.toString().toLowerCase())) { - return {'notInList': {valid: false}}; - } - return null; - } - } - - - constructor(private dialogRef: MatDialogRef<UserDetailsDialogComponent>, - private dialog: MatDialog, - private http: HttpClient, - private lookups: GlobalLookups, - private certificateService: CertificateService, - private userDetailsService: UserDetailsService, - private alertService: AlertMessageService, - private securityService: SecurityService, - private datePipe: DatePipe, - @Inject(MAT_DIALOG_DATA) public data: any, - private fb: UntypedFormBuilder) { - - this.userController = new UserController(this.http, this.lookups, this.dialog); - - this.mode = data.mode; - this.userId = data.row && data.row.userId; - this.editMode = this.mode !== UserDetailsDialogMode.NEW_MODE; - - this.current = !!data.row - ? { - ...data.row, - password: '', // ensures the user password is cleared before editing - confirmation: null, - certificate: data.row.certificate ? {...data.row.certificate} : this.newCertificateRo() - } : { - active: true, - username: '', - emailAddress: '', - password: '', - confirmation: null, - sequentialLoginFailureCount: null, - lastFailedLoginAttempt: null, - suspendedUtil: null, - sequentialTokenLoginFailureCount: null, - lastTokenFailedLoginAttempt: null, - tokenSuspendedUtil: null, - role: ApplicationRoleEnum.USER, - encodedValue: '', - crlUrl: '', - status: EntityStatus.NEW, - statusPassword: EntityStatus.NEW, - certificate: this.newCertificateRo(), - }; - - const bSetPassword: boolean = false; - - // calculate allowed roles - this.existingRoles = this.getAllowedRoles(this.current.role); - - // set empty form ! do not bind it to current object ! - this.userForm = fb.group({ - // common values - 'active': new UntypedFormControl({value: ''}, []), - 'emailAddress': new UntypedFormControl({value: ''}, [Validators.pattern(this.emailPattern), - Validators.maxLength(255)]), - 'role': new UntypedFormControl({ - value: '', - disabled: this.mode === UserDetailsDialogMode.PREFERENCES_MODE - }, Validators.required), - // username/password authentication - 'username': new UntypedFormControl({value: '', disabled: this.editMode}, - !this.editMode || !this.current.username - ? [Validators.nullValidator, Validators.pattern(this.usernamePattern), this.notInList(this.lookups.cachedServiceGroupOwnerList.map(a => a.username ? a.username.toLowerCase() : null))] - : null), - 'passwordExpireOn': new UntypedFormControl({value: '', disabled: true}), - 'sequentialLoginFailureCount': new UntypedFormControl({value: '', disabled: true}), - 'lastFailedLoginAttempt': new UntypedFormControl({value: '', disabled: true}), - 'suspendedUtil': new UntypedFormControl({value: '', disabled: true}), - - 'accessTokenId': new UntypedFormControl({value: '', disabled: true}), - 'accessTokenExpireOn': new UntypedFormControl({value: '', disabled: true}), - 'sequentialTokenLoginFailureCount': new UntypedFormControl({value: '', disabled: true}), - 'lastTokenFailedLoginAttempt': new UntypedFormControl({value: '', disabled: true}), - 'tokenSuspendedUtil': new UntypedFormControl({value: '', disabled: true}), - 'casUserDataUrl': new UntypedFormControl({value: '', disabled: true}), - - - 'confirmation': new UntypedFormControl({value: '', disabled: !bSetPassword}), - // certificate authentication - 'subject': new UntypedFormControl({value: '', disabled: true}, Validators.required), - 'validFrom': new UntypedFormControl({value: '', disabled: true}, Validators.required), - 'validTo': new UntypedFormControl({value: '', disabled: true}, Validators.required), - 'issuer': new UntypedFormControl({value: '', disabled: true}, Validators.required), - 'serialNumber': new UntypedFormControl({value: '', disabled: true}, Validators.required), - 'crlUrl': new UntypedFormControl({value: '', disabled: true}), - 'encodedValue': new UntypedFormControl({value: '', disabled: true}), - 'certificateId': new UntypedFormControl({value: '', disabled: true,}, [Validators.required]), - 'isCertificateValid': new UntypedFormControl({value: 'true', disabled: true,}, [Validators.requiredTrue] - ), - }, { - validator: [ - this.certificateValidator, - this.certificateExistValidator, - ] - }); - // bind values to form! not property - this.userForm.controls['active'].setValue(this.current.active); - this.userForm.controls['emailAddress'].setValue(this.current.emailAddress); - this.userForm.controls['role'].setValue(this.current.role); - // username/password authentication - this.userForm.controls['username'].setValue(this.current.username); - this.userForm.controls['passwordExpireOn'].setValue(this.current.passwordExpireOn); - this.userForm.controls['sequentialLoginFailureCount'].setValue(!this.current.sequentialLoginFailureCount ? 0 : this.current.sequentialLoginFailureCount); - this.userForm.controls['lastFailedLoginAttempt'].setValue(this.current.lastFailedLoginAttempt); - this.userForm.controls['suspendedUtil'].setValue(this.current.suspendedUtil); - - this.userForm.controls['accessTokenId'].setValue(this.current.accessTokenId); - this.userForm.controls['accessTokenExpireOn'].setValue(this.current.accessTokenExpireOn); - this.userForm.controls['sequentialTokenLoginFailureCount'].setValue(!this.current.sequentialTokenLoginFailureCount ? 0 : this.current.sequentialTokenLoginFailureCount); - this.userForm.controls['lastTokenFailedLoginAttempt'].setValue(this.current.lastTokenFailedLoginAttempt); - this.userForm.controls['tokenSuspendedUtil'].setValue(this.current.tokenSuspendedUtil); - - this.userForm.controls['casUserDataUrl'].setValue(this.current.casUserDataUrl); - - // certificate authentication - this.userForm.controls['subject'].setValue(this.current.certificate.subject); - this.userForm.controls['validFrom'].setValue(this.current.certificate.validFrom); - this.userForm.controls['validTo'].setValue(this.current.certificate.validTo); - this.userForm.controls['issuer'].setValue(this.current.certificate.issuer); - this.userForm.controls['serialNumber'].setValue(this.current.certificate.serialNumber); - this.userForm.controls['certificateId'].setValue(this.current.certificate.certificateId); - this.userForm.controls['crlUrl'].setValue(this.current.certificate.crlUrl); - this.userForm.controls['encodedValue'].setValue(this.current.certificate.encodedValue); - this.userForm.controls['isCertificateValid'].setValue(!this.current.certificate.invalid); - - - this.certificateValidationMessage = this.current.certificate.invalidReason; - this.isCertificateInvalid = this.current.certificate.invalid; - - } - - changeCurrentUserPassword() { - const formRef: MatDialogRef<any> = this.userController.changePasswordDialog({ - data: { - user: this.getCurrent(), - adminUser: this.securityService.isCurrentUserSystemAdmin() && - this.securityService.getCurrentUser().userId !== this.current.userId - }, - }); - formRef.afterClosed().subscribe(result => { - if (result) { - this.current.passwordExpireOn = result.passwordExpireOn; - this.userForm.controls['passwordExpireOn'].setValue(this.current.passwordExpireOn); - } - }); - - } - - onShowCertificateDataRow() { - const formRef: MatDialogRef<any> = this.dialog.open(CertificateDialogComponent, { - data: {row: this.getCurrent().certificate} - }); - formRef.afterClosed().subscribe(result => { - if (result) { - // import - } - }); - } - - clearCertificate() { - this.userForm.patchValue({ - 'subject': null, - 'validFrom': null, - 'validTo': null, - 'issuer': null, - 'serialNumber': null, - 'certificateId': null, - 'crlUrl': null, - 'encodedValue': null, - 'isCertificateValid': null, - }); - } - - openCurrentCasUserData() { - window.open(this.current.casUserDataUrl, "_blank"); - } - - regenerateAccessToken() { - const formRef: MatDialogRef<any> = this.userController.generateAccessTokenDialog({ - data: { - user: this.getCurrent(), - adminUser: this.securityService.isCurrentUserSystemAdmin() && - this.securityService.getCurrentUser().userId !== this.current.userId - }, - - }); - formRef.afterClosed().subscribe(result => { - if (result) { - let user = {...formRef.componentInstance.getCurrent()}; - // update value for current user - this.current.accessTokenId = user.accessTokenId - this.current.accessTokenExpireOn = user.accessTokenExpireOn - // set form data - this.userForm.controls['accessTokenId'].setValue(user.accessTokenId); - this.userForm.controls['accessTokenExpireOn'].setValue(user.accessTokenExpireOn); - - this.lookups.refreshUserLookup(); - } - }); - } - - - submitForm() { - this.dialogRef.close(true); - } - - uploadCertificate(event) { - this.newCertFile = null; - const file = event.target.files[0]; - this.certificateService.validateCertificate(file).subscribe((res: CertificateRo) => { - if (res && res.certificateId) { - this.userForm.patchValue({ - 'subject': res.subject, - 'validFrom': res.validFrom, - 'validTo': res.validTo, - 'issuer': res.issuer, - 'serialNumber': res.serialNumber, - 'certificateId': res.certificateId, - 'crlUrl': res.crlUrl, - 'encodedValue': res.encodedValue, - 'isCertificateValid': !res.invalid - }); - this.certificateValidationMessage = res.invalidReason; - this.isCertificateInvalid = res.invalid; - this.newCertFile = file; - } else { - this.alertService.exception("Error occurred while reading certificate.", "Check if uploaded file has valid certificate type.", false); - } - }, - err => { - this.alertService.exception('Error uploading certificate file ' + file.name, err.error?.errorDescription); - } - ); - } - - - isPreferencesMode() { - return this.mode === UserDetailsDialogMode.PREFERENCES_MODE; - } - - public getCurrent(): UserRo { - if (this.mode === UserDetailsDialogMode.NEW_MODE) { - this.current.username = this.userForm.get('username').value; - } - - this.current.active = this.userForm.get('active').value; - this.current.emailAddress = this.userForm.get('emailAddress').value; - this.current.role = this.userForm.get('role').value; - // certificate data - if (this.userForm.controls['certificateId'].value) { - this.current.certificate.certificateId = this.userForm.controls['certificateId'].value; - this.current.certificate.subject = this.userForm.controls['subject'].value; - this.current.certificate.issuer = this.userForm.controls['issuer'].value; - this.current.certificate.serialNumber = this.userForm.controls['serialNumber'].value; - this.current.certificate.validFrom = this.userForm.controls['validFrom'].value; - this.current.certificate.validTo = this.userForm.controls['validTo'].value; - this.current.certificate.crlUrl = this.userForm.controls['crlUrl'].value; - this.current.certificate.encodedValue = this.userForm.controls['encodedValue'].value; - this.current.certificate.invalid = this.isCertificateInvalid; - this.current.certificate.invalidReason = this.certificateValidationMessage; - } else { - this.current.certificate = null; - } - - - // update data - return this.current; - } - - // filters out roles so that the user cannot change from system administrator to the other roles or vice-versa - private getAllowedRoles(userRole) { - if (!this.editMode) { - return Object.keys(Role); - } else if (userRole === Role.SYSTEM_ADMIN) { - return [Role.SYSTEM_ADMIN]; - } else { - return Object.keys(Role).filter(role => role !== Role.SYSTEM_ADMIN); - } - } - - private newCertificateRo(): CertificateRo { - return { - subject: '', - validFrom: null, - validTo: null, - issuer: '', - serialNumber: '', - certificateId: '', - fingerprints: '', - crlUrl: '', - encodedValue: '', - } - } - - private newUserRo(): UserRo { - return { - id: null, - index: null, - username: '', - emailAddress: '', - role: ApplicationRoleEnum.USER, - active: true, - status: EntityStatus.NEW, - - } - } - - isUserAuthSSOEnabled(): boolean { - return this.lookups.cachedApplicationInfo?.authTypes?.includes('SSO'); - } - - isUserAuthPasswdEnabled(): boolean { - return this.lookups.cachedApplicationInfo?.authTypes?.includes('PASSWORD'); - } - - isWebServiceUserCertificateAuthEnabled(): boolean { - return this.lookups.cachedApplicationConfig?.webServiceAuthTypes?.includes('CERTIFICATE'); - } - - isWebServiceUserTokenAuthPasswdEnabled(): boolean { - return this.lookups.cachedApplicationConfig?.webServiceAuthTypes?.includes('TOKEN'); - } - -} - -export enum UserDetailsDialogMode { - NEW_MODE = 'New User', - EDIT_MODE = 'User Edit', - PREFERENCES_MODE = 'User details', -} diff --git a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details.service.ts b/smp-angular/src/app/system-settings/user/user-details.service.ts similarity index 83% rename from smp-angular/src/app/system-settings/user/user-details-dialog/user-details.service.ts rename to smp-angular/src/app/system-settings/user/user-details.service.ts index 2094bce7e79bfda9e4314897bc67d1a0e281421c..509dc1b027268da2fa24c0a3efbc1462312167ea 100644 --- a/smp-angular/src/app/system-settings/user/user-details-dialog/user-details.service.ts +++ b/smp-angular/src/app/system-settings/user/user-details.service.ts @@ -1,17 +1,15 @@ import {Injectable} from "@angular/core"; import {HttpClient} from "@angular/common/http"; -import {SmpConstants} from "../../../smp.constants"; +import {SmpConstants} from "../../smp.constants"; import {Observable} from "rxjs"; -import {AccessTokenRo} from "../../../common/dialogs/access-token-generation-dialog/access-token-ro.model"; -import {AlertMessageService} from "../../../common/alert-message/alert-message.service"; -import {UserRo} from "../user-ro.model"; +import {AccessTokenRo} from "../../common/model/access-token-ro.model"; +import {UserRo} from "./user-ro.model"; @Injectable() export class UserDetailsService { constructor( - private http: HttpClient, - private alertService: AlertMessageService, + private http: HttpClient ) { } diff --git a/smp-angular/src/app/system-settings/user/user-ro.model.ts b/smp-angular/src/app/system-settings/user/user-ro.model.ts index a09c44d486283c89eadb8290d28b2946fd7fae70..71b1bc722010a26f1b3fa78c29e43d0fe1c34345 100644 --- a/smp-angular/src/app/system-settings/user/user-ro.model.ts +++ b/smp-angular/src/app/system-settings/user/user-ro.model.ts @@ -21,7 +21,6 @@ export interface UserRo extends SearchTableEntity { lastFailedLoginAttempt?:Date; suspendedUtil?:Date; - // deprecated accessTokenId?: string; accessTokenExpireOn?: Date; diff --git a/smp-angular/src/app/system-settings/user/user.component.css b/smp-angular/src/app/system-settings/user/user.component.css deleted file mode 100644 index 907389ccaf2ce3c0fa147aa675bc1975dd0e57f7..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/system-settings/user/user.component.css +++ /dev/null @@ -1,40 +0,0 @@ -/* --- Select ---*/ -.mat-select{ - padding:20px 0; -} - -/* --- Button ---*/ -.group-btn { - margin-top:20px; - } - -#hiddenButtonId { - position: fixed; -} -::ng-deep .invalidCertificate { - text-decoration: line-through !important; - font-weight: bold; - color:red; -} - -::ng-deep .certificateWarning { - text-decoration: line-through !important; - font-weight: bold; - color:#c6c639; -} - -::ng-deep .deleted { - text-decoration: line-through !important; - font-weight: bold; -} -::ng-deep .table-row-new { - - color: darkgreen !important; - font-weight: bold; -} -::ng-deep .table-row-updated { - font-weight: bold; -} -::ng-deep .table-row { - font-weight: normal; -} diff --git a/smp-angular/src/app/system-settings/user/user.component.html b/smp-angular/src/app/system-settings/user/user.component.html deleted file mode 100644 index 2cb297518582386a170906fd76f6f0d870f02ad1..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/system-settings/user/user.component.html +++ /dev/null @@ -1,26 +0,0 @@ -<smp-search-table - #searchTable - page_id="user_id" - [title]="'Users'" - [columnPicker]="columnPicker" - [url]="baseUrl" - [additionalToolButtons]="additionalToolButtons" - [searchTableController]="userController" - [showSearchPanel]="false" - [filter]="filter" - [allowNewItems]="securityService.isCurrentUserSystemAdmin()" - [allowDeleteItems]="securityService.isCurrentUserSystemAdmin()" -> - - <ng-template #roleCellTemplate let-value="value" ngx-datatable-cell-template>{{getRoleLabel(value)}}</ng-template> - - <ng-template #certificateTemplate let-row="row" let-value="value" ngx-datatable-cell-template> - <span [class]='certCssClass(row)' [title]="getCertToolTip(value)">{{value?.certificateId}}</span> - </ng-template> - - <ng-template #additionalToolButtons > - - - </ng-template> - -</smp-search-table> diff --git a/smp-angular/src/app/system-settings/user/user.component.ts b/smp-angular/src/app/system-settings/user/user.component.ts deleted file mode 100644 index 85713469ca12c24f720040a57337306d57d5cbc3..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/system-settings/user/user.component.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { - AfterViewChecked, - AfterViewInit, - ChangeDetectorRef, - Component, - OnInit, - TemplateRef, - ViewChild -} from '@angular/core'; -import {ColumnPicker} from '../../common/column-picker/column-picker.model'; -import {MatDialog} from '@angular/material/dialog'; -import {AlertMessageService} from '../../common/alert-message/alert-message.service'; -import {UserController} from './user-controller'; -import {HttpClient} from '@angular/common/http'; -import {SearchTableComponent} from "../../common/search-table/search-table.component"; -import {SecurityService} from "../../security/security.service"; -import {GlobalLookups} from "../../common/global-lookups"; -import {EntityStatus} from "../../common/enums/entity-status.enum"; -import {SmpConstants} from "../../smp.constants"; - -@Component({ - templateUrl: './user.component.html', - styleUrls: ['./user.component.css'] -}) -export class UserComponent implements OnInit, AfterViewInit, AfterViewChecked { - - @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any>; - @ViewChild('rowExtensionAction') rowExtensionAction: TemplateRef<any>; - @ViewChild('rowActions') rowActions: TemplateRef<any>; - @ViewChild('searchTable') searchTable: SearchTableComponent; - @ViewChild('certificateTemplate') certificateTemplate: TemplateRef<any>; - - columnPicker: ColumnPicker = new ColumnPicker(); - userController: UserController; - filter: any = {}; - baseUrl: string = SmpConstants.REST_INTERNAL_USER_MANAGE; - - constructor(private lookups: GlobalLookups, - public securityService: SecurityService, - protected http: HttpClient, - protected alertService: AlertMessageService, - public dialog: MatDialog, - private changeDetector: ChangeDetectorRef) { - } - - ngOnInit() { - this.userController = new UserController(this.http, this.lookups, this.dialog); - } - - initColumns() { - this.columnPicker.allColumns = [ - { - name: 'Username', - prop: 'username', - showInitially: true, - canAutoResize: true - }, - { - name: 'Certificate', - prop: 'certificate', - showInitially: true, - cellTemplate: this.certificateTemplate, - canAutoResize: true - }, - { - name: 'Role', - prop: 'role', - showInitially: true, - canAutoResize: true - }, - ]; - this.searchTable.tableColumnInit(); - } - - ngAfterViewInit() { - this.initColumns(); - } - - ngAfterViewChecked() { - this.changeDetector.detectChanges(); - } - - certCssClass(row) { - - if (row.certificate?.invalid) { - return 'invalidCertificate'; - } else if (!row.certificate?.subject || !row.certificate?.issuer) { - return 'certificateWarning'; - } else if (row.status === EntityStatus.NEW) { - return 'table-row-new'; - } else if (row.status === EntityStatus.UPDATED) { - return 'table-row-updated'; - } else if (row.status === EntityStatus.REMOVED) { - return 'deleted'; - } else { - return 'table-row'; - } - } - - getCertToolTip(certificate) { - if (!certificate) { - return; - } - - if (certificate.invalid) { - return certificate.invalidReason; - } - - if (!certificate.subject || !certificate.issuer) { - return 'Legacy certificate definition. Please register certificate for the user!'; - } - - return ''; - } - - details(row: any) { - this.userController.showDetails(row); - } - - // for dirty guard... - isDirty(): boolean { - return this.searchTable.isDirty(); - } -} diff --git a/smp-angular/src/app/system-settings/user/user.service.ts b/smp-angular/src/app/system-settings/user/user.service.ts index e5716537364eb1a5146c9ae53b79cd7798b788fd..250be93f341f85a77375cc70a4eaae9fb07fa92e 100644 --- a/smp-angular/src/app/system-settings/user/user.service.ts +++ b/smp-angular/src/app/system-settings/user/user.service.ts @@ -6,7 +6,7 @@ import {AlertMessageService} from "../../common/alert-message/alert-message.serv import {SecurityService} from "../../security/security.service"; import {Observable, Subject} from "rxjs"; import {CredentialRo} from "../../security/credential.model"; -import {AccessTokenRo} from "../../common/dialogs/access-token-generation-dialog/access-token-ro.model"; +import {AccessTokenRo} from "../../common/model/access-token-ro.model"; import {HttpErrorHandlerService} from "../../common/error/http-error-handler.service"; /** diff --git a/smp-angular/src/app/user-settings/user-profile/user-profile.component.ts b/smp-angular/src/app/user-settings/user-profile/user-profile.component.ts index 34a607db701bcebcf7579e3ffeafb59cee4b5cdb..c2b6aee50703e2fbc3aa077f1a3ede7116701d48 100644 --- a/smp-angular/src/app/user-settings/user-profile/user-profile.component.ts +++ b/smp-angular/src/app/user-settings/user-profile/user-profile.component.ts @@ -9,9 +9,9 @@ import {MatDialog, MatDialogConfig, MatDialogRef} from "@angular/material/dialog import { PasswordChangeDialogComponent } from "../../common/dialogs/password-change-dialog/password-change-dialog.component"; -import {UserDetailsDialogMode} from "../../system-settings/user/user-details-dialog/user-details-dialog.component"; import {SecurityEventService} from "../../security/security-event.service"; import {Subscription} from "rxjs"; +import {EntityStatus} from "../../common/enums/entity-status.enum"; @Component({ @@ -101,7 +101,7 @@ export class UserProfileComponent implements OnInit, OnDestroy, BeforeLeaveGuard ...config, data: { ...config.data, - mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE) + mode: config.data.mode || (config.data.edit ? EntityStatus.PERSISTED : EntityStatus.NEW) } } : config; diff --git a/smp-angular/src/app/window/footer/footer.component.css b/smp-angular/src/app/window/footer/footer.component.css index 40591c7c869b6d4e9bada29eb408a7bdce85d4dd..a4b5b9c7df0f98ffdf6b65778509b1d29f9eb39c 100644 --- a/smp-angular/src/app/window/footer/footer.component.css +++ b/smp-angular/src/app/window/footer/footer.component.css @@ -1,9 +1,9 @@ #footer { - position:absolute; - bottom:0; + position: absolute; + bottom: 0; left: 0; - width:100%; - overflow:hidden; + width: 100%; + overflow: hidden; text-align: center; } diff --git a/smp-angular/src/app/window/footer/footer.component.ts b/smp-angular/src/app/window/footer/footer.component.ts index 084d6e6066f4febd47170b0da8d16b4edef604c4..9012b5597559bb9b2084c61d219926209a3d40e5 100644 --- a/smp-angular/src/app/window/footer/footer.component.ts +++ b/smp-angular/src/app/window/footer/footer.component.ts @@ -19,7 +19,7 @@ export class FooterComponent implements OnInit { console.log("FooterComponent onInit"); this.smpInfoService.getSmpInfo().subscribe((smpInfo: SmpInfo) => { - this.smpVersion = smpInfo.version; + this.smpVersion = smpInfo.version; } ); } diff --git a/smp-angular/src/app/window/sidenav/nav-tree-menu/nav-tree-menu.component.ts b/smp-angular/src/app/window/sidenav/nav-tree-menu/nav-tree-menu.component.ts index 4968dabd964938bab170f485ed49c7320e68130c..5e3dd1cc8daeb8bd19e04fb17b6d214046c12548 100644 --- a/smp-angular/src/app/window/sidenav/nav-tree-menu/nav-tree-menu.component.ts +++ b/smp-angular/src/app/window/sidenav/nav-tree-menu/nav-tree-menu.component.ts @@ -17,8 +17,8 @@ export class NavTreeMenu { this.notifyClickMenu.emit(this.data); } - get isLeaf(){ - return !this.data.children || this.data.children.length ==0 + get isLeaf() { + return !this.data.children || this.data.children.length == 0 } } diff --git a/smp-angular/src/app/window/sidenav/nav-tree/_nav-tree.component-theme.scss b/smp-angular/src/app/window/sidenav/nav-tree/_nav-tree.component-theme.scss index 2a5ba0736d13b25bf62033706cbb91ba8b952339..718d560aecd7e812afa7445124dce0e5deed991e 100644 --- a/smp-angular/src/app/window/sidenav/nav-tree/_nav-tree.component-theme.scss +++ b/smp-angular/src/app/window/sidenav/nav-tree/_nav-tree.component-theme.scss @@ -4,12 +4,12 @@ .navigation-selected { border-color: smp.get-theme-color($theme, primary, 900) !important; background-color: smp.get-theme-color($theme, primary, 600) !important; - color: smp.get-theme-color($theme, primary, 800-contrast) !important; + color: smp.get-theme-color($theme, primary, 800 -contrast) !important; } .nav-tree-leaf { border-color: smp.get-theme-color($theme, primary, 0.5); background-color: smp.get-theme-color($theme, primary, 0.2); - color: smp.get-theme-color($theme, primary, 500-contrast); + color: smp.get-theme-color($theme, primary, 500 -contrast); } } diff --git a/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.html b/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.html index 1bec6450b2222daaddda4fcb8c5b08c13c2d9fc3..b7239becea5365023d60e8aa185ca52bd0425a34 100644 --- a/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.html +++ b/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.html @@ -4,7 +4,7 @@ <!-- Leaf nodes --> <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle> <li class="mat-tree-node nav-tree-leaf" [ngClass]="{ 'navigation-selected': navigationModel.selected === node}"> - <button [id]="node.code+'Button'" mat-menu-item (click)="menuClickHandler(node)" > + <button [id]="node.code+'Button'" mat-menu-item (click)="menuClickHandler(node)"> <mat-icon *ngIf="node.icon">{{node.icon}}</mat-icon> <span *ngIf="fullMenu">{{node.name}}</span> </button> @@ -21,18 +21,19 @@ {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}} </mat-icon> </a> - <button [id]="node.code+'Button'" mat-menu-item (click)="menuClickHandler(node)" > + <button [id]="node.code+'Button'" mat-menu-item (click)="menuClickHandler(node)"> <mat-icon *ngIf="node.icon">{{node.icon}}</mat-icon> <span *ngIf="fullMenu">{{node.name}}</span> </button> </ng-container> <ng-template #iconButton> - <button [id]="node.code+'Button'" #iconItem mat-menu-item [matMenuTriggerFor]="itemMenu" > + <button [id]="node.code+'Button'" #iconItem mat-menu-item [matMenuTriggerFor]="itemMenu"> <mat-icon *ngIf="node.icon">{{node.icon}}</mat-icon> </button> <mat-menu #itemMenu="matMenu" xPosition="before" yPosition="below"> <ng-container *ngFor="let item of node.children; let i = index"> - <nav-tree-menu (notifyClickMenu)="menuClickHandler(item)" [trigger]="item.code" [data]="item"></nav-tree-menu> + <nav-tree-menu (notifyClickMenu)="menuClickHandler(item)" [trigger]="item.code" + [data]="item"></nav-tree-menu> </ng-container> </mat-menu> </ng-template> diff --git a/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.scss b/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.scss index b418fd03583c1b665d229b75f01383aed8085a7d..0190f13f289677e9febc5beb7aea3e3960631661 100644 --- a/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.scss +++ b/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.scss @@ -22,6 +22,7 @@ .nav-tree li { border-bottom: #EEE 1px solid; } + .nav-tree li button { min-height: 2.5em !important; height: 2.5em; @@ -43,6 +44,7 @@ padding: 0 !important; margin: 0 !important; } + .nav-tree-expand-button-icon { /*fixed size expand icon */ font-size: 0.8em; diff --git a/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.ts b/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.ts index 985a5f1b6a2b197099c8dc7a9295c21ec2f9c3e3..f3fcc18759d73b65e9b72c6e4e23f7cd2b514cc6 100644 --- a/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.ts +++ b/smp-angular/src/app/window/sidenav/nav-tree/nav-tree.component.ts @@ -1,5 +1,5 @@ import {Component} from "@angular/core"; -import {NavigationService, NavigationNode} from "../navigation-model.service"; +import {NavigationNode, NavigationService} from "../navigation-model.service"; import {NestedTreeControl} from "@angular/cdk/tree"; @@ -18,7 +18,7 @@ export class NavTree { constructor(public navigationModel: NavigationService) { navigationModel.getSelectedPathObservable() - .subscribe( selectedPath => { + .subscribe(selectedPath => { if (!selectedPath || selectedPath.length == 0) { return; } @@ -42,6 +42,7 @@ export class NavTree { this.navigationModel.select(node); } + isExpanded(node: NavigationNode) { this.treeControl.isExpanded(node) } diff --git a/smp-angular/src/app/window/sidenav/navigation-model.service.ts b/smp-angular/src/app/window/sidenav/navigation-model.service.ts index 85586860e92f7cb07fe7c4bc8b142868b59fa635..fc96ae3a53a21e6f0ba74d28113de9ec89f7ae48 100644 --- a/smp-angular/src/app/window/sidenav/navigation-model.service.ts +++ b/smp-angular/src/app/window/sidenav/navigation-model.service.ts @@ -66,6 +66,8 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> { selectedPath: NavigationNode[]; private rootNode: NavigationNode = PUBLIC_NAVIGATION_TREE; + private userDetailsNode: NavigationNode = null; + constructor(protected securityService: SecurityService, protected securityEventService: SecurityEventService, @@ -188,9 +190,8 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> { if (!parentNode.children) { return null; } - console.log("find " + nodeCode + "from parent2: " + parentNode.code) + console.log("find " + nodeCode + " from parent: " + parentNode.code) return parentNode.children.find(node => node.routerLink == nodeCode); - } /** @@ -221,6 +222,12 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> { setNavigationTree(userRootNode: NavigationNode) { // find the node by the navigation let path: string[] = this.router.url.split('/'); + this.setNavigationTreeByPath(path, userRootNode) + } + + setNavigationTreeByPath(path: string[], userRootNode: NavigationNode) { + // find the node by the navigation + let startNode = userRootNode; for (let index in path) { @@ -326,4 +333,8 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> { public navigateToHome(): void { this.select(this.rootNode); } + + public navigateToUserDetails(): void { + this.setNavigationTreeByPath(['user-settings', 'user-profile'], this.rootNode) + } } diff --git a/smp-angular/src/app/window/sidenav/navigation-on-leave-guard.ts b/smp-angular/src/app/window/sidenav/navigation-on-leave-guard.ts index 7a33f253841ffe01fa0cd12fa4e5a4aed01f291d..30cf0927345a515ab8822321f91d5ecb73f48c2f 100644 --- a/smp-angular/src/app/window/sidenav/navigation-on-leave-guard.ts +++ b/smp-angular/src/app/window/sidenav/navigation-on-leave-guard.ts @@ -1,7 +1,6 @@ - export declare interface BeforeLeaveGuard { - /** - * return true of component has unsaved data. - */ - isDirty(): boolean; + /** + * return true of component has unsaved data. + */ + isDirty(): boolean; } diff --git a/smp-angular/src/app/window/sidenav/sidenav.component.html b/smp-angular/src/app/window/sidenav/sidenav.component.html index 9a2524bd8e8bcbcae39a4620e05b67a4c47a275c..f10deb6b446f8f3b9e1f658c51c1708a7cbcd96f 100644 --- a/smp-angular/src/app/window/sidenav/sidenav.component.html +++ b/smp-angular/src/app/window/sidenav/sidenav.component.html @@ -1,5 +1,5 @@ -<div id="window-sidenav-panel" style="display:flex; +<div id="window-sidenav-panel" style="display:flex; flex-direction: column;" - [style.width]="fullMenu ? expandedSideNavSize : collapsedSideNavSize"> - <nav-tree #navtree ></nav-tree> + [style.width]="fullMenu ? expandedSideNavSize : collapsedSideNavSize"> + <nav-tree #navtree></nav-tree> </div> diff --git a/smp-angular/src/app/window/sidenav/sidenav.component.ts b/smp-angular/src/app/window/sidenav/sidenav.component.ts index 06af7c8a4685e5aa286a2943626ca0ea70e91e85..d27afbfe8d7ad729f2db932e8a5f73ac18b442d6 100644 --- a/smp-angular/src/app/window/sidenav/sidenav.component.ts +++ b/smp-angular/src/app/window/sidenav/sidenav.component.ts @@ -57,11 +57,11 @@ export class SidenavComponent implements OnInit { this.securityService.logout(); } - get expandedSideNavSize(){ + get expandedSideNavSize() { return SmpConstants.EXPANDED_MENU_WIDTH; } - get collapsedSideNavSize(){ + get collapsedSideNavSize() { return SmpConstants.COLLAPSED_MENU_WIDTH; } } diff --git a/smp-angular/src/app/window/toolbar/toolbar.component.html b/smp-angular/src/app/window/toolbar/toolbar.component.html index a7f4b3f0e153613de314fa6d6e1f093377199fd6..d8db49ff050aae8be0f42bf20c362a39931cb3d5 100644 --- a/smp-angular/src/app/window/toolbar/toolbar.component.html +++ b/smp-angular/src/app/window/toolbar/toolbar.component.html @@ -1,29 +1,30 @@ -<mat-toolbar color="primary" id="window-toolbar" > - <mat-toolbar-row> - <smp-breadcrumb [style.padding-left]="fullMenu ? '180px' : '60px'"></smp-breadcrumb> - <div id="topLogo" class="mat-mdc-card"> - <img src="assets/images/DomiSMP_logo.svg" [attr.height]="fullMenu ? '140px' : '30px'" - [attr.width]="fullMenu ? '180px' : '50px'"/> - </div> +<mat-toolbar color="primary" id="window-toolbar"> + <mat-toolbar-row> + <smp-breadcrumb [style.padding-left]="fullMenu ? '180px' : '60px'"></smp-breadcrumb> + <div id="topLogo" class="mat-mdc-card"> + <img src="assets/images/DomiSMP_logo.svg" [attr.height]="fullMenu ? '140px' : '30px'" + [attr.width]="fullMenu ? '180px' : '50px'"/> + </div> <span class="window-toolbar-spacer"></span> - <a id="login_id" class="window-toolbar-item" *ngIf="!currentUser" [routerLink]="['/login']" (click)="clearWarning()"> Login </a> + <a id="login_id" class="window-toolbar-item" *ngIf="!currentUser" [routerLink]="['/login']" + (click)="clearWarning()"> Login </a> <span class="window-toolbar-item" *ngIf="currentUser">{{currentUserRoleDescription}}: {{currentUser}} </span> <button class="window-toolbar-item" [mat-menu-trigger-for]="settingsMenu" id="settingsmenu_id" - matTooltip="Menu" > + matTooltip="Menu"> <mat-icon>menu</mat-icon> </button> <mat-menu x-position="before" #settingsMenu="matMenu"> <div *ngIf="currentUser"> - <button id="currentuser_id" mat-menu-item (click)="editCurrentUser()"> + <button id="currentuser_id" mat-menu-item (click)="editCurrentUser()"> <mat-icon>person</mat-icon> <span>{{currentUser}}</span> </button> - <button id="changePassword_id" *ngIf="isUserAuthPasswdEnabled" mat-menu-item + <button id="changePassword_id" *ngIf="isUserAuthPasswdEnabled" mat-menu-item (click)="changeCurrentUserPassword()"> <span>Change password</span> </button> - <button id="showSSODetails_id" *ngIf="isUserAuthSSOEnabled" mat-menu-item + <button id="showSSODetails_id" *ngIf="isUserAuthSSOEnabled" mat-menu-item (click)="openCurrentCasUserData()"> <span>Open CAS user data</span> </button> @@ -35,7 +36,7 @@ </div> <div *ngIf="!currentUser" style="text-align: center; vertical-align: middle;margin: 2px;"> - <button id="notLoggedInButton" mat-menu-item disabled="true"> + <button id="notLoggedInButton" mat-menu-item disabled="true"> <mat-icon>person_outline</mat-icon> <span>Not logged in</span> </button> diff --git a/smp-angular/src/app/window/toolbar/toolbar.component.ts b/smp-angular/src/app/window/toolbar/toolbar.component.ts index 9a7b4225b8ed460afc9afa808818841f13490343..cc0bc9a2262734dd1d025e92949b27f052ce9199 100644 --- a/smp-angular/src/app/window/toolbar/toolbar.component.ts +++ b/smp-angular/src/app/window/toolbar/toolbar.component.ts @@ -1,15 +1,14 @@ -import {Component, OnInit} from '@angular/core'; +import {Component} from '@angular/core'; import {SecurityService} from '../../security/security.service'; import {Authority} from "../../security/authority.model"; import {AlertMessageService} from "../../common/alert-message/alert-message.service"; -import {MatDialog, MatDialogRef} from "@angular/material/dialog"; -import {UserDetailsDialogMode} from "../../system-settings/user/user-details-dialog/user-details-dialog.component"; -import {EntityStatus} from "../../common/enums/entity-status.enum"; +import {MatDialog} from "@angular/material/dialog"; import {UserService} from "../../system-settings/user/user.service"; import {UserController} from "../../system-settings/user/user-controller"; import {HttpClient} from "@angular/common/http"; import {GlobalLookups} from "../../common/global-lookups"; +import {NavigationService} from "../sidenav/navigation-model.service"; /** * Expanded side navigation panel of the DomiSMP. The component shows all tools/pages according to user role and permissions @@ -33,6 +32,7 @@ export class ToolbarComponent { constructor(private alertService: AlertMessageService, private securityService: SecurityService, private userService: UserService, + private navigation: NavigationService, private http: HttpClient, private dialog: MatDialog, private lookups: GlobalLookups) { @@ -64,27 +64,19 @@ export class ToolbarComponent { get currentUser(): string { let userDesc = this.userTitle; - return (userDesc.length>25)?userDesc.slice(0,25) + "...":userDesc + return (userDesc.length > 25) ? userDesc.slice(0, 25) + "..." : userDesc } - get userTitle(){ + get userTitle() { let user = this.securityService.getCurrentUser(); if (!user) { return "" } - return !!user.fullName? user.fullName +" ["+user.username+"]":user.username; + return !!user.fullName ? user.fullName + " [" + user.username + "]" : user.username; } editCurrentUser() { - const formRef: MatDialogRef<any> = this.userController.newDialog({ - data: {mode: UserDetailsDialogMode.PREFERENCES_MODE, row: this.securityService.getCurrentUser()} - }); - formRef.afterClosed().subscribe(result => { - if (result) { - const user = {...formRef.componentInstance.getCurrent(), status: EntityStatus.UPDATED}; - this.userService.updateUser(user); - } - }); + this.navigation.navigateToUserDetails(); } get currentUserRoleDescription(): string { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java index 87bc116bec139ca95c9f55f9a16f3777d59ea74f..3f5a99c8ca2f8a95baa081927dd29d2f23a64c7a 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java @@ -259,7 +259,10 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { dbCredential.setExpireOn(adminUpdate ? null : currentTime.plusDays(configurationService.getPasswordPolicyValidDays())); - // if the credentials are not managed by the session , e.g. new - the parsist it + // clear failed attempts + dbCredential.setLastFailedLoginAttempt(null); + dbCredential.setSequentialLoginFailureCount(null); + // if the credentials are not managed by the session , e.g. new - the persist it if (dbCredential.getId()==null) { credentialDao.persist(dbCredential); } @@ -318,7 +321,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { LOG.error("Can not update user because user for id [{}] does not exist!", userId); throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "UserId", "Can not find user id!"); } - LOG.debug("Update user [{}]: email [{}], fullname [{}], smp theme [{}]", user.getUsername(), user.getEmailAddress(), user.getFullName(), user.getSmpTheme()); + LOG.debug("Update user [{}]: email [{}], fullName [{}], smp theme [{}]", user.getUsername(), user.getEmailAddress(), user.getFullName(), user.getSmpTheme()); // update user profile data on managed db entity. (For now Just email, name and theme) dbUser.setEmailAddress(user.getEmailAddress()); dbUser.setFullName(user.getFullName()); @@ -333,7 +336,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { LOG.error("Can not update user because user for id [{}] does not exist!", userId); throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "UserId", "Can not find user id!"); } - LOG.debug("Update user [{}]: email [{}], fullname [{}], smp theme [{}]", user.getUsername(), user.getEmailAddress(), user.getFullName(), user.getSmpTheme()); + LOG.debug("Update user [{}]: email [{}], fullName [{}], smp theme [{}]", user.getUsername(), user.getEmailAddress(), user.getFullName(), user.getSmpTheme()); // update user data by admin dbUser.setActive(user.isActive()); dbUser.setApplicationRole(user.getRole());