Google authenticator integration PR-266
Google authenticator integration PR-266pull/294/head^2 v0.7.0
parent
cf71a4d897
commit
c2a4c1562f
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"55621ac84bdf6c786e25",6:"83056549d4d88d5a89c1",7:"75677e99073742b9f2a4"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([]);
|
||||
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"55621ac84bdf6c786e25",6:"3228fcaf0890e1d596e3",7:"6fecb498f36cf3efcace"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,22 @@
|
||||
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign="space-between stretch">
|
||||
<div fxFlex="100" class="padding-gap-large pl-3">
|
||||
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header mb-2">
|
||||
<div fxFlex="95" fxLayoutAlign="start start">
|
||||
<span class="page-title">Two Factor Token</span>
|
||||
</div>
|
||||
<button tabindex="3" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" (click)="onClose()" mat-button>X</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content fxLayout="row" class="pr-1">
|
||||
<form (ngSubmit)="onVerifyToken()" #tokenForm="ngForm" fxLayout="column" fxFlex="100">
|
||||
<mat-form-field>
|
||||
<input autoFocus matInput placeholder="Token" type="text" id="token" name="token" [(ngModel)]="token" tabindex="2" required>
|
||||
<mat-error *ngIf="!token">Token is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<p *ngIf="tokenErrorMessage !== ''" fxFlex="100" class="color-warn" fxLayoutAlign="start center"><mat-icon class="mr-1 icon-small">close</mat-icon>{{tokenErrorMessage}}</p>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="end center" class="mt-2">
|
||||
<button mat-flat-button color="primary" tabindex="4" type="submit">Verify Token</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginTokenComponent } from './login-2fa-token.component';
|
||||
|
||||
describe('LoginTokenComponent', () => {
|
||||
let component: LoginTokenComponent;
|
||||
let fixture: ComponentFixture<LoginTokenComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LoginTokenComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginTokenComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,59 @@
|
||||
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { faUserClock } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { LoginTokenData } from '../../../models/alertData';
|
||||
|
||||
import * as fromRTLReducer from '../../../../store/rtl.reducers';
|
||||
import * as RTLActions from '../../../../store/rtl.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-login-token',
|
||||
templateUrl: './login-2fa-token.component.html',
|
||||
styleUrls: ['./login-2fa-token.component.scss']
|
||||
})
|
||||
export class LoginTokenComponent implements OnInit, OnDestroy {
|
||||
public faUserClock = faUserClock;
|
||||
public token = '';
|
||||
public tokenErrorMessage = '';
|
||||
public authRes = { token: '' };
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<LoginTokenComponent>, @Inject(MAT_DIALOG_DATA) public data: LoginTokenData, private store: Store<fromRTLReducer.RTLState>) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.authRes = this.data.authRes;
|
||||
this.tokenErrorMessage = '';
|
||||
this.store.select('root')
|
||||
.pipe(takeUntil(this.unSubs[0]))
|
||||
.subscribe((rtlStore) => {
|
||||
rtlStore.effectErrorsRoot.forEach(effectsErr => {
|
||||
if (effectsErr.action === 'VerifyToken') {
|
||||
this.tokenErrorMessage = this.tokenErrorMessage + effectsErr.message + ' ';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
onVerifyToken() {
|
||||
if (!this.token) { return true; }
|
||||
this.tokenErrorMessage = '';
|
||||
this.store.dispatch(new RTLActions.VerifyTwoFA({token: this.token, authResponse: this.authRes}));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('VerifyToken'));
|
||||
this.unSubs.forEach(completeSub => {
|
||||
completeSub.next();
|
||||
completeSub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
<div fxLayout="row">
|
||||
<div fxFlex="100" class="padding-gap-large">
|
||||
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
|
||||
<div fxFlex="95" fxLayoutAlign="start start" class="padding-gap-x-large"><span class="page-title">Setup Two Factor Authentication</span></div>
|
||||
<button tabindex="15" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" [mat-dialog-close]="false" mat-button>X</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-5px">
|
||||
<div fxLayout="column">
|
||||
<mat-vertical-stepper [linear]="true" #stepper (selectionChange)="stepSelectionChanged($event)">
|
||||
<mat-step [stepControl]="passwordFormGroup" [editable]="flgEditable">
|
||||
<form [formGroup]="passwordFormGroup" fxLayout="column" fxLayoutAlign="space-between" class="my-1 pr-1">
|
||||
<ng-template matStepLabel>{{passwordFormLabel}}</ng-template>
|
||||
<div fxLayout="row">
|
||||
<mat-form-field fxFlex="100">
|
||||
<input autoFocus matInput placeholder="Password" type="password" tabindex="1" formControlName="password" required>
|
||||
<mat-error *ngIf="passwordFormGroup.controls.password.errors?.required">Password is required.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="mt-2" fxLayout="row">
|
||||
<button mat-stroked-button color="primary" tabindex="3" type="button" (click)="onAuthenticate()">Confirm</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step [stepControl]="secretFormGroup" [editable]="flgEditable" *ngIf="!showDisableStepper">
|
||||
<form [formGroup]="secretFormGroup" fxLayout="column" class="my-1 pr-1">
|
||||
<ng-template matStepLabel disabled="true">{{secretFormLabel}}</ng-template>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start">
|
||||
<qrcode [qrdata]="otpauth" [margin]="1" [width]="180" [errorCorrectionLevel]="'L'"></qrcode>
|
||||
</div>
|
||||
<div fxFlex="100" class="w-100 alert alert-info">
|
||||
<fa-icon [icon]="faInfoCircle" class="mt-1 mr-1 alert-icon"></fa-icon>
|
||||
<span>You can use a compatible authentication app to get an authentication code when you log in to RTL. e.g.: Google Authenticator.</span>
|
||||
</div>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between stretch">
|
||||
<mat-form-field fxFlex="100">
|
||||
<input autoFocus matInput placeholder="Secret Code" type="text" tabindex="4" formControlName="secret" required>
|
||||
<fa-icon [icon]="faCopy" matSuffix rtlClipboard [payload]="secretFormGroup.controls.secret.value" (copied)="onCopySecret($event)"></fa-icon>
|
||||
<mat-error *ngIf="secretFormGroup.controls.secret.errors?.required">Secret Code is required.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
|
||||
<button mat-stroked-button color="primary" tabindex="6" type="button" matStepperNext>Next</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step [stepControl]="tokenFormGroup" *ngIf="!showDisableStepper">
|
||||
<form [formGroup]="tokenFormGroup" fxLayout="column" fxLayoutAlign="start" class="my-1 pr-1">
|
||||
<ng-template matStepLabel>{{tokenFormLabel}}</ng-template>
|
||||
<div fxLayout="column" *ngIf="!flgValidated || !isTokenValid">
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between stretch">
|
||||
<mat-form-field fxFlex="100">
|
||||
<input autoFocus matInput placeholder="Token" type="text" tabindex="7" formControlName="token" required>
|
||||
<mat-error *ngIf="tokenFormGroup.controls.token.errors?.required">Token is required.</mat-error>
|
||||
<mat-error *ngIf="tokenFormGroup.controls.token.errors?.notValid">Token is invalid.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
|
||||
<button mat-stroked-button color="primary" tabindex="8" type="button" (click)="onVerifyToken()">{{tokenFormGroup.controls.token.errors.notValid ? 'Retry' : 'Verify'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="flgValidated && isTokenValid">
|
||||
<strong>Success! You are all set.</strong>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step [stepControl]="disableFormGroup" *ngIf="showDisableStepper">
|
||||
<form [formGroup]="disableFormGroup" fxLayout="column" fxLayoutAlign="start" class="my-1 pr-1">
|
||||
<ng-template matStepLabel>{{disableFormLabel}}</ng-template>
|
||||
<div fxLayout="column" *ngIf="!flgValidated || !isTokenValid">
|
||||
<div fxFlex="100" class="w-100 alert alert-warn">
|
||||
<fa-icon [icon]="faExclamationTriangle" class="mt-1 mr-1 alert-icon"></fa-icon>
|
||||
<span>You are about to disable two-factor authentication security from RTL. Are you sure you want to turn it off?</span>
|
||||
</div>
|
||||
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
|
||||
<button mat-stroked-button color="primary" tabindex="8" type="button" (click)="onVerifyToken()">Disable</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="flgValidated && isTokenValid">
|
||||
<strong>Two factor authentication removed from RTL.</strong>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
</mat-vertical-stepper>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="end center">
|
||||
<button mat-stroked-button color="primary" tabindex="12" type="button" [mat-dialog-close]="false" default>{{flgValidated && isTokenValid ? 'Close' : 'Cancel'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TwoFactorAuthComponent } from './two-factor-auth.component';
|
||||
|
||||
describe('TwoFactorAuthComponent', () => {
|
||||
let component: TwoFactorAuthComponent;
|
||||
let fixture: ComponentFixture<TwoFactorAuthComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ TwoFactorAuthComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TwoFactorAuthComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,143 @@
|
||||
import { Component, OnInit, OnDestroy, Inject, ViewChild } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
import { Subject } from 'rxjs';
|
||||
import { take, takeUntil } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef, MatVerticalStepper, MatSnackBar } from '@angular/material';
|
||||
import { authenticator } from 'otplib/otplib-browser';
|
||||
import { faInfoCircle, faCopy, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as sha256 from 'sha256';
|
||||
|
||||
import { RTLConfiguration } from '../../../models/RTLconfig';
|
||||
import { AuthConfig } from '../../../models/alertData';
|
||||
|
||||
import { RTLEffects } from '../../../../store/rtl.effects';
|
||||
import * as fromRTLReducer from '../../../../store/rtl.reducers';
|
||||
import * as RTLActions from '../../../../store/rtl.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-two-factor-auth',
|
||||
templateUrl: './two-factor-auth.component.html',
|
||||
styleUrls: ['./two-factor-auth.component.scss']
|
||||
})
|
||||
export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('twoFAForm', { static: true }) twoFAForm: any;
|
||||
@ViewChild('stepper', { static: false }) stepper: MatVerticalStepper;
|
||||
public faExclamationTriangle = faExclamationTriangle;
|
||||
public faCopy = faCopy;
|
||||
public faInfoCircle = faInfoCircle;
|
||||
public flgValidated = false;
|
||||
public isTokenValid = true;
|
||||
public otpauth: string;
|
||||
public appConfig: RTLConfiguration;
|
||||
public flgEditable = true;
|
||||
public showDisableStepper = false;
|
||||
public passwordFormLabel = 'Authenticate with your RTL password';
|
||||
public secretFormLabel = 'Scan or copy the secret';
|
||||
public tokenFormLabel = 'Verify your authentication is working';
|
||||
public disableFormLabel = 'Disable two factor authentication';
|
||||
passwordFormGroup: FormGroup;
|
||||
secretFormGroup: FormGroup;
|
||||
tokenFormGroup: FormGroup;
|
||||
disableFormGroup: FormGroup;
|
||||
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<TwoFactorAuthComponent>, @Inject(MAT_DIALOG_DATA) public data: AuthConfig, private store: Store<fromRTLReducer.RTLState>, private formBuilder: FormBuilder, private rtlEffects: RTLEffects, private snackBar: MatSnackBar) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.appConfig = this.data.appConfig;
|
||||
this.showDisableStepper = !!this.appConfig.enable2FA;
|
||||
this.passwordFormGroup = this.formBuilder.group({
|
||||
hiddenPassword: ['', [Validators.required]],
|
||||
password: ['', [Validators.required]]
|
||||
});
|
||||
this.secretFormGroup = this.formBuilder.group({
|
||||
secret: [{value: !this.appConfig.enable2FA ? this.generateSecret() : '', disabled: true}, Validators.required]
|
||||
});
|
||||
this.tokenFormGroup = this.formBuilder.group({
|
||||
token: ['', Validators.required]
|
||||
});
|
||||
this.disableFormGroup = this.formBuilder.group({});
|
||||
}
|
||||
|
||||
generateSecret() {
|
||||
let secret2fa = authenticator.generateSecret();
|
||||
this.otpauth = authenticator.keyuri('', 'Ride The Lightning (RTL)', secret2fa);
|
||||
return secret2fa;
|
||||
}
|
||||
|
||||
onAuthenticate() {
|
||||
if (!this.passwordFormGroup.controls.password.value) { return true; }
|
||||
this.flgValidated = false;
|
||||
this.store.dispatch(new RTLActions.IsAuthorized(sha256(this.passwordFormGroup.controls.password.value)));
|
||||
this.rtlEffects.isAuthorizedRes
|
||||
.pipe(take(1))
|
||||
.subscribe(authRes => {
|
||||
if (authRes !== 'ERROR') {
|
||||
this.passwordFormGroup.controls.hiddenPassword.setValue(this.passwordFormGroup.controls.password.value);
|
||||
this.stepper.next();
|
||||
} else {
|
||||
this.dialogRef.close();
|
||||
this.snackBar.open('Unauthorized User. Logging out from RTL.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onCopySecret(payload: string) {
|
||||
this.snackBar.open('Secret code ' + this.secretFormGroup.controls.secret.value + ' copied.');
|
||||
}
|
||||
|
||||
onVerifyToken() {
|
||||
if (this.appConfig.enable2FA) {
|
||||
this.store.dispatch(new RTLActions.OpenSpinner('Updating Settings...'));
|
||||
this.store.dispatch(new RTLActions.TwoFASaveSettings({secret2fa: ''}));
|
||||
this.generateSecret();
|
||||
this.isTokenValid = true;
|
||||
} else {
|
||||
if (!this.tokenFormGroup.controls.token.value) { return true; }
|
||||
this.isTokenValid = authenticator.check(this.tokenFormGroup.controls.token.value, this.secretFormGroup.controls.secret.value);
|
||||
if (!this.isTokenValid) {
|
||||
this.tokenFormGroup.controls.token.setErrors({ notValid: true });
|
||||
return true;
|
||||
}
|
||||
this.store.dispatch(new RTLActions.OpenSpinner('Updating Settings...'));
|
||||
this.store.dispatch(new RTLActions.TwoFASaveSettings({secret2fa: this.secretFormGroup.controls.secret.value}));
|
||||
this.tokenFormGroup.controls.token.setValue('');
|
||||
}
|
||||
this.flgValidated = true;
|
||||
this.appConfig.enable2FA = !this.appConfig.enable2FA;
|
||||
}
|
||||
|
||||
stepSelectionChanged(event: any) {
|
||||
switch (event.selectedIndex) {
|
||||
case 0:
|
||||
this.passwordFormLabel = 'Authenticate with your RTL password';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this.passwordFormLabel = 'User authenticated successfully';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
this.passwordFormLabel = 'User authenticated successfully';
|
||||
break;
|
||||
|
||||
default:
|
||||
this.passwordFormLabel = 'Authenticate with your RTL password';
|
||||
break;
|
||||
}
|
||||
if (event.selectedIndex < event.previouslySelectedIndex) {
|
||||
if (event.selectedIndex === 0) {
|
||||
this.passwordFormGroup.controls.hiddenPassword.setValue('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach(unsub => {
|
||||
unsub.next();
|
||||
unsub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,30 +1,42 @@
|
||||
<div fxLayout="column" fxFlex="100" class="overflow-x-hidden">
|
||||
<form (ngSubmit)="onResetPassword()" fxLayout="column" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #authForm="ngForm">
|
||||
<div fxFlex="100" class="mb-1">
|
||||
<fa-icon [icon]="faKey" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Reset Password</span>
|
||||
<form (ngSubmit)="onChangePassword()" fxLayout="column" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #authForm="ngForm">
|
||||
<div fxFlex="100">
|
||||
<fa-icon [icon]="faUserLock" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Password</span>
|
||||
</div>
|
||||
<div fxLayout="row">
|
||||
<div fxLayout="column" fxFlex="100" fxFlex.gt-sm="100" fxLayoutAlign="space-between stretch" class="mt-2">
|
||||
<div fxLayout="column" fxFlex="100" fxFlex.gt-sm="100" fxLayoutAlign="space-between stretch">
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Old Password" type="password" id="oldpassword" name="oldpassword" [(ngModel)]="oldPassword" tabindex="1" required>
|
||||
<mat-error *ngIf="!oldPassword">Old password is required.</mat-error>
|
||||
<input matInput placeholder="Current Password" type="password" id="currpassword" name="currpassword" [(ngModel)]="currPassword" tabindex="4" required>
|
||||
<mat-error *ngIf="!currPassword">Current password is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-divider [inset]="true"></mat-divider>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="New Password" type="password" id="newpassword" name="newpassword" [(ngModel)]="newPassword" tabindex="2" required>
|
||||
<input matInput placeholder="New Password" type="password" id="newpassword" name="newpassword" [(ngModel)]="newPassword" tabindex="5" required>
|
||||
<mat-error *ngIf="matchOldAndNewPasswords()">{{errorMsg}}</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Confirm New Password" type="password" id="confirmpassword" name="confirmpassword" [(ngModel)]="confirmPassword" tabindex="3" required>
|
||||
<input matInput placeholder="New Password" type="password" id="confirmpassword" name="confirmpassword" [(ngModel)]="confirmPassword" tabindex="6" required>
|
||||
<mat-error *ngIf="matchNewPasswords()">{{errorConfirmMsg}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout="row">
|
||||
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2">
|
||||
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="4" type="reset" (click)="resetData()">Clear</button>
|
||||
<button fxFlex="48" fxLayoutAlign="center center" mat-flat-button color="primary" tabindex="5" type="submit">Reset Password</button>
|
||||
<div fxLayout="row" fxLayoutAlign="start start" class="mt-1">
|
||||
<button mat-flat-button color="primary" tabindex="8" type="submit">Change Password</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<mat-divider class="my-2" [inset]="true"></mat-divider>
|
||||
<div fxFlex="100" class="mb-1 settings-container page-sub-title-container mt-1">
|
||||
<fa-icon [icon]="faUserClock" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Two Factor Authentication</span>
|
||||
</div>
|
||||
<div fxFlex="100" class="w-100 alert alert-info">
|
||||
<fa-icon [icon]="faInfoCircle" class="mt-1 mr-1 alert-icon"></fa-icon>
|
||||
<span>Protect your account from unauthorized access by requiring a second authentication method in addition to your password.</span>
|
||||
</div>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start" class="mt-1">
|
||||
<button mat-flat-button color="primary" tabindex="3" (click)="on2FAuth()">{{appConfig.enable2FA ? 'Disable 2FA' : 'Enable 2FA'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue