Password reset page

Password reset page
pull/260/head
Shahana Farooqui 4 years ago
parent 933e19df2c
commit 6065a27c50

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -12,5 +12,5 @@
<link rel="stylesheet" href="styles.90ee7bcb73e8367b2a29.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.2e6ad7cb977005b4f6b6.js" defer></script><script src="polyfills-es5.37b2eeccc22c1df73ce7.js" nomodule defer></script><script src="polyfills.f1c3d2a0bcdfc4e93ca8.js" defer></script><script src="main.0990540ecd60b7ec6d35.js" defer></script></body>
<script src="runtime.bd15680a9b84bab2ef08.js" defer></script><script src="polyfills-es5.37b2eeccc22c1df73ce7.js" nomodule defer></script><script src="polyfills.f1c3d2a0bcdfc4e93ca8.js" defer></script><script src="main.83987fd3a7fe4528412f.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
!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:"b78c4146e439be098e08",6:"30c4bc397969e1e00c8a",7:"4629778ec16eba9683e4"}[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()}([]);

@ -0,0 +1 @@
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=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,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(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,i),t.l=!0,t.exports}i.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,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"."+{1:"b78c4146e439be098e08",6:"3745fb0b662c2ed95634",7:"96c25bc9ca5994f524b4"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.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:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);

@ -82,12 +82,12 @@ connect.normalizePort = val => {
return false;
};
connect.convertCustomToHash = () => {
connect.replacePasswordWithHash = (multiPassHashed) => {
common.rtl_conf_file_path = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : path.normalize(__dirname);
try {
RTLConfFile = common.rtl_conf_file_path + common.path_separator + 'RTL-Config.json';
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.multiPassHashed = hash.update(config.multiPass).digest('hex');
config.multiPassHashed = multiPassHashed;
delete config.multiPass;
fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
console.log('Please note that, RTL has encrypted the plaintext password into its corresponding hash.');
@ -104,7 +104,7 @@ connect.validateNodeConfig = (config) => {
} else if (config.multiPassHashed !== '' && config.multiPassHashed) {
common.rtl_pass = config.multiPassHashed;
} else if (config.multiPass !== '' && config.multiPass) {
common.rtl_pass = connect.convertCustomToHash();
common.rtl_pass = connect.replacePasswordWithHash(hash.update(config.multiPass).digest('hex'));
} else {
errMsg = errMsg + '\nNode Authentication can be set with multiPass only. Please set multiPass in RTL-Config.json';
}

@ -11,7 +11,7 @@ exports.authenticateUser = (req, res, next) => {
} else if (req.body.authenticateWith === 'PASSWORD' && crypto.createHash('sha256').update(common.cookie).digest('hex') === req.body.authenticationValue) {
connect.refreshCookie(common.rtl_cookie_path);
const token = jwt.sign(
{ user: 'Custom_User', configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
{ user: 'SSO_USER', configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
common.secret_key
);
res.status(200).json({ token: token });
@ -25,7 +25,7 @@ exports.authenticateUser = (req, res, next) => {
} else {
const password = req.body.authenticationValue;
if (common.rtl_pass === password) {
var rpcUser = 'Node_User';
var rpcUser = 'NODE_USER';
const token = jwt.sign(
{ user: rpcUser, configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
common.secret_key
@ -40,3 +40,30 @@ exports.authenticateUser = (req, res, next) => {
}
}
};
exports.resetPassword = (req, res, next) => {
if(+common.rtl_sso) {
logger.error({fileName: 'Authenticate', lineNum: 46, msg: 'Password Reset Failed!'});
res.status(402).json({
message: "Password Reset Failure!",
error: "Password cannot be reset for SSO authentication!"
});
} else {
const oldPassword = req.body.oldPassword;
if (common.rtl_pass === oldPassword) {
common.rtl_pass = connect.replacePasswordWithHash(req.body.newPassword);
var rpcUser = 'NODE_USER';
const token = jwt.sign(
{ user: rpcUser, configPath: common.nodes[0].config_path, macaroonPath: common.nodes[0].macaroon_path },
common.secret_key
);
res.status(200).json({ token: token });
} else {
logger.error({fileName: 'Authenticate', lineNum: 62, msg: 'Password Reset Failed!'});
res.status(402).json({
message: "Password Reset Failed!",
error: "Old password is not correct!"
});
}
}
};

@ -3,5 +3,6 @@ const express = require("express");
const router = express.Router();
router.post("/", AuthenticateController.authenticateUser);
router.post("/reset", AuthenticateController.resetPassword);
module.exports = router;

@ -42,7 +42,7 @@ export class LoginComponent implements OnInit, OnDestroy {
onLogin() {
if(!this.password) { return true; }
this.store.dispatch(new RTLActions.Login(sha256(this.password)));
this.store.dispatch(new RTLActions.Login({password: sha256(this.password), initialPass: this.password === 'password'}));
}
resetData() {

@ -0,0 +1,34 @@
<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" #form="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>
</div>
<div fxFlex="100" class="alert alert-info">
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
<span>Password set by the environment variable can not be reset from this page.</span>
</div>
<div fxLayout="row">
<div fxLayout="column" fxFlex="100" fxFlex.gt-sm="100" fxLayoutAlign="space-between stretch" class="mt-2">
<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>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="New Password" type="password" id="newpassword" name="newpassword" [(ngModel)]="newPassword" tabindex="2" required>
<mat-error *ngIf="!newPassword">New password is required.</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>
<mat-error *ngIf="!confirmPassword">Confirm new password is required.</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>
</div>
</form>
</div>

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AuthSettingsComponent } from './auth-settings.component';
describe('AuthSettingsComponent', () => {
let component: AuthSettingsComponent;
let fixture: ComponentFixture<AuthSettingsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AuthSettingsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AuthSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,36 @@
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { faInfoCircle, faKey } from '@fortawesome/free-solid-svg-icons';
import * as sha256 from 'sha256';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
import * as RTLActions from '../../../../store/rtl.actions';
@Component({
selector: 'rtl-auth-settings',
templateUrl: './auth-settings.component.html',
styleUrls: ['./auth-settings.component.scss']
})
export class AuthSettingsComponent implements OnInit {
public faKey = faKey;
public faInfoCircle = faInfoCircle;
public oldPassword = '';
public newPassword = '';
public confirmPassword = '';
constructor(private store: Store<fromRTLReducer.RTLState>) {}
ngOnInit() {}
onResetPassword() {
if(!this.oldPassword || !this.newPassword || !this.confirmPassword) { return true; }
this.store.dispatch(new RTLActions.ResetPassword({oldPassword: sha256(this.oldPassword), newPassword: sha256(this.newPassword)}));
}
resetData() {
this.oldPassword = '';
this.newPassword = '';
this.confirmPassword = '';
}
}

@ -5,8 +5,9 @@
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<mat-tab-group>
<mat-tab label="Settings"><rtl-app-settings></rtl-app-settings></mat-tab>
<mat-tab-group selectedIndex="{{loadTab === 'authSettings' ? 1 : 0}}">
<mat-tab id="appSettings" label="Settings"><rtl-app-settings></rtl-app-settings></mat-tab>
<mat-tab label="Password Reset"><rtl-auth-settings></rtl-auth-settings></mat-tab>
<mat-tab *ngIf="showLnConfig" [label]="lnImplementationStr">
<ng-template matTabContent>
<rtl-server-config [selectedNodeType]="'ln'"></rtl-server-config>

@ -6,6 +6,7 @@ import { faTools } from '@fortawesome/free-solid-svg-icons';
import { ConfigSettingsNode } from '../../models/RTLconfig';
import * as fromRTLReducer from '../../../store/rtl.reducers';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'rtl-settings',
@ -18,13 +19,19 @@ export class SettingsComponent implements OnInit, OnDestroy{
public showBitcoind = false;
public selNode: ConfigSettingsNode;
public lnImplementationStr = '';
public loadTab = 'appSettings';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>) {}
constructor(private store: Store<fromRTLReducer.RTLState>, private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.store.select('root')
this.activatedRoute.paramMap
.pipe(takeUntil(this.unSubs[0]))
.subscribe(data => {
this.loadTab = window.history.state.loadTab ? window.history.state.loadTab : 'appSettings';
});
this.store.select('root')
.pipe(takeUntil(this.unSubs[1]))
.subscribe((rtlStore) => {
this.showLnConfig = false;
this.showBitcoind = false;

@ -50,6 +50,7 @@ import { NonNegativeAmountValidator } from './directive/non-negative-amount.dire
import { RemoveLeadingZerosPipe } from './pipes/app.pipe';
import { LoggerService, ConsoleLoggerService } from '../shared/services/logger.service';
import { AuthSettingsComponent } from './components/settings/auth-settings/auth-settings.component';
@NgModule({
imports: [
@ -179,7 +180,8 @@ import { LoggerService, ConsoleLoggerService } from '../shared/services/logger.s
RemoveLeadingZerosPipe,
CLOpenChannelComponent,
OpenChannelComponent,
ShowPubkeyComponent
ShowPubkeyComponent,
AuthSettingsComponent
],
entryComponents: [
CLInvoiceInformationComponent,

@ -94,6 +94,7 @@ export const IS_AUTHORIZED = 'IS_AUTHORIZED';
export const IS_AUTHORIZED_RES = 'IS_AUTHORIZED_RES';
export const LOGIN = 'LOGIN';
export const LOGOUT = 'LOGOUT';
export const RESET_PASSWORD = 'RESET_PASSWORD';
export const PEER_LOOKUP = 'PEER_LOOKUP';
export const CHANNEL_LOOKUP = 'CHANNEL_LOOKUP';
export const INVOICE_LOOKUP = 'INVOICE_LOOKUP';
@ -595,7 +596,7 @@ export class IsAuthorizedRes implements Action {
export class Login implements Action {
readonly type = LOGIN;
constructor(public payload: string) {} // payload = password
constructor(public payload: {password: string, initialPass: boolean}) {}
}
export class Logout implements Action {
@ -603,6 +604,11 @@ export class Logout implements Action {
constructor() {}
}
export class ResetPassword implements Action {
readonly type = RESET_PASSWORD;
constructor(public payload: {oldPassword: string, newPassword: string}) {}
}
export class SetChildNodeSettingsCL implements Action {
readonly type = SET_CHILD_NODE_SETTINGS_CL;
constructor(public payload: SelNodeChild) {}
@ -845,7 +851,7 @@ export type RTLActions =
GetNewAddress | SetNewAddress | SetChannelTransaction |
GenSeed | GenSeedResponse | InitWallet | InitWalletResponse | UnlockWallet |
FetchConfig | ShowConfig | PeerLookup | ChannelLookup | InvoiceLookup | SetLookup |
IsAuthorized | IsAuthorizedRes | Login | Logout |
IsAuthorized | IsAuthorizedRes | Login | Logout | ResetPassword |
SetChildNodeSettingsCL | FetchInfoCL | SetInfoCL | FetchFeesCL | SetFeesCL | FetchFeeRatesCL | SetFeeRatesCL |
FetchBalanceCL | SetBalanceCL | FetchLocalRemoteBalanceCL | SetLocalRemoteBalanceCL |
GetNewAddressCL | SetNewAddressCL |

@ -277,8 +277,8 @@ export class RTLEffects implements OnDestroy {
this.store.dispatch(new RTLActions.ClearEffectErrorCl('FetchInfoCL'));
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('Login'));
return this.httpClient.post(environment.AUTHENTICATE_API, {
authenticateWith: (undefined === action.payload || action.payload == null || action.payload === '') ? AuthenticateWith.TOKEN : AuthenticateWith.PASSWORD,
authenticationValue: (undefined === action.payload || action.payload == null || action.payload === '') ? (this.sessionService.getItem('token') ? this.sessionService.getItem('token') : '') : action.payload
authenticateWith: (!action.payload.password) ? AuthenticateWith.TOKEN : AuthenticateWith.PASSWORD,
authenticationValue: (!action.payload.password) ? (this.sessionService.getItem('token') ? this.sessionService.getItem('token') : '') : action.payload.password
})
.pipe(
map((postRes: any) => {
@ -287,6 +287,10 @@ export class RTLEffects implements OnDestroy {
this.SetToken(postRes.token);
rootStore.selNode.settings.currencyUnits = [...CURRENCY_UNITS, rootStore.selNode.settings.currencyUnit];
this.store.dispatch(new RTLActions.SetSelelectedNode({lnNode: rootStore.selNode, isInitialSetup: true}))
if(action.payload.initialPass) {
this.store.dispatch(new RTLActions.OpenSnackBar('Reset your password before moving forward.'));
this.router.navigate(['/settings'], { state: { loadTab: 'authSettings' }});
}
}),
catchError((err) => {
this.store.dispatch(new RTLActions.EffectErrorRoot({ action: 'Login', code: err.status, message: err.error.message }));
@ -320,6 +324,31 @@ export class RTLEffects implements OnDestroy {
}));
@Effect({ dispatch: false })
resetPassword = this.actions$.pipe(
ofType(RTLActions.RESET_PASSWORD),
withLatestFrom(this.store.select('root')),
mergeMap(([action, rootStore]: [RTLActions.ResetPassword, fromRTLReducer.RootState]) => {
this.store.dispatch(new RTLActions.ClearEffectErrorRoot('ResetPassword'));
return this.httpClient.post(environment.AUTHENTICATE_API + '/reset', {
oldPassword: action.payload.oldPassword,
newPassword: action.payload.newPassword
})
.pipe(
map((postRes: any) => {
this.logger.info(postRes);
this.logger.info('Password Reset Successful!');
this.store.dispatch(new RTLActions.OpenSnackBar('Password Reset Successful!'));
this.SetToken(postRes.token);
}),
catchError((err) => {
this.store.dispatch(new RTLActions.EffectErrorRoot({ action: 'ResetPassword', code: err.status, message: err.error.message }));
this.handleErrorWithAlert('ERROR', 'Password Reset Failed!', environment.AUTHENTICATE_API + '/reset', err.error);
return of({type: RTLActions.VOID});
})
);
}));
@Effect()
setSelectedNode = this.actions$.pipe(
ofType(RTLActions.SET_SELECTED_NODE),

Loading…
Cancel
Save