Skip to content

Commit

Permalink
user: password validator
Browse files Browse the repository at this point in the history
The password is mandatory if the user is not loaded,
otherwise it is optional

* Adds validator on new password on change password dialog.
* Fixes the identifier of the delete button so that it is unique.

Co-Authored-by: Bertrand Zuchuat <bertrand.zuchuat@rero.ch>
  • Loading branch information
Garfield-fr committed Feb 6, 2023
1 parent 9612060 commit aee1e36
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 9 deletions.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"easymde": "^2.18.0",
"font-awesome": "^4.7.0",
"issn": "^1.0.6",
"js-generate-password": "^0.1.7",
"lodash-es": "^4.17.21",
"luxon": "^3.1.1",
"marked": "^4.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ <h4 id="dialog-sizes-name2" class="modal-title pull-left" translate>Personal inf
(search)="searchValueUpdated($event)" class="form-inline mr-3" [focus]="false" [displayLabel]="false">
</ng-core-search-input>
</form>
<button type="button" id="editor-delete-button" class="btn btn-outline-danger btn-sm mr-1"
<button type="button" id="user-editor-delete-button" class="btn btn-outline-danger btn-sm mr-1"
(click)="bsModalRef.hide()">
<i class="fa fa-times"></i>
{{ 'Cancel' | translate }}
</button>
<button type="submit" id="editor-save-button" class="btn btn-primary btn-sm">
<button type="submit" id="user-editor-save-button" class="btn btn-primary btn-sm">
<i class="fa fa-save"></i>
{{ 'Save' | translate }}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { FormlyJsonschema } from '@ngx-formly/core/json-schema';
import { TranslateService } from '@ngx-translate/core';
Expand Down Expand Up @@ -54,6 +54,9 @@ export class UserIdEditorComponent implements OnInit {
/** Formly fields configuration populate by the JSONSchema */
fields: FormlyFieldConfig[];

/** Password field */
passwordField: FormlyFieldConfig;

/**
* Constructor
*
Expand Down Expand Up @@ -106,6 +109,12 @@ export class UserIdEditorComponent implements OnInit {
}
field.asyncValidators.uniqueUsername = this.getUniqueValidator('username');
}
if (field.key === 'password') {
if (!this.userID) {
field.templateOptions.required = true;
}
this.passwordField = field;
}
// remove Message suffix to the message validation key
// (required for backend translations)
if (field.validation) {
Expand Down Expand Up @@ -142,6 +151,7 @@ export class UserIdEditorComponent implements OnInit {
searchValueUpdated(query: (string | null)): void {
if (!query) {
this.loadedUserID = null;
this.passwordField.templateOptions.required = true;
this.form.reset();
this.model = {};
return;
Expand Down Expand Up @@ -179,6 +189,7 @@ export class UserIdEditorComponent implements OnInit {
this._translateService.instant('The personal data has been successfully linked to this patron.')
);
this.loadedUserID = model.id;
this.passwordField.templateOptions.required = false;
this.form.reset();
return this.model = model.metadata ? model.metadata : null;
}),
Expand Down
13 changes: 12 additions & 1 deletion projects/public-search/src/app/api/user-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ApiService } from '@rero/ng-core';
import { Observable } from 'rxjs';

@Injectable({
Expand All @@ -27,7 +28,10 @@ export class UserApiService {
* Constructor
* @param _httpClient - HttpClient
*/
constructor(private _httpClient: HttpClient) { }
constructor(
private _httpClient: HttpClient,
private _apiService: ApiService
) { }

/**
* Update password
Expand All @@ -37,6 +41,13 @@ export class UserApiService {
updatePassword(data: IPassword): Observable<any> {
return this._httpClient.post('/api/change-password', data);
}

validatePassword(password: string): Observable<any> {
return this._httpClient.post(
this._apiService.getEndpointByType('user/password/validate'),
{ 'password': password }
);
}
}

interface IPassword {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* RERO ILS UI
* Copyright (C) 2022 RERO
* Copyright (C) 2022-2023 RERO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
Expand All @@ -16,14 +16,14 @@
*/
import { DOCUMENT } from '@angular/common';
import { Component, ElementRef, Inject, Input } from '@angular/core';
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { TranslateService } from '@ngx-translate/core';
import { AppSettingsService } from '@rero/shared';
import { ToastrService } from 'ngx-toastr';
import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { catchError, debounceTime, map } from 'rxjs/operators';
import { UserApiService } from '../../api/user-api.service';

export function fieldPasswordMatchValidator(control: AbstractControl) {
Expand Down Expand Up @@ -81,9 +81,12 @@ export class PatronProfilePasswordComponent {
type: 'password',
label: 'New password',
required: true,
minLength: 6,
minLength: 8,
maxLength: 128
},
asyncValidators: {
'validatePassword': this.validatePassword()
}
},
{
key: 'confirmPassword',
Expand All @@ -92,7 +95,7 @@ export class PatronProfilePasswordComponent {
type: 'password',
label: 'Confirm new password',
required: true,
minLength: 6,
minLength: 8,
maxLength: 128,
}
}
Expand All @@ -106,6 +109,9 @@ export class PatronProfilePasswordComponent {
new_password_confirm: 'confirmPassword'
};

/** Error message for password validator */
private _validatePasswordMessage: string = '';

/**
* Constructor
*
Expand Down Expand Up @@ -159,6 +165,27 @@ export class PatronProfilePasswordComponent {
});
}

/** Async validator for the password validator */
validatePassword(): any {
return {
expression: (control: UntypedFormControl) => {
const value = control.value;
if (value == null || value.length === 0) {
return of(true);
}
return this._userApiService.validatePassword(value).pipe(
debounceTime(500),
map(() => of(true)),
catchError((response) => {
this._validatePasswordMessage = response.error.message;
return of(false);
})
);
},
message: () => this._translateService.instant(this._validatePasswordMessage)
};
}

/** Cancel action on form */
cancel(): void {
this._redirect();
Expand Down

0 comments on commit aee1e36

Please sign in to comment.