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.

View File

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

View File

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

View File

@ -34,6 +34,15 @@ module.exports =
,get: function(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.

View File

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

View File

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

View File

@ -6,61 +6,31 @@ class RecoverPassword extends Vn\Web\JsonRequest {
const PARAMS = ['recoverUser'];
function run($db) {
$recoverUser = $_REQUEST['recoverUser'];
$user = $db->getRow(
'SELECT email, active FROM account.user WHERE name = #',
[$_REQUEST['recoverUser']]
[$recoverUser]
);
if (!($user['active'] && $user['email']))
return TRUE;
$verificationToken = bin2hex(random_bytes(16));
$db->query(
'UPDATE account.user SET verificationToken = #
WHERE name = #',
[$verificationToken, $recoverUser]
);
$service = $this->service;
$token = $service->createToken($_REQUEST['recoverUser'], FALSE, TRUE);
$url = $service->getUrl() ."#!form=account/conf&token=$token";
$token = $service->createToken($recoverUser);
$url = $service->getUrl() ."#!form=account/conf&verificationToken=$verificationToken&token=$token";
$report = new Vn\Web\Report($db, 'recover-password', ['url' => $url]);
$report->sendMail($user['email']);
return \Vn\Lib\Locale::get();
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();
$user = $jwtPayload['sub'];
if (!empty($jwtPayload['recover']))
$db->query(
'UPDATE account.user SET recoverPass = TRUE
WHERE name = #',
[$user]
);
}
else {
$user = $db->getValue('SELECT guestUser FROM config');
@ -263,7 +256,7 @@ abstract class Service {
* @param {boolean} $recover Wether to enable recovery mode on login
* @return {string} The JWT generated token
*/
function createToken($user, $remember = FALSE, $recover = FALSE) {
function createToken($user, $remember = FALSE) {
if ($remember)
$tokenLife = WEEK;
else
@ -274,9 +267,6 @@ abstract class Service {
'exp' => time() + $tokenLife
];
if ($recover)
$payload['recover'] = 'TRUE';
$key = $this->db->getValue('SELECT jwtKey FROM config');
return Jwt::encode($payload, $key);
}