146 lines
5.3 KiB
PHP
146 lines
5.3 KiB
PHP
<?php
|
|
namespace Misuzu;
|
|
|
|
use RuntimeException;
|
|
use Misuzu\Users\User;
|
|
|
|
if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|
die('Script must be called through the Misuzu route dispatcher.');
|
|
|
|
if($msz->authInfo->isLoggedIn) {
|
|
Tools::redirect($msz->urls->format('settings-account'));
|
|
return;
|
|
}
|
|
|
|
$reset = !empty($_POST['reset']) && is_array($_POST['reset']) ? $_POST['reset'] : [];
|
|
$forgot = !empty($_POST['forgot']) && is_array($_POST['forgot']) ? $_POST['forgot'] : [];
|
|
$userId = !empty($reset['user']) ? (int)$reset['user'] : (
|
|
!empty($_GET['user']) ? (int)$_GET['user'] : 0
|
|
);
|
|
|
|
if($userId > 0)
|
|
try {
|
|
$userInfo = $msz->usersCtx->users->getUser((string)$userId, 'id');
|
|
} catch(RuntimeException $ex) {
|
|
Tools::redirect($msz->urls->format('auth-forgot'));
|
|
return;
|
|
}
|
|
|
|
$notices = [];
|
|
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
|
$siteIsPrivate = $msz->config->getBoolean('private.enable');
|
|
$canResetPassword = $siteIsPrivate ? $msz->config->getBoolean('private.allow_password_reset', true) : true;
|
|
|
|
$remainingAttempts = $msz->authCtx->loginAttempts->countRemainingAttempts($ipAddress);
|
|
|
|
while($canResetPassword) {
|
|
if(!empty($reset) && $userId > 0) {
|
|
if(!CSRF::validateRequest()) {
|
|
$notices[] = 'Was unable to verify the request, please try again!';
|
|
break;
|
|
}
|
|
|
|
$verifyCode = !empty($reset['verification']) && is_string($reset['verification']) ? $reset['verification'] : '';
|
|
|
|
try {
|
|
$tokenInfo = $msz->authCtx->recoveryTokens->getToken(verifyCode: $verifyCode);
|
|
} catch(RuntimeException $ex) {
|
|
unset($tokenInfo);
|
|
}
|
|
|
|
if(empty($tokenInfo) || !$tokenInfo->isValid || $tokenInfo->userId !== (string)$userInfo->id) {
|
|
$notices[] = 'Invalid verification code!';
|
|
break;
|
|
}
|
|
|
|
$password = !empty($reset['password']) && is_array($reset['password']) ? $reset['password'] : [];
|
|
$passwordNew = !empty($password['new']) && is_string($password['new']) ? $password['new'] : '';
|
|
$passwordConfirm = !empty($password['confirm']) && is_string($password['confirm']) ? $password['confirm'] : '';
|
|
|
|
if(empty($passwordNew) || empty($passwordConfirm)
|
|
|| $passwordNew !== $passwordConfirm) {
|
|
$notices[] = "Password confirmation failed!";
|
|
break;
|
|
}
|
|
|
|
$passwordValidation = $msz->usersCtx->users->validatePassword($passwordNew);
|
|
if($passwordValidation !== '') {
|
|
$notices[] = $msz->usersCtx->users->validatePasswordText($passwordValidation);
|
|
break;
|
|
}
|
|
|
|
// also disables two factor auth to prevent getting locked out of account entirely
|
|
// this behaviour should really be replaced with recovery keys...
|
|
$msz->usersCtx->users->updateUser($userInfo, password: $passwordNew, totpKey: '');
|
|
|
|
$msz->createAuditLog('PASSWORD_RESET', [], $userInfo);
|
|
|
|
$msz->authCtx->recoveryTokens->invalidateToken($tokenInfo);
|
|
|
|
Tools::redirect($msz->urls->format('auth-login', ['redirect' => '/']));
|
|
return;
|
|
}
|
|
|
|
if(!empty($forgot)) {
|
|
if(!CSRF::validateRequest()) {
|
|
$notices[] = 'Was unable to verify the request, please try again!';
|
|
break;
|
|
}
|
|
|
|
if(empty($forgot['email']) || !is_string($forgot['email'])) {
|
|
$notices[] = "You didn't supply an e-mail address.";
|
|
break;
|
|
}
|
|
|
|
if($remainingAttempts < 1) {
|
|
$notices[] = "There are too many failed login attempts from your IP address, please try again later.";
|
|
break;
|
|
}
|
|
|
|
try {
|
|
$forgotUser = $msz->usersCtx->users->getUser($forgot['email'], 'email');
|
|
} catch(RuntimeException $ex) {
|
|
unset($forgotUser);
|
|
}
|
|
|
|
if(empty($forgotUser) || $forgotUser->deleted) {
|
|
$notices[] = "This e-mail address is not registered with us.";
|
|
break;
|
|
}
|
|
|
|
try {
|
|
$tokenInfo = $msz->authCtx->recoveryTokens->getToken(userInfo: $forgotUser, remoteAddr: $ipAddress);
|
|
} catch(RuntimeException $ex) {
|
|
$tokenInfo = $msz->authCtx->recoveryTokens->createToken($forgotUser, $ipAddress);
|
|
|
|
$recoveryMessage = Mailer::template('password-recovery', [
|
|
'username' => $forgotUser->name,
|
|
'token' => $tokenInfo->code,
|
|
]);
|
|
|
|
$recoveryMail = Mailer::sendMessage(
|
|
[$forgotUser->emailAddress => $forgotUser->name],
|
|
$recoveryMessage['subject'], $recoveryMessage['message']
|
|
);
|
|
|
|
if(!$recoveryMail) {
|
|
$notices[] = "Failed to send reset email, please contact the administrator.";
|
|
$msz->authCtx->recoveryTokens->invalidateToken($tokenInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Tools::redirect($msz->urls->format('auth-reset', ['user' => $forgotUser->id]));
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Template::render(isset($userInfo) ? 'auth.password_reset' : 'auth.password_forgot', [
|
|
'password_notices' => $notices,
|
|
'password_email' => !empty($forget['email']) && is_string($forget['email']) ? $forget['email'] : '',
|
|
'password_attempts_remaining' => $remainingAttempts,
|
|
'password_user' => $userInfo ?? null,
|
|
'password_verification' => $verifyCode ?? '',
|
|
]);
|