misuzu/public-legacy/auth/login.php

172 lines
6.2 KiB
PHP

<?php
namespace Misuzu;
use Exception;
use Misuzu\Auth\AuthTokenCookie;
if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
die('Script must be called through the Misuzu route dispatcher.');
if($msz->authInfo->loggedIn) {
Tools::redirect($msz->urls->format('index'));
return;
}
if(!empty($_GET['resolve'])) {
header('Content-Type: application/json; charset=utf-8');
try {
// Only works for usernames, this is by design
$userInfo = $msz->usersCtx->users->getUser((string)filter_input(INPUT_GET, 'name'), 'name');
} catch(Exception $ex) {
echo json_encode([
'id' => 0,
'name' => '',
'avatar' => $msz->urls->format('user-avatar', ['res' => 200, 'user' => 0]),
]);
return;
}
echo json_encode([
'id' => (int)$userInfo->id,
'name' => $userInfo->name,
'avatar' => $msz->urls->format('user-avatar', ['user' => $userInfo->id, 'res' => 200]),
]);
return;
}
$notices = [];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$remainingAttempts = $msz->authCtx->loginAttempts->countRemainingAttempts($ipAddress);
$siteIsPrivate = $msz->config->getBoolean('private.enable');
if($siteIsPrivate) {
[
'private.perm.cat' => $loginPermCat,
'private.perm.val' => $loginPermVal,
'private.msg' => $sitePrivateMessage,
'private.allow_password_reset' => $canResetPassword,
] = $msz->config->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 = $msz->usersCtx->users->getUser($_POST['login']['username'], 'login');
} catch(Exception $ex) {
$msz->authCtx->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->deleted || !$userInfo->verifyPassword($_POST['login']['password'])) {
$msz->authCtx->loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
$notices[] = $loginFailedError;
break;
}
if($userInfo->passwordNeedsRehash)
$msz->usersCtx->users->updateUser($userInfo, password: $_POST['login']['password']);
if(!empty($loginPermCat) && $loginPermVal > 0 && !$msz->perms->checkPermissions($loginPermCat, $loginPermVal, $userInfo)) {
$notices[] = "Login succeeded, but you're not allowed to browse the site right now.";
$msz->authCtx->loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
break;
}
if($msz->usersCtx->totps->hasUserTotp($userInfo)) {
$tfaToken = $msz->authCtx->tfaSessions->createToken($userInfo);
Tools::redirect($msz->urls->format('auth-two-factor', ['token' => $tfaToken, 'redirect' => $loginRedirect]));
return;
}
$msz->authCtx->loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
try {
$sessionInfo = $msz->authCtx->sessions->createSession($userInfo, $ipAddress, $countryCode, $userAgent, $clientInfo);
} catch(Exception $ex) {
$notices[] = "Something broke while creating a session for you, please tell an administrator or developer about this!";
break;
}
$tokenBuilder = $msz->authInfo->tokenInfo->toBuilder();
$tokenBuilder->setUserId($userInfo);
$tokenBuilder->setSessionToken($sessionInfo);
$tokenBuilder->removeImpersonatedUserId();
$tokenInfo = $tokenBuilder->toInfo();
AuthTokenCookie::apply($msz->authCtx->createAuthTokenPacker()->pack($tokenInfo));
if(!Tools::isLocalURL($loginRedirect))
$loginRedirect = $msz->urls->format('index');
Tools::redirect($loginRedirect);
return;
}
$welcomeMode = !empty($_GET['welcome']);
$oauth2Mode = !empty($_GET['oauth2']);
$loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login']['username']) ? $_POST['login']['username'] : (
!empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : ''
);
$loginRedirect = $welcomeMode ? $msz->urls->format('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? $msz->urls->format('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_oauth2' => $oauth2Mode,
'login_private' => [
'enabled' => $siteIsPrivate,
'message' => $sitePrivateMessage,
],
]);