Skip to content

Commit

Permalink
Merge pull request #3747 from cisagov/feature/change-password
Browse files Browse the repository at this point in the history
Internationalized Change Password dialog
  • Loading branch information
jekuipers authored Feb 15, 2024
2 parents a5772f9 + a8051e5 commit 3a708b9
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public IActionResult PostChangePassword([FromBody] ChangePassword changePass)
new PasswordResponse()
{
IsValid = false,
Message = "Current password is invalid.<br>Correct it or request a new temporary password."
Message = "current invalid"
});
}

Expand All @@ -153,7 +153,7 @@ public IActionResult PostChangePassword([FromBody] ChangePassword changePass)
!respComplex.PasswordContainsSpecial || !respComplex.PasswordNotReused)
{
respComplex.IsValid = false;
respComplex.Message = "New password does not satisfy all password policy requirements.";
respComplex.Message = "rules not satisfied";
return Ok(respComplex);
}

Expand Down
4 changes: 2 additions & 2 deletions CSETWebNg/src/app/dialogs/alert/alert.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------->
<div class="alert-dialog d-flex flex-column justify-content-center flex-11a">
<div class="alert-dialog d-flex flex-column justify-content-center flex-11a" *transloco="let t">
<div class="mat-dialog-header p-3 d-flex justify-content-start align-items-center flex-00a">
<span [ngClass]="iconClass" class="fs-base-6 mr-3"></span>
<span>{{ data.title | titlecase }}</span>
Expand All @@ -34,7 +34,7 @@
</div>

<mat-dialog-actions class="p-3 mb-0 d-flex justify-content-start flex-00a">
<button class="btn btn-secondary" (click)="close()">OK</button>
<button class="btn btn-secondary" (click)="close()">{{t('buttons.ok')}}</button>
</mat-dialog-actions>

</div>
Original file line number Diff line number Diff line change
Expand Up @@ -20,74 +20,73 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------->
<div class="d-flex flex-column justify-content-center flex-11a">
<div class="d-flex flex-column justify-content-center flex-11a" *transloco="let t">
<div class="mat-dialog-header p-3 d-flex justify-content-start align-items-center flex-00a">
<span class="mr-3 fs-base-6 cset-icons-key"></span>
<div>Change Password</div>
<div>{{ t('change password.dialog title') }}</div>
</div>
<mat-dialog-content class="p-3 pr-0 oy-auto d-flex flex-column flex-11a">
<div class="alert alert-default">
<ul class="list-group list-group-flush">
<li class="list-group-item"><span *ngIf="passwordResponse.passwordLengthMet"><i
class='fa-solid fa-square-check'></i></span><span *ngIf="!passwordResponse.passwordLengthMet"><i
class='fa-solid fa-square-xmark'></i></span> &nbsp;Length between {{passwordResponse.passwordLengthMin}}
and {{passwordResponse.passwordLengthMax}} characters</li>
class='fa-solid fa-square-check mr-2'></i></span><span *ngIf="!passwordResponse.passwordLengthMet"><i
class='fa-solid fa-square-xmark mr-2'></i></span>{{ t('change password.rules.length between', {lengthMin:
passwordResponse.passwordLengthMin, lengthMax: passwordResponse.passwordLengthMax}) }}</li>
<li class="list-group-item"><span *ngIf="passwordResponse.passwordContainsNumbers"><i
class='fa-solid fa-square-check'></i></span><span *ngIf="!passwordResponse.passwordContainsNumbers"><i
class='fa-solid fa-square-xmark'></i></span> &nbsp;Must contain at least 2 numbers</li>
class='fa-solid fa-square-check mr-2'></i></span><span *ngIf="!passwordResponse.passwordContainsNumbers"><i
class='fa-solid fa-square-xmark mr-2'></i></span>{{ t('change password.rules.required numbers') }}</li>
<li class="list-group-item"><span *ngIf="passwordResponse.passwordContainsLower"><i
class='fa-solid fa-square-check'></i></span><span *ngIf="!passwordResponse.passwordContainsLower"><i
class='fa-solid fa-square-xmark'></i></span> &nbsp;Must contain at least 1 lowercase letter</li>
class='fa-solid fa-square-check mr-2'></i></span><span *ngIf="!passwordResponse.passwordContainsLower"><i
class='fa-solid fa-square-xmark mr-2'></i></span>{{ t('change password.rules.required lowercase') }}</li>
<li class="list-group-item"><span *ngIf="passwordResponse.passwordContainsUpper"><i
class='fa-solid fa-square-check'></i></span><span *ngIf="!passwordResponse.passwordContainsUpper"><i
class='fa-solid fa-square-xmark'></i></span> &nbsp;Must contain at least 1 uppercase letter</li>
class='fa-solid fa-square-check mr-2'></i></span><span *ngIf="!passwordResponse.passwordContainsUpper"><i
class='fa-solid fa-square-xmark mr-2'></i></span>{{ t('change password.rules.required uppercase') }}</li>
<li class="list-group-item"><span *ngIf="passwordResponse.passwordContainsSpecial"><i
class='fa-solid fa-square-check'></i></span><span *ngIf="!passwordResponse.passwordContainsSpecial"><i
class='fa-solid fa-square-xmark'></i></span> &nbsp;Must contain at least 1 special character</li>
class='fa-solid fa-square-check mr-2'></i></span><span *ngIf="!passwordResponse.passwordContainsSpecial"><i
class='fa-solid fa-square-xmark mr-2'></i></span>{{ t('change password.rules.required special') }}</li>
<li class="list-group-item"><span *ngIf="passwordResponse.passwordNotReused"><i
class='fa-solid fa-square-check'></i></span><span *ngIf="!passwordResponse.passwordNotReused"><i
class='fa-solid fa-square-xmark'></i></span> &nbsp;Must not re-use previous
{{passwordResponse.numberOfHistoricalPasswords}} passwords</li>
class='fa-solid fa-square-check mr-2'></i></span><span *ngIf="!passwordResponse.passwordNotReused"><i
class='fa-solid fa-square-xmark mr-2'></i></span>{{ t('change password.rules.previous count', {count:
passwordResponse.numberOfHistoricalPasswords}) }}</li>
</ul>
</div>
<div class="alert alert-danger" *ngIf="message?.length > 0" [innerHTML]="message"></div>

<form #fReg="ngForm" (ngSubmit)="onPasswordChangeClick(fReg)" class="pr-3">
<div class="form-group">
<div class="d-flex justify-content-between">
<label required for="currentPassword">Current Password</label>
<label required for="currentPassword">{{ t('change password.current password') }}</label>
<div *ngIf="!showPassword" class="show-hide-password" (click)="togglePasswordVisibility()">
Show Passwords<i class="fas fa-eye ml-1"></i>
{{ t('change password.show passwords') }}<i class="fas fa-eye ml-1"></i>
</div>
<div *ngIf="showPassword" class="show-hide-password" (click)="togglePasswordVisibility()">
Hide Passwords<i class="fas fa-eye-slash ml-1"></i>
{{ t('change password.hide passwords') }}<i class="fas fa-eye-slash ml-1"></i>
</div>
</div>
<input [type]="showPassword ? 'text' : 'password'" id="currentPassword" name="currentPassword"
class="form-control" required [(ngModel)]="cpwd.currentPassword">
</div>
<div class="form-group">
<label required for="password">New Password</label>
<label required for="password">{{ t('change password.new password') }}</label>
<input [type]="showPassword ? 'text' : 'password'" name="password" id="password" class="form-control"
(ngModelChange)="checkPassword($event)" [(ngModel)]="cpwd.newPassword" required>
</div>
<div class="form-group">
<label required for="confirmPassword">Confirm New Password</label>
<label required for="confirmPassword">{{ t('change password.confirm new password') }}</label>
<input [type]="showPassword ? 'text' : 'password'" class="form-control" id="confirmPassword"
name="confirmPassword" [(ngModel)]="cpwd.confirmPassword" appConfirmEqualValidator="password" required>
</div>

<mat-dialog-actions class="pt-0 mb-0 d-flex flex-column flex-00a">
<div class="alert alert-danger"
*ngIf="fReg.submitted && fReg.controls.confirmPassword.touched && fReg.controls.confirmPassword.errors?.notEqual">
Passwords do not match.</div>
<div class="alert alert-danger"
*ngIf="fReg.submitted && (fReg.controls.confirmPassword.errors?.required && fReg.controls.confirmPassword.errors?.required)">
New Password and Confirmation are required.</div>
<div class="d-flex justify-content-between flex-11a">
<button class="btn btn-primary m-0 mr-1">Change Password</button>
<button class="btn btn-secondary mr-1" (click)="cancel()">Cancel</button>
</div>
<div class="alert alert-danger"
*ngIf="fReg.submitted && fReg.controls.confirmPassword.touched && fReg.controls.confirmPassword.errors?.notEqual">
{{ t('change password.passwords do not match') }}</div>
<div class="alert alert-danger"
*ngIf="fReg.submitted && (fReg.controls.confirmPassword.errors?.required && fReg.controls.confirmPassword.errors?.required)">
{{ t('change password.new and confirmation required') }}</div>

<mat-dialog-actions class="p-3 pt-0 mb-0 d-flex justify-content-between flex-11a">
<button class="btn btn-primary m-0 mr-1">{{ t('buttons.change password') }}</button>
<button class="btn btn-secondary mr-1" (click)="cancel()">{{ t('buttons.cancel') }}</button>
</mat-dialog-actions>
</form>
</mat-dialog-content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { environment } from '../../../environments/environment';
import { ChangePassword } from '../../models/reset-pass.model';
import { AuthenticationService } from '../../services/authentication.service';
import { BehaviorSubject, Observable } from 'rxjs';
import { TranslocoService } from '@ngneat/transloco';

@Component({
selector: 'app-change-password',
Expand All @@ -48,7 +49,7 @@ export class ChangePasswordComponent implements OnInit {
private _passwordContainsNumbers: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public passwordContainsNumbers: Observable<boolean> = this._passwordContainsNumbers.asObservable();

msgChangeTempPw = 'Temporary password must be changed on first logon.';
msgChangeTempPw = this.tSvc.translate('change password.change temp on logon');
check = true;
passwordResponse: any = {
passwordLengthMin: 13,
Expand All @@ -64,6 +65,7 @@ export class ChangePasswordComponent implements OnInit {

constructor(private auth: AuthenticationService,
private router: Router,
public tSvc: TranslocoService,
public dialogRef: MatDialogRef<ChangePasswordComponent>,
private ref: ChangeDetectorRef,
private appRef: ApplicationRef,
Expand All @@ -76,12 +78,18 @@ export class ChangePasswordComponent implements OnInit {
this.warning = data.warning;
}

/**
*
*/
ngOnInit() {
if (this.warning) {
this.message = this.msgChangeTempPw;
}
}

/**
*
*/
onPasswordChangeClick(fReg: NgForm): void {
if (this.cpwd.newPassword !== this.cpwd.confirmPassword) {
return;
Expand All @@ -90,11 +98,12 @@ export class ChangePasswordComponent implements OnInit {
this.auth.changePassword(this.cpwd).subscribe(
(response: any) => {
this.passwordResponse = JSON.parse(response);

if (this.passwordResponse.isValid) {
this.dialogRef.close(true);
} else {
this.warning = true;
this.message = this.passwordResponse.message;
this.message = this.tSvc.translate('change password.' + this.passwordResponse.message);
this.ref.detectChanges();
}
},
Expand All @@ -105,6 +114,9 @@ export class ChangePasswordComponent implements OnInit {
});
}

/**
*
*/
checkPassword(event) {
var temp: ChangePassword = {
newPassword: event ?? '',
Expand All @@ -125,6 +137,9 @@ export class ChangePasswordComponent implements OnInit {
});
}

/**
*
*/
cancel() {
this.dialogRef.close();

Expand All @@ -135,6 +150,9 @@ export class ChangePasswordComponent implements OnInit {
}
}

/**
*
*/
togglePasswordVisibility() {
this.showPassword = !this.showPassword;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { filter } from 'rxjs/operators';
import { AuthenticationService } from '../../services/authentication.service';
import { ChangePasswordComponent } from "../../dialogs/change-password/change-password.component";
import { AlertComponent } from '../../dialogs/alert/alert.component';
import { TranslocoService } from '@ngneat/transloco';


@Component({
Expand All @@ -46,6 +47,7 @@ export class LandingPageTabsComponent implements OnInit, AfterViewInit {
constructor(
private route: ActivatedRoute,
private router: Router,
private tSvc: TranslocoService,
public authSvc: AuthenticationService,
public dialog: MatDialog
) { }
Expand Down Expand Up @@ -133,8 +135,8 @@ export class LandingPageTabsComponent implements OnInit, AfterViewInit {
if (passwordChangeSuccess) {
this.dialog.open(AlertComponent, {
data: {
messageText: 'Your password has been changed successfully.',
title: 'Password Changed',
messageText: this.tSvc.translate('change password.changed message'),
title: this.tSvc.translate('change password.changed dialog title'),
iconClass: 'cset-icons-check-circle'
}
});
Expand Down
26 changes: 25 additions & 1 deletion CSETWebNg/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@
"questions mode": "Questions Mode",
"requirements mode": "Requirements Mode",
"add document": "Add a document",
"copy text": "Copy Texzt"
"copy text": "Copy Text",
"change password": "Change Password"
},
"contact": {
"contacts": "Contacts",
Expand Down Expand Up @@ -784,6 +785,29 @@
"not valid email": "Not a valid email",
"emails do not match": "Emails do not match"
},
"change password": {
"dialog title": "Change Password",
"current password": "Current Password",
"show passwords": "Show Passwords",
"hide passwords": "Hide Passwords",
"new password": "New Password",
"confirm new password": "Confirm New Password",
"passwords do not match": "Passwords do not match",
"new and confirmation required": "New Password and Confirmation are required",
"change temp on logon": "Temporary password must be changed on first logon",
"current invalid": "Current password is invalid.<br>Correct it or request a new temporary password.",
"rules not satisfied": "New password does not satisfy all password policy requirements.",
"rules": {
"length between": "Length between {{lengthMin}} and {{lengthMax}} characters",
"required numbers": "Must contain at least 2 numbers",
"required lowercase": "Must contain at least 1 lowercase character",
"required uppercase": "Must contain at least 1 uppercase character",
"required special": "Must contain at least 1 special character",
"previous count": "Must not re-use previous {{count}} passwords"
},
"changed dialog title": "Password Changed",
"changed message": "Your password has been changed successfully"
},
"about": {
"about": "About",
"version": "Version",
Expand Down
26 changes: 25 additions & 1 deletion CSETWebNg/src/assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@
"questions mode": "Modo preguntas",
"requirements mode": "Modo requisitos",
"add document": "Agregar documento",
"copy text": "Copiar texto"
"copy text": "Copiar texto",
"change password": "Cambiar la contraseña"
},
"answer-options": {
"button-labels": {
Expand Down Expand Up @@ -782,6 +783,29 @@
"not valid email": "Email no es válido",
"emails do not match": "Los correos electrónicos no coinciden"
},
"change password": {
"dialog title": "Cambiar la contraseña",
"current password": "Contraseña actual",
"show passwords": "Mostrar contraseñas",
"hide passwords": "Ocultar contraseñas",
"new password": "Nueva contraseña",
"confirm new password": "Confirmar nueva contraseña",
"passwords do not match": "Las contraseñas no coinciden",
"new and confirmation required": "Se requiere nueva contraseña y confirmación",
"change temp on logon": "La contraseña temporal debe cambiarse en el primer inicio de sesión",
"current invalid": "La contraseña actual no es válida.<br>Corríjala o solicite una nueva contraseña temporal.",
"rules not satisfied": "La nueva contraseña no cumple con todos los requisitos de la política de contraseñas.",
"rules": {
"length between": "Longitud entre {{lengthMin}} y {{lengthMax}} caracteres",
"required numbers": "Debe contener al menos 2 números",
"required lowercase": "Debe contener al menos 1 carácter en minúscula",
"required uppercase": "Debe contener al menos 1 carácter en mayúscula",
"required special": "Debe contener al menos 1 carácter especial",
"previous count": "No se deben reutilizar {{count}} contraseñas anteriores"
},
"changed dialog title": "contraseña Cambiada",
"changed message": "tu contraseña ha sido cambiada exitosamente"
},
"about": {
"about": "Sobre",
"version": "Versión",
Expand Down
Loading

0 comments on commit 3a708b9

Please sign in to comment.