Password recovery fixes
gitea/hedera-web/pipeline/head This commit looks good Details

This commit is contained in:
Juan Ferrer 2021-03-31 12:18:25 +02:00
parent 66875ce193
commit f9c8cd0e83
9 changed files with 98 additions and 104 deletions

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
hedera-web (1.407.40) stable; urgency=low hedera-web (1.407.41) stable; urgency=low
* Initial Release. * Initial Release.

View File

@ -1,77 +1,81 @@
Hedera.Conf = new Class Hedera.Conf = new Class({
({
Extends: Hedera.Form Extends: Hedera.Form
,activate: function () ,activate: function() {
{ this.$('user-model').setInfo('c', 'myClient', 'hedera');
this.$('user-model').setInfo ('c', 'myClient', 'hedera');
console.log(this.hash.get('verificationToken'));
if (this.hash.get('verificationToken'))
this.onPassChangeClick();
} }
,onPassChangeClick: function () ,onPassChangeClick: function() {
{
this.$('old-password').value = ''; this.$('old-password').value = '';
this.$('new-password').value = ''; this.$('new-password').value = '';
this.$('repeat-password').value = ''; this.$('repeat-password').value = '';
var recoverPass = this.$('user-form').get ('recoverPass'); var verificationToken = this.hash.get('verificationToken');
this.$('old-password').style.display = recoverPass ? 'none' : 'block'; this.$('old-password').style.display = verificationToken ? 'none' : 'block';
this.$('change-password').show (); this.$('change-password').show();
if (recoverPass) if (verificationToken)
this.$('new-password').focus (); this.$('new-password').focus();
else else
this.$('old-password').focus (); this.$('old-password').focus();
} }
,onPassModifyClick: function () ,onPassModifyClick: function() {
{
try { try {
var oldPassword = this.$('old-password').value; var oldPassword = this.$('old-password').value;
var newPassword = this.$('new-password').value; var newPassword = this.$('new-password').value;
var repeatedPassword = this.$('repeat-password').value; var repeatedPassword = this.$('repeat-password').value;
if (newPassword == '' && repeatedPassword == '') if (newPassword == '' && repeatedPassword == '')
throw new Error (_('Passwords empty')); throw new Error(_('Passwords empty'));
if (newPassword !== repeatedPassword) if (newPassword !== repeatedPassword)
throw new Error (_('Passwords doesn\'t match')); throw new Error(_('Passwords doesn\'t match'));
var params = { var verificationToken = this.hash.get('verificationToken');
oldPassword: oldPassword, var params = {newPassword: newPassword};
newPassword: newPassword
}; if (verificationToken) {
this.conn.send ('core/change-password', params, params.verificationToken = verificationToken;
this._onPassChange.bind (this)); this.conn.send('core/restore-password', params,
} this._onPassChange.bind(this));
catch (e) } else {
{ params.oldPassword = oldPassword;
Htk.Toast.showError (e.message); this.conn.send('core/change-password', params,
this._onPassChange.bind(this));
}
} catch (e) {
Htk.Toast.showError(e.message);
} }
} }
,_onPassChange: function (json, error) ,_onPassChange: function(json, error) {
{ if (json) {
if (json) this.$('change-password').hide();
{ this.hash.unset('verificationToken');
this.$('change-password').hide (); Htk.Toast.showMessage(_('Password changed!'));
Htk.Toast.showMessage (_('Password changed!')); this.$('user-form').refresh();
this.$('user-form').refresh (); } else {
} Htk.Toast.showError(error.message);
else
{ if (this.hash.get('verificationToken'))
Htk.Toast.showError (error.message); this.$('new-password').select();
this.$('old-password').select (); else
this.$('old-password').select();
} }
} }
,onPassInfoClick: function () ,onPassInfoClick: function() {
{ this.$('password-info').show();
this.$('password-info').show ();
} }
,onAddressesClick: function () ,onAddressesClick: function() {
{ this.hash.set({form: 'account/address-list'});
this.hash.set ({form: 'account/address-list'});
} }
}); });

View File

@ -11,8 +11,8 @@
<db-form id="user-form"> <db-form id="user-form">
<db-model property="model" id="user-model" updatable="true"> <db-model property="model" id="user-model" updatable="true">
<custom> <custom>
SELECT u.id, u.name, u.email, u.recoverPass, SELECT u.id, u.name, u.email, u.nickname,
u.nickname, u.lang, c.isToBeMailed, c.id clientFk u.lang, c.isToBeMailed, c.id clientFk
FROM account.myUser u FROM account.myUser u
LEFT JOIN myClient c LEFT JOIN myClient c
ON u.id = c.id ON u.id = c.id

View File

@ -35,6 +35,15 @@ module.exports =
return this._hashMap[key]; return this._hashMap[key];
} }
/**
* Unsets a hash key.
*
* @param {string} key The variable name
**/
,unset: function(key) {
this.add({[key]: undefined});
}
/** /**
* Sets the hash part of the URL, respecting the current hash variables. * Sets the hash part of the URL, respecting the current hash variables.
* *

View File

@ -1,6 +1,6 @@
{ {
"name": "hedera-web", "name": "hedera-web",
"version": "1.407.40", "version": "1.407.41",
"description": "Verdnatura web page", "description": "Verdnatura web page",
"license": "GPL-3.0", "license": "GPL-3.0",
"repository": { "repository": {

View File

@ -6,11 +6,11 @@ include __DIR__.'/account.php';
* Updates the user password. * Updates the user password.
**/ **/
class ChangePassword extends Vn\Web\JsonRequest { class ChangePassword extends Vn\Web\JsonRequest {
const PARAMS = ['newPassword']; const PARAMS = ['oldPassword', 'newPassword'];
function run($db) { function run($db) {
$newPassword = $_REQUEST['newPassword'];
$oldPassword = $_REQUEST['oldPassword']; $oldPassword = $_REQUEST['oldPassword'];
$newPassword = $_REQUEST['newPassword'];
$db->query('CALL account.myUser_changePassword(#, #)', $db->query('CALL account.myUser_changePassword(#, #)',
[$oldPassword, $newPassword]); [$oldPassword, $newPassword]);

View File

@ -6,61 +6,31 @@ class RecoverPassword extends Vn\Web\JsonRequest {
const PARAMS = ['recoverUser']; const PARAMS = ['recoverUser'];
function run($db) { function run($db) {
$recoverUser = $_REQUEST['recoverUser'];
$user = $db->getRow( $user = $db->getRow(
'SELECT email, active FROM account.user WHERE name = #', 'SELECT email, active FROM account.user WHERE name = #',
[$_REQUEST['recoverUser']] [$recoverUser]
); );
if (!($user['active'] && $user['email'])) if (!($user['active'] && $user['email']))
return TRUE; return TRUE;
$verificationToken = bin2hex(random_bytes(16));
$db->query(
'UPDATE account.user SET verificationToken = #
WHERE name = #',
[$verificationToken, $recoverUser]
);
$service = $this->service; $service = $this->service;
$token = $service->createToken($_REQUEST['recoverUser'], FALSE, TRUE); $token = $service->createToken($recoverUser);
$url = $service->getUrl() ."#!form=account/conf&token=$token"; $url = $service->getUrl() ."#!form=account/conf&verificationToken=$verificationToken&token=$token";
$report = new Vn\Web\Report($db, 'recover-password', ['url' => $url]); $report = new Vn\Web\Report($db, 'recover-password', ['url' => $url]);
$report->sendMail($user['email']); $report->sendMail($user['email']);
return \Vn\Lib\Locale::get();
return TRUE; return TRUE;
} }
const LOWERS = 'abcdefghijklmnopqrstuvwxyz';
const UPPERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const DIGITS = '1234567890';
const SYMBOLS = '!$%&()=.';
function genPassword($db) {
$restrictions = $db->getRow(
'SELECT length, nUpper, nDigits, nPunct FROM account.userPassword');
$pass = [];
$newPass = '';
$nAlpha = $restrictions['length'] -(
$restrictions['nUpper'] +
$restrictions['nDigits'] +
$restrictions['nPunct']);
$this->genRands($pass, self::LOWERS, $nAlpha);
$this->genRands($pass, self::UPPERS, $restrictions['nUpper']);
$this->genRands($pass, self::DIGITS, $restrictions['nDigits']);
$this->genRands($pass, self::SYMBOLS, $restrictions['nPunct']);
for ($i = count($pass) - 1; $i >= 0; $i--) {
$rand = rand(0, $i);
$newPass .= $pass[$rand];
array_splice($pass, $rand, 1);
}
return $newPass;
}
function genRands(&$pass, $chars, $max) {
$len = strlen($chars) - 1;
for ($i = 0; $i < $max; $i++)
$pass[] = $chars[rand(0, $len)];
}
} }

View File

@ -0,0 +1,21 @@
<?php
use Vn\Web;
/**
* Restores the user password.
**/
class RestorePassword extends Vn\Web\JsonRequest {
const PARAMS = ['verificationToken', 'newPassword'];
function run($db) {
$verificationToken = $_REQUEST['verificationToken'];
$newPassword = $_REQUEST['newPassword'];
$db->query('CALL account.myUser_restorePassword(#, #)',
[$verificationToken, $newPassword]);
Account::sync($db, $_SESSION['user'], $newPassword);
return TRUE;
}
}

View File

@ -180,13 +180,6 @@ abstract class Service {
throw new SessionExpiredException(); throw new SessionExpiredException();
$user = $jwtPayload['sub']; $user = $jwtPayload['sub'];
if (!empty($jwtPayload['recover']))
$db->query(
'UPDATE account.user SET recoverPass = TRUE
WHERE name = #',
[$user]
);
} }
else { else {
$user = $db->getValue('SELECT guestUser FROM config'); $user = $db->getValue('SELECT guestUser FROM config');
@ -263,7 +256,7 @@ abstract class Service {
* @param {boolean} $recover Wether to enable recovery mode on login * @param {boolean} $recover Wether to enable recovery mode on login
* @return {string} The JWT generated token * @return {string} The JWT generated token
*/ */
function createToken($user, $remember = FALSE, $recover = FALSE) { function createToken($user, $remember = FALSE) {
if ($remember) if ($remember)
$tokenLife = WEEK; $tokenLife = WEEK;
else else
@ -274,9 +267,6 @@ abstract class Service {
'exp' => time() + $tokenLife 'exp' => time() + $tokenLife
]; ];
if ($recover)
$payload['recover'] = 'TRUE';
$key = $this->db->getValue('SELECT jwtKey FROM config'); $key = $this->db->getValue('SELECT jwtKey FROM config');
return Jwt::encode($payload, $key); return Jwt::encode($payload, $key);
} }