Moved password recovery code into its own file.

This commit is contained in:
flash 2018-10-08 14:59:58 +02:00
parent daae61aacf
commit 1785aae6eb
4 changed files with 92 additions and 61 deletions

View file

@ -57,6 +57,7 @@ require_once 'src/Users/avatar.php';
require_once 'src/Users/background.php';
require_once 'src/Users/login_attempt.php';
require_once 'src/Users/profile.php';
require_once 'src/Users/recovery.php';
require_once 'src/Users/relations.php';
require_once 'src/Users/role.php';
require_once 'src/Users/session.php';

View file

@ -83,21 +83,7 @@ switch ($authMode) {
tpl_var('auth_reset_message', "A verification code should've been sent to your e-mail address.");
while ($isSubmission) {
$validateRequest = db_prepare('
SELECT COUNT(`user_id`) > 0
FROM `msz_users_password_resets`
WHERE `user_id` = :user
AND `verification_code` = :code
AND `verification_code` IS NOT NULL
AND `reset_requested` > NOW() - INTERVAL 1 HOUR
');
$validateRequest->bindValue('user', $resetUser['user_id']);
$validateRequest->bindValue('code', $authVerification);
$validateRequest = $validateRequest->execute()
? (bool)$validateRequest->fetchColumn()
: false;
if (!$validateRequest) {
if (!user_recovery_token_validate($resetUser['user_id'], $authVerification)) {
tpl_var('auth_reset_error', 'Invalid verification code!');
break;
}
@ -116,32 +102,13 @@ switch ($authMode) {
break;
}
$updatePassword = db_prepare('
UPDATE `msz_users`
SET `password` = :password
WHERE `user_id` = :user
');
$updatePassword->bindValue('user', $resetUser['user_id']);
$updatePassword->bindValue('password', user_password_hash($authPassword['new']));
if ($updatePassword->execute()) {
if (user_password_set($resetUser['user_id'], $authPassword['new'])) {
audit_log('PASSWORD_RESET', $resetUser['user_id']);
} else {
throw new UnexpectedValueException('Password reset failed.');
}
$invalidateCode = db_prepare('
UPDATE `msz_users_password_resets`
SET `verification_code` = NULL
WHERE `verification_code` = :code
AND `user_id` = :user
');
$invalidateCode->bindValue('user', $resetUser['user_id']);
$invalidateCode->bindValue('code', $authVerification);
if (!$invalidateCode->execute()) {
throw new UnexpectedValueException('Verification code invalidation failed.');
}
user_recovery_token_invalidate($resetUser['user_id'], $authVerification);
header('Location: /auth.php?m=login&u=' . $resetUser['user_id']);
break;
@ -183,33 +150,11 @@ switch ($authMode) {
}
$ipAddress = ip_remote_address();
$emailSent = db_prepare('
SELECT COUNT(`verification_code`) > 0
FROM `msz_users_password_resets`
WHERE `user_id` = :user
AND `reset_ip` = INET6_ATON(:ip)
AND `reset_requested` > NOW() - INTERVAL 1 HOUR
AND `verification_code` IS NOT NULL
');
$emailSent->bindValue('user', $forgotUser['user_id']);
$emailSent->bindValue('ip', $ipAddress);
$emailSent = $emailSent->execute()
? (bool)$emailSent->fetchColumn()
: false;
if (!$emailSent) {
$verificationCode = bin2hex(random_bytes(6));
$insertResetKey = db_prepare('
REPLACE INTO `msz_users_password_resets`
(`user_id`, `reset_ip`, `verification_code`)
VALUES
(:user, INET6_ATON(:ip), :code)
');
$insertResetKey->bindValue('user', $forgotUser['user_id']);
$insertResetKey->bindValue('ip', $ipAddress);
$insertResetKey->bindValue('code', $verificationCode);
if (!user_recovery_emaiL_sent($forgotUser['user_id'], $ipAddress)) {
$verificationCode = user_recovery_token_create($forgotUser['user_id'], $ipAddress);
if (!$insertResetKey->execute()) {
if (empty($verificationCode)) {
throw new UnexpectedValueException('A verification code failed to insert.');
}
@ -231,6 +176,7 @@ MSG;
if (!mail_send($message)) {
tpl_var('auth_forgot_error', 'Failed to send reset email, please contact the administrator.');
user_recovery_token_invalidate($forgotUser['user_id'], $verificationCode);
break;
}
}

72
src/Users/recovery.php Normal file
View file

@ -0,0 +1,72 @@
<?php
define('MSZ_USER_RECOVERY_TOKEN_LENGTH', 6); // * 2
function user_recovery_token_sent(int $userId, string $ipAddress): bool
{
$tokenSent = db_prepare('
SELECT COUNT(`verification_code`) > 0
FROM `msz_users_password_resets`
WHERE `user_id` = :user
AND `reset_ip` = INET6_ATON(:ip)
AND `reset_requested` > NOW() - INTERVAL 1 HOUR
AND `verification_code` IS NOT NULL
');
$tokenSent->bindValue('user', $userId);
$tokenSent->bindValue('ip', $ipAddress);
return $tokenSent->execute() ? (bool)$tokenSent->fetchColumn() : false;
}
function user_recovery_token_validate(int $userId, string $token): bool
{
$validateToken = db_prepare('
SELECT COUNT(`user_id`) > 0
FROM `msz_users_password_resets`
WHERE `user_id` = :user
AND `verification_code` = :code
AND `verification_code` IS NOT NULL
AND `reset_requested` > NOW() - INTERVAL 1 HOUR
');
$validateToken->bindValue('user', $userId);
$validateToken->bindValue('code', $token);
return $validateToken->execute() ? (bool)$validateToken->fetchColumn() : false;
}
function user_recovery_token_generate(): string
{
return bin2hex(random_bytes(MSZ_USER_RECOVERY_TOKEN_LENGTH));
}
function user_recovery_token_create(int $userId, string $ipAddress): string
{
$code = user_recovery_token_generate();
$insertResetKey = db_prepare('
REPLACE INTO `msz_users_password_resets`
(`user_id`, `reset_ip`, `verification_code`)
VALUES
(:user, INET6_ATON(:ip), :code)
');
$insertResetKey->bindValue('user', $userId);
$insertResetKey->bindValue('ip', $ipAddress);
$insertResetKey->bindValue('code', $code);
return $insertResetKey->execute() ? $code : '';
}
function user_recovery_token_invalidate(int $userId, string $token): void
{
$invalidateCode = db_prepare('
UPDATE `msz_users_password_resets`
SET `verification_code` = NULL
WHERE `verification_code` = :code
AND `user_id` = :user
');
$invalidateCode->bindValue('user', $userId);
$invalidateCode->bindValue('code', $token);
$invalidateCode->execute();
}

View file

@ -57,6 +57,18 @@ function user_password_hash(string $password): string
return password_hash($password, MSZ_USERS_PASSWORD_HASH_ALGO);
}
function user_password_set(int $userId, string $password): bool
{
$updatePassword = db_prepare('
UPDATE `msz_users`
SET `password` = :password
WHERE `user_id` = :user
');
$updatePassword->bindValue('user', $userId);
$updatePassword->bindValue('password', user_password_hash($password));
return $updatePassword->execute();
}
// function of the century, only use this if it doesn't make sense to grab data otherwise
function user_exists(int $userId): bool
{