misuzu/public-legacy/auth/login.php
flash 383e2ed0e0 Rewrote the user information class.
This one took multiple days and it pretty invasive into the core of Misuzu so issue might (will) arise, there's also some features that have gone temporarily missing in the mean time and some inefficiencies introduced that will be fixed again at a later time.
The old class isn't gone entirely because I still have to figure out what I'm gonna do about validation, but for the most part this knocks out one of the "layers of backwards compatibility", as I've been referring to it, and is moving us closer to a future where Flashii actually gets real updates.
If you run into anything that's broken and you're inhibited from reporting it through the forum, do it through chat or mail me at flashii-issues@flash.moe.
2023-08-02 22:12:47 +00:00

168 lines
5.5 KiB
PHP

<?php
namespace Misuzu;
use Exception;
if($msz->isLoggedIn()) {
url_redirect('index');
return;
}
$users = $msz->getUsers();
$sessions = $msz->getSessions();
$loginAttempts = $msz->getLoginAttempts();
if(!empty($_GET['resolve'])) {
header('Content-Type: application/json; charset=utf-8');
try {
// Only works for usernames, this is by design
$userInfo = $users->getUser((string)filter_input(INPUT_GET, 'name'), 'name');
} catch(Exception $ex) {
echo json_encode([
'id' => 0,
'name' => '',
'avatar' => url('user-avatar', ['res' => 200, 'user' => 0]),
]);
return;
}
echo json_encode([
'id' => (int)$userInfo->getId(),
'name' => $userInfo->getName(),
'avatar' => url('user-avatar', ['user' => $userInfo->getId(), 'res' => 200]),
]);
return;
}
$notices = [];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$remainingAttempts = $loginAttempts->countRemainingAttempts($ipAddress);
$siteIsPrivate = $cfg->getBoolean('private.enable');
if($siteIsPrivate) {
[
'private.perm.cat' => $loginPermCat,
'private.perm.val' => $loginPermVal,
'private.msg' => $sitePrivateMessage,
'private.allow_password_reset' => $canResetPassword,
] = $cfg->getValues([
'private.perm.cat:s',
'private.perm.val:i',
'private.msg:s',
['private.allow_password_reset:b', true],
]);
} else {
$loginPermCat = '';
$loginPermVal = 0;
$sitePrivateMessage = '';
$canResetPassword = true;
}
while(!empty($_POST['login']) && is_array($_POST['login'])) {
if(!CSRF::validateRequest()) {
$notices[] = 'Was unable to verify the request, please try again!';
break;
}
$loginRedirect = empty($_POST['login']['redirect']) || !is_string($_POST['login']['redirect']) ? '' : $_POST['login']['redirect'];
if(empty($_POST['login']['username']) || empty($_POST['login']['password'])
|| !is_string($_POST['login']['username']) || !is_string($_POST['login']['password'])) {
$notices[] = "You didn't fill in a username and/or password.";
break;
}
if($remainingAttempts < 1) {
$notices[] = "There are too many failed login attempts from your IP address, please try again later.";
break;
}
$clientInfo = ClientInfo::fromRequest();
$attemptsRemainingError = sprintf(
"%d attempt%s remaining",
$remainingAttempts - 1,
$remainingAttempts === 2 ? '' : 's'
);
$loginFailedError = "Invalid username or password, {$attemptsRemainingError}.";
try {
$userInfo = $users->getUser($_POST['login']['username'], 'login');
} catch(RuntimeException $ex) {
$loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo);
$notices[] = $loginFailedError;
break;
}
if(!$userInfo->hasPasswordHash()) {
$notices[] = 'Your password has been invalidated, please reset it.';
break;
}
if($userInfo->isDeleted() || !$userInfo->verifyPassword($_POST['login']['password'])) {
$loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
$notices[] = $loginFailedError;
break;
}
if($userInfo->passwordNeedsRehash())
$users->updateUser($userInfo, password: $_POST['login']['password']);
if(!empty($loginPermCat) && $loginPermVal > 0 && !perms_check_user($loginPermCat, $userInfo->getId(), $loginPermVal)) {
$notices[] = "Login succeeded, but you're not allowed to browse the site right now.";
$loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
break;
}
if($userInfo->hasTOTPKey()) {
$tfaToken = $msz->getTFASessions()->createToken($userInfo);
url_redirect('auth-two-factor', [
'token' => $tfaToken,
]);
return;
}
$loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
try {
$sessionInfo = $sessions->createSession($userInfo, $ipAddress, $countryCode, $userAgent, $clientInfo);
} catch(RuntimeException $ex) {
$notices[] = "Something broke while creating a session for you, please tell an administrator or developer about this!";
break;
}
$authToken = AuthToken::create($userInfo, $sessionInfo);
$authToken->applyCookie($sessionInfo->getExpiresTime());
if(!is_local_url($loginRedirect))
$loginRedirect = url('index');
redirect($loginRedirect);
return;
}
$welcomeMode = !empty($_GET['welcome']);
$loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login']['username']) ? $_POST['login']['username'] : (
!empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : ''
);
$loginRedirect = $welcomeMode ? url('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? url('index');
$canRegisterAccount = !$siteIsPrivate;
Template::render('auth.login', [
'login_notices' => $notices,
'login_username' => $loginUsername,
'login_redirect' => $loginRedirect,
'login_can_reset_password' => $canResetPassword,
'login_can_register' => $canRegisterAccount,
'login_attempts_remaining' => $remainingAttempts,
'login_welcome' => $welcomeMode,
'login_private' => [
'enabled' => $siteIsPrivate,
'message' => $sitePrivateMessage,
],
]);