From 1785aae6eb797a5dfeafca3a07c1b9f62b26c51b Mon Sep 17 00:00:00 2001 From: flashwave Date: Mon, 8 Oct 2018 14:59:58 +0200 Subject: [PATCH] Moved password recovery code into its own file. --- misuzu.php | 1 + public/auth.php | 68 ++++----------------------------------- src/Users/recovery.php | 72 ++++++++++++++++++++++++++++++++++++++++++ src/Users/user.php | 12 +++++++ 4 files changed, 92 insertions(+), 61 deletions(-) create mode 100644 src/Users/recovery.php diff --git a/misuzu.php b/misuzu.php index a966289b..90a8eda1 100644 --- a/misuzu.php +++ b/misuzu.php @@ -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'; diff --git a/public/auth.php b/public/auth.php index af698865..d6f8b545 100644 --- a/public/auth.php +++ b/public/auth.php @@ -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; } } diff --git a/src/Users/recovery.php b/src/Users/recovery.php new file mode 100644 index 00000000..a90bf110 --- /dev/null +++ b/src/Users/recovery.php @@ -0,0 +1,72 @@ + 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(); +} diff --git a/src/Users/user.php b/src/Users/user.php index 76a528f0..ebf38341 100644 --- a/src/Users/user.php +++ b/src/Users/user.php @@ -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 {