2022-09-13 13:14:49 +00:00
|
|
|
<?php
|
|
|
|
namespace Misuzu;
|
|
|
|
|
2023-08-02 22:12:47 +00:00
|
|
|
use Exception;
|
2023-08-03 01:35:08 +00:00
|
|
|
use Misuzu\Auth\AuthTokenCookie;
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-09-08 20:40:48 +00:00
|
|
|
$urls = $msz->getURLs();
|
2023-09-06 20:06:07 +00:00
|
|
|
$authInfo = $msz->getAuthInfo();
|
|
|
|
if($authInfo->isLoggedIn()) {
|
2023-09-08 20:40:48 +00:00
|
|
|
Tools::redirect($urls->format('index'));
|
2022-09-13 13:14:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-08 00:43:00 +00:00
|
|
|
$authCtx = $msz->getAuthContext();
|
2023-09-06 13:50:19 +00:00
|
|
|
$users = $msz->getUsersContext()->getUsers();
|
2023-09-08 00:43:00 +00:00
|
|
|
$sessions = $authCtx->getSessions();
|
|
|
|
$loginAttempts = $authCtx->getLoginAttempts();
|
2023-08-02 22:12:47 +00:00
|
|
|
|
2022-09-13 13:14:49 +00:00
|
|
|
if(!empty($_GET['resolve'])) {
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Only works for usernames, this is by design
|
2023-08-02 22:12:47 +00:00
|
|
|
$userInfo = $users->getUser((string)filter_input(INPUT_GET, 'name'), 'name');
|
|
|
|
} catch(Exception $ex) {
|
2022-09-13 13:14:49 +00:00
|
|
|
echo json_encode([
|
|
|
|
'id' => 0,
|
|
|
|
'name' => '',
|
2023-09-08 20:40:48 +00:00
|
|
|
'avatar' => $urls->format('user-avatar', ['res' => 200, 'user' => 0]),
|
2022-09-13 13:14:49 +00:00
|
|
|
]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
echo json_encode([
|
2023-08-02 22:12:47 +00:00
|
|
|
'id' => (int)$userInfo->getId(),
|
|
|
|
'name' => $userInfo->getName(),
|
2023-09-08 20:40:48 +00:00
|
|
|
'avatar' => $urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => 200]),
|
2022-09-13 13:14:49 +00:00
|
|
|
]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$notices = [];
|
2023-01-05 18:33:03 +00:00
|
|
|
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
2023-07-11 00:25:43 +00:00
|
|
|
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
|
2023-07-22 16:37:57 +00:00
|
|
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
|
|
|
|
|
|
|
$remainingAttempts = $loginAttempts->countRemainingAttempts($ipAddress);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-07-18 21:48:44 +00:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
2022-09-13 13:14:49 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-07-28 20:06:12 +00:00
|
|
|
$clientInfo = ClientInfo::fromRequest();
|
|
|
|
|
2022-09-13 13:14:49 +00:00
|
|
|
$attemptsRemainingError = sprintf(
|
|
|
|
"%d attempt%s remaining",
|
|
|
|
$remainingAttempts - 1,
|
|
|
|
$remainingAttempts === 2 ? '' : 's'
|
|
|
|
);
|
|
|
|
$loginFailedError = "Invalid username or password, {$attemptsRemainingError}.";
|
|
|
|
|
|
|
|
try {
|
2023-08-02 22:12:47 +00:00
|
|
|
$userInfo = $users->getUser($_POST['login']['username'], 'login');
|
2023-08-06 18:22:39 +00:00
|
|
|
} catch(Exception $ex) {
|
2023-07-28 20:06:12 +00:00
|
|
|
$loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo);
|
2022-09-13 13:14:49 +00:00
|
|
|
$notices[] = $loginFailedError;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-08-02 22:12:47 +00:00
|
|
|
if(!$userInfo->hasPasswordHash()) {
|
2022-09-13 13:14:49 +00:00
|
|
|
$notices[] = 'Your password has been invalidated, please reset it.';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-08-02 22:12:47 +00:00
|
|
|
if($userInfo->isDeleted() || !$userInfo->verifyPassword($_POST['login']['password'])) {
|
2023-07-28 20:06:12 +00:00
|
|
|
$loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
|
2022-09-13 13:14:49 +00:00
|
|
|
$notices[] = $loginFailedError;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if($userInfo->passwordNeedsRehash())
|
2023-08-02 22:12:47 +00:00
|
|
|
$users->updateUser($userInfo, password: $_POST['login']['password']);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-08-30 22:37:21 +00:00
|
|
|
if(!empty($loginPermCat) && $loginPermVal > 0 && !$msz->getPerms()->checkPermissions($loginPermCat, $loginPermVal, $userInfo)) {
|
2022-09-13 13:14:49 +00:00
|
|
|
$notices[] = "Login succeeded, but you're not allowed to browse the site right now.";
|
2023-07-28 20:06:12 +00:00
|
|
|
$loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
|
2022-09-13 13:14:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-07-29 20:18:41 +00:00
|
|
|
if($userInfo->hasTOTPKey()) {
|
2023-09-08 00:43:00 +00:00
|
|
|
$tfaToken = $authCtx->getTwoFactorAuthSessions()->createToken($userInfo);
|
2023-09-08 20:40:48 +00:00
|
|
|
Tools::redirect($urls->format('auth-two-factor', ['token' => $tfaToken]));
|
2022-09-13 13:14:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-28 20:06:12 +00:00
|
|
|
$loginAttempts->recordAttempt(true, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
|
|
|
try {
|
2023-07-28 20:06:12 +00:00
|
|
|
$sessionInfo = $sessions->createSession($userInfo, $ipAddress, $countryCode, $userAgent, $clientInfo);
|
2023-08-06 18:22:39 +00:00
|
|
|
} catch(Exception $ex) {
|
2022-09-13 13:14:49 +00:00
|
|
|
$notices[] = "Something broke while creating a session for you, please tell an administrator or developer about this!";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-09-06 20:06:07 +00:00
|
|
|
$tokenBuilder = $authInfo->getTokenInfo()->toBuilder();
|
2023-08-03 01:35:08 +00:00
|
|
|
$tokenBuilder->setUserId($userInfo);
|
|
|
|
$tokenBuilder->setSessionToken($sessionInfo);
|
|
|
|
$tokenBuilder->removeImpersonatedUserId();
|
|
|
|
$tokenInfo = $tokenBuilder->toInfo();
|
|
|
|
|
|
|
|
AuthTokenCookie::apply($tokenPacker->pack($tokenInfo));
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-09-08 20:40:48 +00:00
|
|
|
if(!Tools::isLocalURL($loginRedirect))
|
|
|
|
$loginRedirect = $urls->format('index');
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-09-08 20:40:48 +00:00
|
|
|
Tools::redirect($loginRedirect);
|
2022-09-13 13:14:49 +00:00
|
|
|
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'] : ''
|
|
|
|
);
|
2023-09-08 20:40:48 +00:00
|
|
|
$loginRedirect = $welcomeMode ? $urls->format('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? $urls->format('index');
|
2022-09-13 13:14:49 +00:00
|
|
|
$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,
|
|
|
|
],
|
|
|
|
]);
|