Split auth.php up into multiple files.
This commit is contained in:
parent
4d47d290bf
commit
9c739e1062
31 changed files with 811 additions and 692 deletions
|
@ -1,49 +0,0 @@
|
|||
.auth {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 2px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
&__warning {
|
||||
margin: 2px 5px 0;
|
||||
|
||||
&--welcome {
|
||||
--start-colour: var(--accent-colour);
|
||||
--end-colour: #222;
|
||||
}
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
&__paragraph,
|
||||
&__input {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
&__form {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
&__button {
|
||||
flex: 1 1 auto;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
}
|
14
assets/less/classes/auth/buttons.less
Normal file
14
assets/less/classes/auth/buttons.less
Normal file
|
@ -0,0 +1,14 @@
|
|||
.auth__buttons {
|
||||
margin: 5px;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
|
||||
&__button {
|
||||
&--minor {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
4
assets/less/classes/auth/container.less
Normal file
4
assets/less/classes/auth/container.less
Normal file
|
@ -0,0 +1,4 @@
|
|||
.auth__container {
|
||||
margin: 2px auto;
|
||||
max-width: 400px;
|
||||
}
|
39
assets/less/classes/auth/label.less
Normal file
39
assets/less/classes/auth/label.less
Normal file
|
@ -0,0 +1,39 @@
|
|||
.auth__label {
|
||||
overflow: hidden;
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
|
||||
&__text {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
&__value {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__action {
|
||||
padding: 3px 8px;
|
||||
display: block;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-radius: 4px;
|
||||
transition: background-color .2s;
|
||||
margin: 2px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: fade(#fff, 20%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: fade(#fff, 10%);
|
||||
}
|
||||
}
|
||||
}
|
19
assets/less/classes/auth/login.less
Normal file
19
assets/less/classes/auth/login.less
Normal file
|
@ -0,0 +1,19 @@
|
|||
.auth__login {
|
||||
|
||||
&--disabled {
|
||||
--accent-colour: #555;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
7
assets/less/classes/auth/logout.less
Normal file
7
assets/less/classes/auth/logout.less
Normal file
|
@ -0,0 +1,7 @@
|
|||
.auth__logout {
|
||||
margin: 5px;
|
||||
|
||||
&__paragraph {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
37
assets/less/classes/auth/register.less
Normal file
37
assets/less/classes/auth/register.less
Normal file
|
@ -0,0 +1,37 @@
|
|||
.auth__register {
|
||||
max-width: 700px;
|
||||
width: 100%;
|
||||
|
||||
&__container {
|
||||
display: flex;
|
||||
|
||||
@media (max-width: @site-mobile-width) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
&__form {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__info {
|
||||
max-width: 300px;
|
||||
width: 100%;
|
||||
flex: 0 1 auto;
|
||||
padding: 5px;
|
||||
|
||||
@media (max-width: @site-mobile-width) {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__paragraph {
|
||||
line-height: 1.5em;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
&__link {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
12
assets/less/classes/auth/warning.less
Normal file
12
assets/less/classes/auth/warning.less
Normal file
|
@ -0,0 +1,12 @@
|
|||
.auth__warning {
|
||||
margin: 5px;
|
||||
|
||||
&--welcome {
|
||||
--start-colour: var(--accent-colour);
|
||||
--end-colour: #222;
|
||||
}
|
||||
|
||||
&__paragraph {
|
||||
line-height: 2em;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
min-width: 80px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: color .2s, background-color .2s, opacity .2s;
|
||||
transition: color .2s, background-color .2s, opacity .2s, border-color .2s;
|
||||
color: var(--accent-colour);
|
||||
border: 1px solid var(--accent-colour);
|
||||
border-radius: 2px;
|
||||
|
@ -21,6 +21,7 @@
|
|||
&:focus {
|
||||
color: #111;
|
||||
background-color: var(--accent-colour);
|
||||
border-color: var(--accent-colour);
|
||||
}
|
||||
|
||||
&--busy {
|
||||
|
|
|
@ -121,7 +121,6 @@ html {
|
|||
@import "classes/messagebox";
|
||||
|
||||
// Specific styles
|
||||
@import "classes/auth";
|
||||
@import "classes/header";
|
||||
@import "classes/footer";
|
||||
@import "classes/permissions";
|
||||
|
@ -130,6 +129,15 @@ html {
|
|||
@import "classes/home";
|
||||
@import "classes/landing";
|
||||
|
||||
// Auth
|
||||
@import "classes/auth/buttons";
|
||||
@import "classes/auth/container";
|
||||
@import "classes/auth/label";
|
||||
@import "classes/auth/login";
|
||||
@import "classes/auth/logout";
|
||||
@import "classes/auth/register";
|
||||
@import "classes/auth/warning";
|
||||
|
||||
// Manage
|
||||
@import "classes/manage/manage";
|
||||
@import "classes/manage/navigation";
|
||||
|
|
|
@ -32,15 +32,6 @@ window.addEventListener('load', () => {
|
|||
changelogChangeAction.title = "This is supposed to be sideways, but your browser doesn't support that.";
|
||||
}
|
||||
|
||||
const loginButtons: HTMLCollectionOf<HTMLAnchorElement> = document.getElementsByClassName('js-login-button') as HTMLCollectionOf<HTMLAnchorElement>;
|
||||
|
||||
if (loginButtons.length > 0) {
|
||||
for (let i = 0; i < loginButtons.length; i++) {
|
||||
loginButtons[i].href = 'javascript:void(0);';
|
||||
loginButtons[i].addEventListener('click', () => loginModal());
|
||||
}
|
||||
}
|
||||
|
||||
const loginForms: HTMLCollectionOf<HTMLFormElement> = document.getElementsByClassName('js-login-form') as HTMLCollectionOf<HTMLFormElement>;
|
||||
|
||||
if (loginForms.length > 0) {
|
||||
|
@ -80,7 +71,7 @@ function loginFormUpdateAvatar(avatarElement: HTMLElement, usernameElement: HTML
|
|||
|
||||
avatarElement.style.backgroundImage = "url('{0}')".replace('{0}', urlFormat('user-avatar', [{name: 'user', value: xhr.responseText}]));
|
||||
});
|
||||
xhr.open('GET', urlFormat('auth-resolve-user', [{name: 'username', value: encodeURI(usernameElement.value)}]));
|
||||
xhr.open('GET', urlFormat('auth-resolve-user', [{name: 'username', value: encodeURIComponent(usernameElement.value)}]));
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
|
@ -142,77 +133,3 @@ function messageBox(text: string, title: string = null, buttons: MessageBoxButto
|
|||
firstButton.focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
function loginModal(): boolean {
|
||||
if (document.querySelector('.messagebox') || getCurrentUser('user_id') > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const element: HTMLDivElement = document.createElement('div');
|
||||
element.className = 'messagebox';
|
||||
|
||||
const container: HTMLFormElement = element.appendChild(document.createElement('form'));
|
||||
container.className = 'container messagebox__container auth js-login-form';
|
||||
container.method = 'post';
|
||||
container.action = urlFormat('auth-login');
|
||||
|
||||
const titleElement = container.appendChild(document.createElement('div')),
|
||||
titleBackground = titleElement.appendChild(document.createElement('div')),
|
||||
titleHeader = titleElement.appendChild(document.createElement('div'));
|
||||
|
||||
titleElement.className = 'container__title';
|
||||
titleBackground.className = 'container__title__background';
|
||||
titleHeader.className = 'auth__header';
|
||||
|
||||
const authAvatar: HTMLDivElement = titleHeader.appendChild(document.createElement('div'));
|
||||
authAvatar.className = 'avatar auth__avatar';
|
||||
authAvatar.style.backgroundImage = "url('{0}')".replace('{0}', urlFormat('user-avatar'));
|
||||
|
||||
const hiddenMode: HTMLInputElement = container.appendChild(document.createElement('input'));
|
||||
hiddenMode.type = 'hidden';
|
||||
hiddenMode.name = 'auth[mode]';
|
||||
hiddenMode.value = 'login';
|
||||
|
||||
const hiddenCsrf: HTMLInputElement = container.appendChild(document.createElement('input'));
|
||||
hiddenCsrf.type = 'hidden';
|
||||
hiddenCsrf.name = 'csrf[login]';
|
||||
hiddenCsrf.value = getCSRFToken('login');
|
||||
|
||||
const hiddenRedirect: HTMLInputElement = container.appendChild(document.createElement('input'));
|
||||
hiddenRedirect.type = 'hidden';
|
||||
hiddenRedirect.name = 'auth[redirect]';
|
||||
hiddenRedirect.value = location.toString();
|
||||
|
||||
const authForm: HTMLDivElement = container.appendChild(document.createElement('div'));
|
||||
authForm.className = 'auth__form';
|
||||
|
||||
const inputUsername: HTMLInputElement = authForm.appendChild(document.createElement('input'));
|
||||
inputUsername.className = 'input__text auth__input';
|
||||
inputUsername.placeholder = 'Username';
|
||||
inputUsername.type = 'text';
|
||||
inputUsername.name = 'auth[username]';
|
||||
inputUsername.addEventListener('keyup', () => loginFormUpdateAvatar(authAvatar, inputUsername));
|
||||
|
||||
const inputPassword: HTMLInputElement = authForm.appendChild(document.createElement('input'));
|
||||
inputPassword.className = 'input__text auth__input';
|
||||
inputPassword.placeholder = 'Password';
|
||||
inputPassword.type = 'password';
|
||||
inputPassword.name = 'auth[password]';
|
||||
|
||||
const formButtons: HTMLDivElement = authForm.appendChild(document.createElement('div'));
|
||||
formButtons.className = 'auth__buttons';
|
||||
|
||||
const inputLogin: HTMLButtonElement = formButtons.appendChild(document.createElement('button'));
|
||||
inputLogin.className = 'input__button auth__button';
|
||||
inputLogin.textContent = 'Log in';
|
||||
|
||||
const inputClose: HTMLButtonElement = formButtons.appendChild(document.createElement('button'));
|
||||
inputClose.className = 'input__button auth__button';
|
||||
inputClose.textContent = 'Close';
|
||||
inputClose.type = 'button';
|
||||
inputClose.addEventListener('click', () => element.remove());
|
||||
|
||||
document.body.appendChild(element);
|
||||
inputUsername.focus();
|
||||
return true;
|
||||
}
|
||||
|
|
44
misuzu.php
44
misuzu.php
|
@ -386,16 +386,6 @@ MIG;
|
|||
|
||||
tpl_add_path(MSZ_ROOT . '/templates');
|
||||
|
||||
$misuzuBypassLockdown = !empty($misuzuBypassLockdown);
|
||||
|
||||
if (!$misuzuBypassLockdown && boolval(config_get_default(false, 'Auth', 'lockdown'))) {
|
||||
http_response_code(503);
|
||||
echo tpl_render('auth.lockdown', [
|
||||
'message' => config_get_default(null, 'Auth', 'lockdown_msg'),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (file_exists(MSZ_ROOT . '/.migrating')) {
|
||||
http_response_code(503);
|
||||
echo tpl_render('auth.lockdown', [
|
||||
|
@ -448,32 +438,32 @@ MIG;
|
|||
empty($userDisplayInfo) ? ip_remote_address() : $cookieData['session_token']
|
||||
);
|
||||
|
||||
if (!$misuzuBypassLockdown && boolval(config_get_default(false, 'Private', 'enabled'))) {
|
||||
if (user_session_active()) {
|
||||
$privatePermission = intval(config_get_default(0, 'Private', 'permission'));
|
||||
if (config_get_default(false, 'Private', 'enabled')) {
|
||||
$onLoginPage = $_SERVER['PHP_SELF'] === url('auth-login');
|
||||
$onPasswordPage = parse_url($_SERVER['PHP_SELF'], PHP_URL_PATH) === url('auth-forgot');
|
||||
$misuzuBypassLockdown = !empty($misuzuBypassLockdown) || $onLoginPage;
|
||||
|
||||
if ($privatePermission > 0) {
|
||||
$generalPerms = perms_get_user(MSZ_PERMS_GENERAL, $userDisplayInfo['user_id']);
|
||||
if (!$misuzuBypassLockdown) {
|
||||
if (user_session_active()) {
|
||||
$privatePermission = (int)config_get_default(0, 'Private', 'permission');
|
||||
|
||||
if (!perms_check($generalPerms, $privatePermission)) {
|
||||
unset($userDisplayInfo);
|
||||
user_session_stop(); // au revoir
|
||||
if ($privatePermission > 0) {
|
||||
$generalPerms = perms_get_user(MSZ_PERMS_GENERAL, $userDisplayInfo['user_id']);
|
||||
|
||||
if (!perms_check($generalPerms, $privatePermission)) {
|
||||
unset($userDisplayInfo);
|
||||
user_session_stop(); // au revoir
|
||||
}
|
||||
}
|
||||
} elseif (!$onLoginPage && !($onPasswordPage && config_get_default(false, 'Private', 'password_reset'))) {
|
||||
header(sprintf('Location: %s', url('auth-login')));
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
http_response_code(401);
|
||||
echo tpl_render('auth.private', [
|
||||
'private_message'=> config_get_default('', 'Private', 'message'),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($userDisplayInfo)) {
|
||||
tpl_var('current_user', $userDisplayInfo);
|
||||
} else {
|
||||
// make sure the login csrf token is available
|
||||
csrf_token('login');
|
||||
}
|
||||
|
||||
$inManageMode = starts_with($_SERVER['REQUEST_URI'], '/manage');
|
||||
|
|
345
public/auth.php
345
public/auth.php
|
@ -1,358 +1,27 @@
|
|||
<?php
|
||||
$isSubmission = !empty($_POST['auth']) && is_array($_POST['auth']);
|
||||
$authMode = $isSubmission ? ($_POST['auth']['mode'] ?? '') : ($_GET['m'] ?? 'login');
|
||||
$misuzuBypassLockdown = $authMode === 'login' || $authMode === 'get_user';
|
||||
// Delete this file in April 2019
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
$siteIsPrivate = boolval(config_get_default(false, 'Private', 'enabled'));
|
||||
$loginPermission = $siteIsPrivate ? intval(config_get_default(0, 'Private', 'permission')) : 0;
|
||||
$canResetPassword = $siteIsPrivate ? boolval(config_get_default(false, 'Private', 'password_reset')) : true;
|
||||
$canCreateAccount = !$siteIsPrivate && !boolval(config_get_default(false, 'Auth', 'lockdown'));
|
||||
|
||||
$authUsername = $isSubmission ? ($_POST['auth']['username'] ?? '') : ($_GET['username'] ?? '');
|
||||
$authEmail = $isSubmission ? ($_POST['auth']['email'] ?? '') : ($_GET['email'] ?? '');
|
||||
$authPassword = $_POST['auth']['password'] ?? '';
|
||||
$authVerification = $_POST['auth']['verification'] ?? '';
|
||||
$authRedirect = $_POST['auth']['redirect'] ?? $_GET['redirect'] ?? $_SERVER['HTTP_REFERER'] ?? '/';
|
||||
$authRestricted = ip_blacklist_check(ip_remote_address())
|
||||
? 1
|
||||
: (
|
||||
user_warning_check_ip(ip_remote_address())
|
||||
? 2
|
||||
: 0
|
||||
);
|
||||
|
||||
tpl_vars([
|
||||
'can_create_account' => $canCreateAccount,
|
||||
'can_reset_password' => $canResetPassword,
|
||||
'auth_mode' => $authMode,
|
||||
'auth_username' => $authUsername,
|
||||
'auth_email' => $authEmail,
|
||||
'auth_redirect' => $authRedirect,
|
||||
'auth_restricted' => $authRestricted,
|
||||
]);
|
||||
|
||||
switch ($authMode) {
|
||||
case 'get_user':
|
||||
echo user_id_from_username($_GET['u'] ?? '');
|
||||
break;
|
||||
|
||||
switch ($_GET['m'] ?? '') {
|
||||
case 'logout':
|
||||
if (!user_session_active()) {
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
return;
|
||||
}
|
||||
|
||||
if (csrf_verify('logout', $_GET['s'] ?? '')) {
|
||||
setcookie('msz_auth', '', -3600, '/', '', true, true);
|
||||
user_session_stop(true);
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
return;
|
||||
}
|
||||
|
||||
echo tpl_render('auth.logout');
|
||||
break;
|
||||
|
||||
case 'reset':
|
||||
// If we're logged in, redirect to the password/e-mail change part in settings instead.
|
||||
if (user_session_active()) {
|
||||
header(sprintf('Location: %s', url('settings-mode', ['mode' => 'account'])));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$canResetPassword) {
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
return;
|
||||
}
|
||||
|
||||
$resetUserId = (int)($_POST['user'] ?? $_GET['u'] ?? 0);
|
||||
|
||||
if (empty($resetUserId)) {
|
||||
header(sprintf('Location: %s', url('auth-forgot')));
|
||||
break;
|
||||
}
|
||||
|
||||
$resetUsername = user_username_from_id($resetUserId);
|
||||
|
||||
if (empty($resetUsername)) {
|
||||
header(sprintf('Location: %s', url('auth-login')));
|
||||
break;
|
||||
}
|
||||
|
||||
tpl_var('auth_reset_message', "A verification code should've been sent to your e-mail address.");
|
||||
|
||||
while ($isSubmission) {
|
||||
if (!csrf_verify('passreset', $_POST['csrf'] ?? '')) {
|
||||
tpl_var('auth_reset_error', 'Possible request forgery detected, refresh and try again.');
|
||||
break;
|
||||
}
|
||||
|
||||
if (!user_recovery_token_validate($resetUserId, $authVerification)) {
|
||||
tpl_var('auth_reset_error', 'Invalid verification code!');
|
||||
break;
|
||||
}
|
||||
|
||||
tpl_var('reset_verify', $authVerification);
|
||||
|
||||
if (empty($authPassword['new'])
|
||||
|| empty($authPassword['confirm'])
|
||||
|| $authPassword['new'] !== $authPassword['confirm']) {
|
||||
tpl_var('auth_reset_error', 'Your passwords didn\'t match!');
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_validate_password($authPassword['new']) !== '') {
|
||||
tpl_var('auth_reset_error', 'Your password is too weak!');
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_password_set($resetUserId, $authPassword['new'])) {
|
||||
audit_log(MSZ_AUDIT_PASSWORD_RESET, $resetUserId);
|
||||
} else {
|
||||
throw new UnexpectedValueException('Password reset failed.');
|
||||
}
|
||||
|
||||
user_recovery_token_invalidate($resetUserId, $authVerification);
|
||||
|
||||
header(sprintf('Location: %s', url('auth-login')));
|
||||
break;
|
||||
}
|
||||
|
||||
echo tpl_render('auth.password', [
|
||||
'reset_user' => [
|
||||
'user_id' => $resetUserId,
|
||||
'username' => $resetUsername,
|
||||
],
|
||||
]);
|
||||
header('Location: ' . url('auth-reset'));
|
||||
break;
|
||||
|
||||
case 'forgot':
|
||||
if (user_session_active() || !$canResetPassword) {
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
break;
|
||||
}
|
||||
|
||||
while ($isSubmission) {
|
||||
if (!csrf_verify('passforgot', $_POST['csrf'] ?? '')) {
|
||||
tpl_var('auth_forgot_error', 'Possible request forgery detected, refresh and try again.');
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($authEmail)) {
|
||||
tpl_var('auth_forgot_error', 'Please enter an e-mail address.');
|
||||
break;
|
||||
}
|
||||
|
||||
$forgotUser = user_find_for_reset($authEmail);
|
||||
|
||||
if (empty($forgotUser)) {
|
||||
tpl_var('auth_forgot_error', 'This user is not registered with us.');
|
||||
break;
|
||||
}
|
||||
|
||||
$ipAddress = ip_remote_address();
|
||||
|
||||
if (!user_recovery_token_sent($forgotUser['user_id'], $ipAddress)) {
|
||||
$verificationCode = user_recovery_token_create($forgotUser['user_id'], $ipAddress);
|
||||
|
||||
if (empty($verificationCode)) {
|
||||
throw new UnexpectedValueException('A verification code failed to insert.');
|
||||
}
|
||||
|
||||
$messageBody = <<<MSG
|
||||
Hey {$forgotUser['username']},
|
||||
|
||||
You, or someone pretending to be you, has requested a password reset for your account.
|
||||
|
||||
Your verification code is: {$verificationCode}
|
||||
|
||||
If you weren't the person who requested this reset, please send a reply to this e-mail.
|
||||
MSG;
|
||||
|
||||
$message = mail_compose(
|
||||
[$forgotUser['email'] => $forgotUser['username']],
|
||||
'Flashii Password Reset',
|
||||
$messageBody
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
header(sprintf('Location: %s', url('auth-reset', ['user' => $forgotUser['user_id']])));
|
||||
break;
|
||||
}
|
||||
|
||||
echo tpl_render('auth.auth');
|
||||
header('Location: ' . url('auth-forgot'));
|
||||
break;
|
||||
|
||||
case 'login':
|
||||
if (user_session_active()) {
|
||||
header('Location: ' . url('index'));
|
||||
break;
|
||||
}
|
||||
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
$authLoginError = '';
|
||||
|
||||
while ($isSubmission) {
|
||||
$ipAddress = ip_remote_address();
|
||||
|
||||
if (!isset($authUsername, $authPassword)) {
|
||||
$authLoginError = "You didn't fill all the forms!";
|
||||
break;
|
||||
}
|
||||
|
||||
$remainingAttempts = user_login_attempts_remaining($ipAddress);
|
||||
|
||||
if ($remainingAttempts < 1) {
|
||||
$authLoginError = 'Too many failed login attempts, try again later.';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!csrf_verify('login', $_POST['csrf'] ?? '')) {
|
||||
$authLoginError = 'Possible request forgery detected, refresh and try again.';
|
||||
break;
|
||||
}
|
||||
|
||||
$userData = user_find_for_login($authUsername);
|
||||
|
||||
$loginFailedError = sprintf(
|
||||
"Invalid username or password, %d attempt%s remaining.",
|
||||
$remainingAttempts - 1,
|
||||
$remainingAttempts === 2 ? '' : 's'
|
||||
);
|
||||
|
||||
if (empty($userData) || $userData['user_id'] < 1) {
|
||||
user_login_attempt_record(false, null, $ipAddress, $userAgent);
|
||||
$authLoginError = $loginFailedError;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!password_verify($authPassword, $userData['password'])) {
|
||||
user_login_attempt_record(false, $userData['user_id'], $ipAddress, $userAgent);
|
||||
$authLoginError = $loginFailedError;
|
||||
break;
|
||||
}
|
||||
|
||||
user_login_attempt_record(true, $userData['user_id'], $ipAddress, $userAgent);
|
||||
|
||||
if ($loginPermission > 0) {
|
||||
$generalPerms = perms_get_user(MSZ_PERMS_GENERAL, $userData['user_id']);
|
||||
|
||||
if (!perms_check($generalPerms, $loginPermission)) {
|
||||
$authLoginError = 'Your credentials were correct, but your account lacks the proper permissions to use this website.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$sessionKey = user_session_create($userData['user_id'], $ipAddress, $userAgent);
|
||||
|
||||
if ($sessionKey === '') {
|
||||
$authLoginError = 'Unable to create new session, contact an administrator ASAP.';
|
||||
break;
|
||||
}
|
||||
|
||||
user_session_start($userData['user_id'], $sessionKey);
|
||||
$cookieLife = strtotime(user_session_current('session_expires'));
|
||||
$cookieValue = base64url_encode(user_session_cookie_pack($userData['user_id'], $sessionKey));
|
||||
setcookie('msz_auth', $cookieValue, $cookieLife, '/', '', true, true);
|
||||
|
||||
if (!is_local_url($authRedirect)) {
|
||||
$authRedirect = url('index');
|
||||
}
|
||||
|
||||
header("Location: {$authRedirect}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($authLoginError)) {
|
||||
tpl_var('auth_login_error', $authLoginError);
|
||||
} elseif ($siteIsPrivate) {
|
||||
tpl_var('auth_register_message', config_get_default('', 'Private', 'message'));
|
||||
}
|
||||
|
||||
echo tpl_render('auth.auth');
|
||||
default:
|
||||
header('Location: ' . url('auth-login'));
|
||||
break;
|
||||
|
||||
case 'register':
|
||||
if (user_session_active()) {
|
||||
header('Location: ' . url('index'));
|
||||
}
|
||||
|
||||
$authRegistrationError = '';
|
||||
|
||||
while ($isSubmission) {
|
||||
if (!$canCreateAccount || $authRestricted) {
|
||||
$authRegistrationError = 'You may not create an account right now.';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($authUsername, $authPassword, $authEmail)) {
|
||||
$authRegistrationError = "You didn't fill all the forms!";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!csrf_verify('register', $_POST['csrf'] ?? '')) {
|
||||
$authRegistrationError = 'Possible request forgery detected, refresh and try again.';
|
||||
break;
|
||||
}
|
||||
|
||||
$checkSpamBot = mb_strtolower($_POST['auth']['meow'] ?? '');
|
||||
$spamBotValid = [
|
||||
'19', '21', 'nineteen', 'nine-teen', 'nine teen', 'twentyone', 'twenty-one', 'twenty one',
|
||||
];
|
||||
|
||||
if (!in_array($checkSpamBot, $spamBotValid)) {
|
||||
$authRegistrationError = 'Human only cool club, robots begone.';
|
||||
break;
|
||||
}
|
||||
|
||||
$usernameValidation = user_validate_username($authUsername, true);
|
||||
if ($usernameValidation !== '') {
|
||||
$authRegistrationError = MSZ_USER_USERNAME_VALIDATION_STRINGS[$usernameValidation];
|
||||
break;
|
||||
}
|
||||
|
||||
$emailValidation = user_validate_email($authEmail, true);
|
||||
if ($emailValidation !== '') {
|
||||
$authRegistrationError = $emailValidation === 'in-use'
|
||||
? 'This e-mail address has already been used!'
|
||||
: 'The e-mail address you entered is invalid!';
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_validate_password($authPassword) !== '') {
|
||||
$authRegistrationError = 'Your password is too weak!';
|
||||
break;
|
||||
}
|
||||
|
||||
$createUser = user_create(
|
||||
$authUsername,
|
||||
$authPassword,
|
||||
$authEmail,
|
||||
ip_remote_address()
|
||||
);
|
||||
|
||||
if ($createUser < 1) {
|
||||
$authRegistrationError = 'Something happened?';
|
||||
break;
|
||||
}
|
||||
|
||||
user_role_add($createUser, MSZ_ROLE_MAIN);
|
||||
|
||||
tpl_var('auth_register_message', 'Welcome to Flashii! You may now log in.');
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($authRegistrationError)) {
|
||||
tpl_var('auth_register_error', $authRegistrationError);
|
||||
}
|
||||
|
||||
echo tpl_render('auth.auth');
|
||||
header('Location: ' . url('auth-register'));
|
||||
break;
|
||||
}
|
||||
|
|
2
public/auth/index.php
Normal file
2
public/auth/index.php
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?php
|
||||
header('Location: /auth/login.php');
|
109
public/auth/login.php
Normal file
109
public/auth/login.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
use Misuzu\Request\RequestVar;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if (user_session_active()) {
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(RequestVar::get()->resolve_user)) {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo user_id_from_username(RequestVar::get()->resolve_user->value('string'));
|
||||
return;
|
||||
}
|
||||
|
||||
$login = RequestVar::post()->login;
|
||||
$notices = [];
|
||||
$siteIsPrivate = boolval(config_get_default(false, 'Private', 'enabled'));
|
||||
$loginPermission = $siteIsPrivate ? intval(config_get_default(0, 'Private', 'permission')) : 0;
|
||||
$ipAddress = ip_remote_address();
|
||||
$remainingAttempts = user_login_attempts_remaining($ipAddress);
|
||||
|
||||
while (!empty($login->value('array'))) {
|
||||
if (!csrf_verify('login', $_POST['csrf'] ?? '')) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
|
||||
if ($login->username->empty() || $login->password->empty()) {
|
||||
$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;
|
||||
}
|
||||
|
||||
$userData = user_find_for_login($login->username->value('string', ''));
|
||||
$loginFailedError = sprintf(
|
||||
"Invalid username or password, %d attempt%s remaining.",
|
||||
$remainingAttempts - 1,
|
||||
$remainingAttempts === 2 ? '' : 's'
|
||||
);
|
||||
|
||||
if (empty($userData) || $userData['user_id'] < 1) {
|
||||
user_login_attempt_record(false, null, $ipAddress, $userAgent);
|
||||
$notices[] = $loginFailedError;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!password_verify($login->password->value('string', ''), $userData['password'])) {
|
||||
user_login_attempt_record(false, $userData['user_id'], $ipAddress, $userAgent);
|
||||
$notices[] = $loginFailedError;
|
||||
break;
|
||||
}
|
||||
|
||||
user_login_attempt_record(true, $userData['user_id'], $ipAddress, $userAgent);
|
||||
|
||||
if ($loginPermission > 0 && !perms_check_user(MSZ_PERMS_GENERAL, $userData['user_id'], $loginPermission)) {
|
||||
$notices[] = "Login succeeded, but you're not allowed to browse the site right now.";
|
||||
break;
|
||||
}
|
||||
|
||||
$sessionKey = user_session_create($userData['user_id'], $ipAddress, $userAgent);
|
||||
|
||||
if (empty($sessionKey)) {
|
||||
$notices[] = "Something broke while creating a session for you, please tell an administrator or developer about this!";
|
||||
break;
|
||||
}
|
||||
|
||||
user_session_start($userData['user_id'], $sessionKey);
|
||||
$cookieLife = strtotime(user_session_current('session_expires'));
|
||||
$cookieValue = base64url_encode(user_session_cookie_pack($userData['user_id'], $sessionKey));
|
||||
setcookie('msz_auth', $cookieValue, $cookieLife, '/', '', true, true);
|
||||
|
||||
$redirect = $login->redirect->value('string', '');
|
||||
|
||||
if (!is_local_url($redirect)) {
|
||||
$redirect = url('index');
|
||||
}
|
||||
|
||||
header("Location: {$redirect}");
|
||||
return;
|
||||
}
|
||||
|
||||
$welcomeMode = RequestVar::get()->welcome->value('bool', false);
|
||||
$loginUsername = $login->username->value('string') ?? RequestVar::get()->username->value('string', '');
|
||||
$loginRedirect = $welcomeMode ? '/' : RequestVar::get()->redirect->value('string') ?? $_SERVER['HTTP_REFERER'] ?? '/';
|
||||
$sitePrivateMessage = $siteIsPrivate ? config_get_default('', 'Private', 'message') : '';
|
||||
$canResetPassword = $siteIsPrivate ? boolval(config_get_default(false, 'Private', 'password_reset')) : true;
|
||||
$canRegisterAccount = !$siteIsPrivate;
|
||||
|
||||
echo tpl_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,
|
||||
],
|
||||
]);
|
18
public/auth/logout.php
Normal file
18
public/auth/logout.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
use Misuzu\Request\RequestVar;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if (!user_session_active()) {
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
return;
|
||||
}
|
||||
|
||||
if (csrf_verify('logout', RequestVar::get()->token->value('string', ''))) {
|
||||
setcookie('msz_auth', '', -9001, '/', '', true, true);
|
||||
user_session_stop(true);
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
return;
|
||||
}
|
||||
|
||||
echo tpl_render('auth.logout');
|
134
public/auth/password.php
Normal file
134
public/auth/password.php
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
use Misuzu\Request\RequestVar;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if (user_session_active()) {
|
||||
header(sprintf('Location: %s', url('settings-mode', ['mode' => 'account'])));
|
||||
return;
|
||||
}
|
||||
|
||||
$reset = RequestVar::post()->reset;
|
||||
$forgot = RequestVar::post()->forgot;
|
||||
$userId = $reset->user->value('int') ?? RequestVar::get()->user->value('int', 0);
|
||||
$username = $userId > 0 ? user_username_from_id($userId) : '';
|
||||
|
||||
if ($userId > 0 && empty($username)) {
|
||||
header(sprintf('Location: %s', url('auth-forgot')));
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
$siteIsPrivate = boolval(config_get_default(false, 'Private', 'enabled'));
|
||||
$canResetPassword = $siteIsPrivate ? boolval(config_get_default(false, 'Private', 'password_reset')) : true;
|
||||
$ipAddress = ip_remote_address();
|
||||
$remainingAttempts = user_login_attempts_remaining($ipAddress);
|
||||
|
||||
while ($canResetPassword) {
|
||||
if (!empty($reset->value('array', null)) && $userId > 0) {
|
||||
if (!csrf_verify('passreset', $_POST['csrf'] ?? '')) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$verificationCode = $reset->verification->value('string', '');
|
||||
|
||||
if (!user_recovery_token_validate($userId, $verificationCode)) {
|
||||
$notices[] = 'Invalid verification code!';
|
||||
break;
|
||||
}
|
||||
|
||||
$passwordNew = $reset->password->new->value('string', '');
|
||||
$passwordConfirm = $reset->password->confirm->value('string', '');
|
||||
|
||||
if (empty($passwordNew) || empty($passwordConfirm)
|
||||
|| $passwordNew !== $passwordConfirm) {
|
||||
$notices[] = "Password confirmation failed!";
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_validate_password($passwordNew) !== '') {
|
||||
$notices[] = 'Your password is too weak!';
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_password_set($userId, $passwordNew)) {
|
||||
audit_log(MSZ_AUDIT_PASSWORD_RESET, $userId);
|
||||
} else {
|
||||
throw new UnexpectedValueException('Password reset failed.');
|
||||
}
|
||||
|
||||
user_recovery_token_invalidate($userId, $verificationCode);
|
||||
|
||||
header(sprintf('Location: %s', url('auth-login', ['redirect' => '/'])));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($forgot->value('array', null))) {
|
||||
if (!csrf_verify('passforgot', $_POST['csrf'] ?? '')) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($forgot->email->empty()) {
|
||||
$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;
|
||||
}
|
||||
|
||||
$forgotUser = user_find_for_reset($forgot->email->value('string'));
|
||||
|
||||
if (empty($forgotUser)) {
|
||||
$notices[] = "This e-mail address is not registered with us.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!user_recovery_token_sent($forgotUser['user_id'], $ipAddress)) {
|
||||
$verificationCode = user_recovery_token_create($forgotUser['user_id'], $ipAddress);
|
||||
|
||||
if (empty($verificationCode)) {
|
||||
throw new UnexpectedValueException('A verification code failed to insert.');
|
||||
}
|
||||
|
||||
$messageBody = <<<MSG
|
||||
Hey {$forgotUser['username']},
|
||||
|
||||
You, or someone pretending to be you, has requested a password reset for your account.
|
||||
|
||||
Your verification code is: {$verificationCode}
|
||||
|
||||
If you weren't the person who requested this reset, please send a reply to this e-mail.
|
||||
MSG;
|
||||
|
||||
$message = mail_compose(
|
||||
[$forgotUser['email'] => $forgotUser['username']],
|
||||
'Flashii Password Reset',
|
||||
$messageBody
|
||||
);
|
||||
|
||||
if (!mail_send($message)) {
|
||||
$notices[] = "Failed to send reset email, please contact the administrator.";
|
||||
user_recovery_token_invalidate($forgotUser['user_id'], $verificationCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
header(sprintf('Location: %s', url('auth-reset', ['user' => $forgotUser['user_id']])));
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
echo tpl_render($userId > 0 ? 'auth.password_reset' : 'auth.password_forgot', [
|
||||
'password_notices' => $notices,
|
||||
'password_email' => $forgot->email->value('string', ''),
|
||||
'password_attempts_remaining' => $remainingAttempts,
|
||||
'password_user_id' => $userId,
|
||||
'password_username' => $username,
|
||||
'password_verification' => $verificationCode ?? '',
|
||||
]);
|
89
public/auth/register.php
Normal file
89
public/auth/register.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
use Misuzu\Request\RequestVar;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if (user_session_active()) {
|
||||
header(sprintf('Location: %s', url('index')));
|
||||
return;
|
||||
}
|
||||
|
||||
$register = RequestVar::post()->register;
|
||||
$notices = [];
|
||||
$ipAddress = ip_remote_address();
|
||||
$remainingAttempts = user_login_attempts_remaining($ipAddress);
|
||||
$restricted = ip_blacklist_check(ip_remote_address()) ? 'blacklist'
|
||||
: (user_warning_check_ip(ip_remote_address()) ? 'ban' : '');
|
||||
|
||||
while (!$restricted && !empty($register->value('array'))) {
|
||||
if (!csrf_verify('register', $_POST['csrf'] ?? '')) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($remainingAttempts < 1) {
|
||||
$notices[] = "There are too many failed login attempts from your IP address, you may not create an account right now.";
|
||||
break;
|
||||
}
|
||||
|
||||
if ($register->username->empty() || $register->password->empty() || $register->email->empty() || $register->question->empty()) {
|
||||
$notices[] = "You haven't filled in all fields.";
|
||||
break;
|
||||
}
|
||||
|
||||
$checkSpamBot = mb_strtolower($register->question->value('string', ''));
|
||||
$spamBotValid = [
|
||||
'19', '21', 'nineteen', 'nine-teen', 'nine teen', 'twentyone', 'twenty-one', 'twenty one',
|
||||
];
|
||||
|
||||
if (!in_array($checkSpamBot, $spamBotValid)) {
|
||||
$notices[] = 'Human only cool club, robots begone.';
|
||||
break;
|
||||
}
|
||||
|
||||
$username = $register->username->value('string', '');
|
||||
$usernameValidation = user_validate_username($username, true);
|
||||
if ($usernameValidation !== '') {
|
||||
$notices[] = MSZ_USER_USERNAME_VALIDATION_STRINGS[$usernameValidation];
|
||||
}
|
||||
|
||||
$email = $register->email->value('string', '');
|
||||
$emailValidation = user_validate_email($email, true);
|
||||
if ($emailValidation !== '') {
|
||||
$notices[] = $emailValidation === 'in-use'
|
||||
? 'This e-mail address has already been used!'
|
||||
: 'The e-mail address you entered is invalid!';
|
||||
}
|
||||
|
||||
$password = $register->password->value('string', '');
|
||||
if (user_validate_password($password) !== '') {
|
||||
$notices[] = 'Your password is too weak!';
|
||||
}
|
||||
|
||||
if (!empty($notices)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$createUser = user_create(
|
||||
$username,
|
||||
$password,
|
||||
$email,
|
||||
$ipAddress
|
||||
);
|
||||
|
||||
if ($createUser < 1) {
|
||||
$notices[] = 'Something went wrong while creating your account, please alert an administrator or a developer about this!';
|
||||
break;
|
||||
}
|
||||
|
||||
user_role_add($createUser, MSZ_ROLE_MAIN);
|
||||
header(sprintf('Location: %s', url('auth-login-welcome', ['username' => $username])));
|
||||
return;
|
||||
}
|
||||
|
||||
echo tpl_render('auth.register', [
|
||||
'register_notices' => $notices,
|
||||
'register_username' => $register->username->value('string', ''),
|
||||
'register_email' => $register->email->value('string', ''),
|
||||
'register_restricted' => $restricted,
|
||||
]);
|
|
@ -23,12 +23,22 @@ class RequestVar
|
|||
return new static($_POST ?? []);
|
||||
}
|
||||
|
||||
public static function request(): RequestVar
|
||||
{
|
||||
return new static($_REQUEST);
|
||||
}
|
||||
|
||||
public function __get(string $name)
|
||||
{
|
||||
return $this->select($name);
|
||||
}
|
||||
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return $this->isset($name);
|
||||
}
|
||||
|
||||
public function isset(string $name): bool
|
||||
{
|
||||
switch ($this->type) {
|
||||
case 'array':
|
||||
|
@ -38,32 +48,37 @@ class RequestVar
|
|||
return isset($this->value->{$name});
|
||||
|
||||
default:
|
||||
return null;
|
||||
return !is_null($this->value);
|
||||
}
|
||||
}
|
||||
|
||||
public function empty(): bool
|
||||
{
|
||||
return empty($this->value);
|
||||
}
|
||||
|
||||
public function select(string $name): RequestVar
|
||||
{
|
||||
switch ($this->type) {
|
||||
case 'array':
|
||||
return new static($this->value[$name]);
|
||||
return new static($this->value[$name] ?? []);
|
||||
|
||||
case 'object':
|
||||
return new static($this->value->{$name});
|
||||
return new static($this->value->{$name} ?? new \stdClass);
|
||||
|
||||
default:
|
||||
return null;
|
||||
return new static(null);
|
||||
}
|
||||
}
|
||||
|
||||
public function value(string $type = 'string')
|
||||
public function value(string $type = 'string', $default = null)
|
||||
{
|
||||
if (!is_null($this->valueCasted)) {
|
||||
$this->valueCasted;
|
||||
}
|
||||
|
||||
if ($this->type === 'NULL' || (($type === 'object' || $type === 'array') && $this->type !== $type)) {
|
||||
return null;
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ($type !== 'string' && $this->type === 'string') {
|
||||
|
@ -79,7 +94,7 @@ class RequestVar
|
|||
return (float)$this->value;
|
||||
}
|
||||
} elseif ($type !== $this->type) {
|
||||
return null;
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->valueCasted = $this->value;
|
||||
|
|
13
src/url.php
13
src/url.php
|
@ -12,12 +12,13 @@ define('MSZ_URLS', [
|
|||
'info' => ['/info.php/<title>'],
|
||||
'media-proxy' => ['/proxy.php/<hash>/<url>'],
|
||||
|
||||
'auth-login' => ['/auth.php', ['m' => 'login']],
|
||||
'auth-register' => ['/auth.php', ['m' => 'register']],
|
||||
'auth-forgot' => ['/auth.php', ['m' => 'forgot']],
|
||||
'auth-reset' => ['/auth.php', ['m' => 'reset', 'u' => '<user>']],
|
||||
'auth-logout' => ['/auth.php', ['m' => 'logout', 's' => '{logout}']],
|
||||
'auth-resolve-user' => ['/auth.php', ['m' => 'get_user', 'u' => '<username>']],
|
||||
'auth-login' => ['/auth/login.php', ['username' => '<username>', 'redirect' => '<redirect>']],
|
||||
'auth-login-welcome' => ['/auth/login.php', ['welcome' => '1', 'username' => '<username>']],
|
||||
'auth-register' => ['/auth/register.php'],
|
||||
'auth-forgot' => ['/auth/password.php'],
|
||||
'auth-reset' => ['/auth/password.php', ['user' => '<user>']],
|
||||
'auth-logout' => ['/auth/logout.php', ['token' => '{logout}']],
|
||||
'auth-resolve-user' => ['/auth/login.php', ['resolve_user' => '<username>']],
|
||||
|
||||
'changelog-index' => ['/changelog.php'],
|
||||
'changelog-change' => ['/changelog.php', ['c' => '<change>']],
|
||||
|
|
|
@ -82,7 +82,6 @@
|
|||
'title': 'Log in',
|
||||
'url': url('auth-login'),
|
||||
'icon': 'fas fa-sign-in-alt fa-fw',
|
||||
'class': 'js-login-button',
|
||||
},
|
||||
]
|
||||
%}
|
||||
|
@ -127,7 +126,7 @@
|
|||
<a href="{{ url('user-profile', {'user': current_user.user_id}) }}" class="avatar header__desktop__user__avatar" title="{{ current_user.username }}"
|
||||
style="background-image:url('{{ url('user-avatar', {'user': current_user.user_id}) }}');{{ current_user.user_colour|html_colour }}"></a>
|
||||
{% else %}
|
||||
<a href="{{ url('auth-login') }}" class="avatar header__desktop__user__avatar js-login-button"
|
||||
<a href="{{ url('auth-login') }}" class="avatar header__desktop__user__avatar"
|
||||
style="background-image:url('{{ url('user-avatar') }}');"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from 'macros.twig' import container_title %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
|
||||
{% from 'auth/macros.twig' import auth_login %}
|
||||
|
||||
{% block content %}
|
||||
{{ auth_login(
|
||||
auth_username|default(''),
|
||||
auth_register_message|default(auth_login_error|default('')),
|
||||
auth_register_message is defined,
|
||||
auth_redirect|default('/'),
|
||||
auth_mode == 'login'
|
||||
) }}
|
||||
|
||||
{% if can_create_account %}
|
||||
<form class="container auth" method="post" action="">
|
||||
{{ input_hidden('auth[mode]', 'register') }}
|
||||
{{ input_csrf('register') }}
|
||||
|
||||
{{ container_title('<i class="fas fa-user-check fa-fw"></i> Register') }}
|
||||
|
||||
{% if auth_restricted %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{% if auth_restricted == 2 %}
|
||||
A user is currently in a banned and/or silenced state from the same IP address you're currently visiting the site from. If said user isn't you and you wish to create an account, please <a href="{{ url('info', {'title': 'contact'}) }}" class="warning__link">contact us</a>!
|
||||
{% else %}
|
||||
The IP address from which you are visiting the website appears on our blacklist, you are not allowed to register from this address but if you already have an account you can log in just fine using the form above. If you think this blacklisting is a mistake, please <a href="{{ url('info', {'title': 'contact'}) }}" class="warning__link">contact us</a>!
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if auth_register_error is defined %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{{ auth_register_error }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="auth__form">
|
||||
{{ input_text('auth[username]', 'auth__input', auth_username|default(''), 'text', 'Username', true, null, 0, auth_mode == 'register') }}
|
||||
{{ input_text('auth[password]', 'auth__input', '', 'password', 'Password', true) }}
|
||||
{{ input_text('auth[email]', 'auth__input', auth_email|default(''), 'text', 'E-mail', true) }}
|
||||
{{ input_text('auth[meow]', 'auth__input', '', 'text', 'What is the outcome of nine plus ten?', true) }}
|
||||
|
||||
<button class="input__button">Create your account</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% if can_reset_password %}
|
||||
<form class="container auth" method="post" action="">
|
||||
{{ input_hidden('auth[mode]', 'forgot') }}
|
||||
{{ input_csrf('passforgot') }}
|
||||
|
||||
{{ container_title('<i class="fas fa-user-lock fa-fw"></i> Forgot password') }}
|
||||
|
||||
{% if auth_forgot_error is defined %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{{ auth_forgot_error }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="auth__form">
|
||||
{{ input_text('auth[email]', 'auth__input', auth_email|default(''), 'text', 'E-mail', true, null, 0, auth_mode == 'forgot') }}
|
||||
|
||||
<button class="input__button">Send reminder</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,12 +0,0 @@
|
|||
{% extends 'master_minimal.twig' %}
|
||||
{% from 'macros.twig' import container_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container auth">
|
||||
{{ container_title(message_title|default('<i class="fas fa-lock fa-fw"></i> Unavailable')) }}
|
||||
|
||||
<div class="container__content">
|
||||
{{ message|default('The site is currently unavailable, try again later.')|raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
62
templates/auth/login.twig
Normal file
62
templates/auth/login.twig
Normal file
|
@ -0,0 +1,62 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
|
||||
|
||||
{% set title = 'Login' %}
|
||||
|
||||
{% block content %}
|
||||
<form class="container auth__container auth__login js-login-form" method="post" action="{{ url('auth-login') }}">
|
||||
{{ input_csrf('login') }}
|
||||
{{ input_hidden('login[redirect]', login_redirect) }}
|
||||
|
||||
<div class="container__title">
|
||||
<div class="container__title__background"></div>
|
||||
<div class="auth__login__header">
|
||||
<div class="avatar auth__login__avatar js-login-avatar" style="background-image:url('{{ url('user-avatar') }}');"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if login_notices|length > 0 %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{% for notice in login_notices %}
|
||||
<p class="auth__warning__paragraph">{{ notice }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% elseif login_welcome %}
|
||||
<div class="warning auth__warning auth__warning--welcome">
|
||||
<div class="warning__content">
|
||||
<p class="auth__warning__paragraph">Welcome to Flashii, you may now log in!</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
Username
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('login[username]', 'auth__label__input js-login-username', login_username, 'text', '', true, null, 1, not login_welcome) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
Password
|
||||
{% if login_can_reset_password %}
|
||||
<a href="{{ url('auth-forgot') }}" class="auth__label__action" tabindex="4">Forgot?</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('login[password]', 'auth__label__input', '', 'password', '', true, null, 2, login_welcome) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="auth__buttons">
|
||||
<button class="input__button auth__buttons__button" tabindex="3">Log in</button>
|
||||
{% if login_can_register %}
|
||||
<a href="{{ url('auth-register') }}" class="input__button auth__buttons__button auth__buttons__button--minor" tabindex="5">Create an account</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,14 +1,16 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from 'macros.twig' import container_title %}
|
||||
|
||||
{% set title = 'Logout confirmation' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container auth">
|
||||
<div class="container auth__container">
|
||||
{{ container_title('<i class="fas fa-user-clock fa-fw"></i> Logout confirmation') }}
|
||||
|
||||
<div class="auth__form">
|
||||
<p class="auth__paragraph">We couldn't verify that you were actually the person attempting to log out.</p>
|
||||
<p class="auth__paragraph">Press the button below to verify the logout request, otherwise click back in your browser or close this tab.</p>
|
||||
<p class="auth__paragraph">This error is usually caused by pressing the logout button on a page that's been loaded for a while.</p>
|
||||
<div class="auth__logout">
|
||||
<p class="auth__logout__paragraph">We couldn't verify that you were actually the person attempting to log out.</p>
|
||||
<p class="auth__logout__paragraph">Press the button below to verify the logout request, otherwise click back in your browser or close this tab.</p>
|
||||
<p class="auth__logout__paragraph">This error is usually caused by pressing the logout button on a page that's been loaded for a while.</p>
|
||||
<a href="{{ url('auth-logout') }}" class="input__button">Log out</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
{% macro auth_login(username, message, is_welcome, redirect, autofocus) %}
|
||||
{% set is_welcome = is_welcome|default(false) %}
|
||||
{% set autofocus = autofocus|default(false) %}
|
||||
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
|
||||
|
||||
<form class="container auth js-login-form" method="post" action="{{ url('auth-login') }}">
|
||||
{{ input_hidden('auth[mode]', 'login') }}
|
||||
{{ input_csrf('login') }}
|
||||
|
||||
{% if redirect|length > 0 %}
|
||||
{{ input_hidden('auth[redirect]', redirect) }}
|
||||
{% endif %}
|
||||
|
||||
<div class="container__title">
|
||||
<div class="container__title__background"></div>
|
||||
<div class="auth__header">
|
||||
<div class="avatar auth__avatar js-login-avatar" style="background-image:url('{{ url('user-avatar') }}');"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if message|length > 0 %}
|
||||
<div class="warning auth__warning{% if is_welcome %} auth__warning--welcome{% endif %}">
|
||||
<div class="warning__content">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="auth__form">
|
||||
{{ input_text('auth[username]', 'auth__input js-login-username', username|default(''), 'text', 'Username', true, null, 0, autofocus) }}
|
||||
{{ input_text('auth[password]', 'auth__input', '', 'password', 'Password', true, null) }}
|
||||
|
||||
<div class="auth__buttons">
|
||||
<button class="input__button auth__button">Log in</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endmacro %}
|
|
@ -1,36 +0,0 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from 'macros.twig' import container_title %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
|
||||
|
||||
{% block content %}
|
||||
<form class="container auth" method="post" action="">
|
||||
{{ input_hidden('auth[mode]', 'reset') }}
|
||||
{{ input_hidden('auth[user]', reset_user.user_id) }}
|
||||
{{ input_csrf('passreset') }}
|
||||
|
||||
{{ container_title('<i class="fas fa-user-lock fa-fw"></i> Resetting password for ' ~ reset_user.username) }}
|
||||
|
||||
<div class="warning auth__warning{% if auth_reset_error is not defined %} auth__warning--welcome{% endif %}">
|
||||
<div class="warning__content">
|
||||
{{ auth_reset_error|default(auth_reset_message|default('')) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="auth__form">
|
||||
{{ input_text(
|
||||
'auth[verification]',
|
||||
'input__text--monospace auth__input',
|
||||
reset_verify|default(''),
|
||||
reset_verify is defined ? 'hidden' : 'text',
|
||||
'verification code',
|
||||
true,
|
||||
{'maxlength':12}
|
||||
) }}
|
||||
|
||||
{{ input_text('auth[password][new]', 'auth__input', '', 'password', 'new password', true) }}
|
||||
{{ input_text('auth[password][confirm]', 'auth__input', '', 'password', 'confirm password', true) }}
|
||||
|
||||
<button class="input__button">Change password</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
36
templates/auth/password_forgot.twig
Normal file
36
templates/auth/password_forgot.twig
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from 'macros.twig' import container_title %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
|
||||
|
||||
{% set title = 'Forgot password' %}
|
||||
|
||||
{% block content %}
|
||||
<form class="container auth__container auth__password" method="post" action="{{ url('auth-forgot') }}">
|
||||
{{ container_title('<i class="fas fa-user-lock fa-fw"></i> Forgot password') }}
|
||||
{{ input_csrf('passforgot') }}
|
||||
|
||||
{% if password_notices|length > 0 %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{% for notice in password_notices %}
|
||||
<p class="auth__warning__paragraph">{{ notice }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
E-mail
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('forgot[email]', 'auth__label__input', password_email, 'text', '', true, null, 1) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="auth__buttons">
|
||||
<button class="input__button auth__buttons__button" tabindex="2">Send Reminder</button>
|
||||
<a href="{{ url('auth-login') }}" class="input__button auth__buttons__button auth__buttons__button--minor" tabindex="3">Log in</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
66
templates/auth/password_reset.twig
Normal file
66
templates/auth/password_reset.twig
Normal file
|
@ -0,0 +1,66 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from 'macros.twig' import container_title %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
|
||||
|
||||
{% set title = 'Resetting password' %}
|
||||
|
||||
{% block content %}
|
||||
<form class="container auth__container auth__password" method="post" action="{{ url('auth-reset') }}">
|
||||
{{ container_title('<i class="fas fa-user-lock fa-fw"></i> Resetting password for ' ~ password_username) }}
|
||||
|
||||
{{ input_hidden('reset[user]', password_user_id) }}
|
||||
{{ input_csrf('passreset') }}
|
||||
|
||||
{% if password_notices|length > 0 %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{% for notice in password_notices %}
|
||||
<p class="auth__warning__paragraph">{{ notice }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="warning auth__warning auth__warning--welcome">
|
||||
<div class="warning__content">
|
||||
<p class="auth__warning__paragraph">A verification code should've been sent to your e-mail address.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if password_verification|length == 12 %}
|
||||
{{ input_hidden('reset[verification]', password_verification) }}
|
||||
{% else %}
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
Verification Code
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('reset[verification]', 'input__text--monospace auth__label__input', '', 'text', '', true, {'maxlength':12}, 1) }}
|
||||
</div>
|
||||
</label>
|
||||
{% endif %}
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
New Password
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('reset[password][new]', 'auth__label__input', '', 'password', '', true, null, 2) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
Confirm Password
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('reset[password][confirm]', 'auth__label__input', '', 'password', '', true, null, 3) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="auth__buttons">
|
||||
<button class="input__button auth__buttons__button" tabindex="4">Change Password</button>
|
||||
<a href="{{ url('auth-login') }}" class="input__button auth__buttons__button auth__buttons__button--minor" tabindex="5">Log in</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,10 +0,0 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from 'auth/macros.twig' import auth_login %}
|
||||
|
||||
{% block content %}
|
||||
{{ auth_login(
|
||||
auth_username|default(''),
|
||||
auth_login_error|default(private_message|default('')),
|
||||
auth_login_error is not defined
|
||||
) }}
|
||||
{% endblock %}
|
90
templates/auth/register.twig
Normal file
90
templates/auth/register.twig
Normal file
|
@ -0,0 +1,90 @@
|
|||
{% extends 'auth/master.twig' %}
|
||||
{% from 'macros.twig' import container_title %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
|
||||
|
||||
{% set title = 'Register' %}
|
||||
|
||||
{% block content %}
|
||||
<form class="container auth__container auth__register" method="post" action="{{ url('auth-register') }}">
|
||||
{{ container_title('<i class="fas fa-user-check fa-fw"></i> Register') }}
|
||||
|
||||
<div class="auth__register__container">
|
||||
{% if not register_restricted %}
|
||||
<div class="auth__register__info">
|
||||
{% if register_notices|length > 0 %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{% for notice in register_notices %}
|
||||
<p class="auth__warning__paragraph">{{ notice }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p class="auth__register__paragraph">Welcome to Flashii! Before creating your account, here are a few things you should take note of.</p>
|
||||
<p class="auth__register__paragraph">By creating an account you agree to the <a href="{{ url('info', {'title': 'rules'}) }}" class="auth__register__link">rules</a>.</p>
|
||||
<p class="auth__register__paragraph">Engaging in borderline illegal activity on platforms provided by Flashii will result in a permanent ban, as described by Global Rule 5.</p>
|
||||
<p class="auth__register__paragraph">You are not allowed to have more than one account unless given explicit permission, as described by Global Rule 6.</p>
|
||||
<p class="auth__register__paragraph">You must be at least 13 years of age to use this website, as described by Global Rule 8.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="auth__register__form">
|
||||
{{ input_csrf('register') }}
|
||||
|
||||
{% if register_restricted %}
|
||||
<div class="warning auth__warning">
|
||||
<div class="warning__content">
|
||||
{% if register_restricted == 'ban' %}
|
||||
<p class="auth__warning__paragraph">A user is currently in a banned and/or silenced state from the same IP address you're currently visiting the site from. If said user isn't you and you wish to create an account, please <a href="{{ url('info', {'title': 'contact'}) }}" class="warning__link">contact us</a>!</p>
|
||||
{% else %}
|
||||
<p class="auth__warning__paragraph">The IP address from which you are visiting the website appears on our blacklist, you are not allowed to register from this address but if you already have an account you can log in just fine using the form above. If you think this blacklisting is a mistake, please <a href="{{ url('info', {'title': 'contact'}) }}" class="warning__link">contact us</a>!</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
Username
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[username]', 'auth__label__input', register_username, 'text', '', true, null, 1, true) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
Password
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[password]', 'auth__label__input', '', 'password', '', true, null, 2) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
E-mail
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[email]', 'auth__label__input', register_email, 'text', '', true, null, 3) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
What is the outcome of nine plus ten?
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[question]', 'auth__label__input', '', 'text', '', true, null, 4) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="auth__buttons">
|
||||
<button class="input__button auth__buttons__button" tabindex="5">Create your account</button>
|
||||
<a href="{{ url('auth-login') }}" class="input__button auth__buttons__button auth__buttons__button--minor" tabindex="6">Log in</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
Loading…
Add table
Reference in a new issue