Feature/refactor account lost (#326)

* [WIP] Account Lost refactor

* [WIP] Refactor account/lost

* Update form.html.twig

* Use myaac-table class for tables

* Set $title to 'Lost Account'

* Remove duplicated code - extract lostAccountCooldown function

* [WIP] Add csrfProtect()

* Refactor code, better $error messages

* Formatting

* Refactor

Add missing password check
Formatting

* [WIP] Refactor

* [WIP] Refactor account lost

* [WIP] Refactor account lost - fixes

* [WIP] Account lost refactor

* Fixes
* Add account lost hooks for password strength plugin
This commit is contained in:
Slawomir Boczek
2026-01-17 18:59:01 +01:00
committed by GitHub
parent 173b1ace88
commit 5040a93031
26 changed files with 1027 additions and 583 deletions

View File

@@ -0,0 +1,18 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
function lostAccountWriteCooldown(string $nick, int $time): void
{
global $twig;
$inSec = $time - time();
$minutesLeft = floor($inSec / 60);
$secondsLeft = $inSec - ($minutesLeft * 60);
$timeLeft = "$minutesLeft minutes $secondsLeft seconds";
$timeRounded = ceil(setting('core.mail_lost_account_interval') / 60);
$twig->display('error_box.html.twig', [
'errors' => ["Account of selected character (<b>" . escapeHtml($nick) . "</b>) received e-mail in last $timeRounded minutes. You must wait $timeLeft before you can use Lost Account Interface again."]
]);
}

View File

@@ -0,0 +1,51 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$code = $_REQUEST['code'] ?? '';
$character = $_REQUEST['character'] ?? '';
if(empty($code) || empty($character)) {
$twig->display('account/lost/check-code.html.twig', [
'code' => $code,
'characters' => $character,
]);
}
else {
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($character);
if($player->isLoaded()) {
$account = $player->getAccount();
}
if($account->isLoaded()) {
if($account->getCustomField('email_code') == $code) {
$twig->display('account/lost/check-code.finish.html.twig', [
'character' => $character,
'code' => $code,
]);
}
else {
$error = 'Wrong code to change password.';
}
}
else {
$error = "Account of this character or this character doesn't exist.";
}
}
if(!empty($error)) {
$twig->display('error_box.html.twig', [
'errors' => [$error],
]);
echo '<br/>';
$twig->display('account/lost/check-code.html.twig', [
]);
}

View File

@@ -0,0 +1,75 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
require __DIR__ . '/../base.php';
$title = 'Lost Account';
$email = $_POST['email'] ?? '';
$nick = $_POST['nick'] ?? '';
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($nick);
if($player->isLoaded()) {
$account = $player->getAccount();
}
if($account->isLoaded()) {
if($account->getCustomField('email_next') < time()) {
if($account->getEMail() == $email) {
$newCode = generateRandomString(30, true, false, true);
$mailBody = $twig->render('mail.account.lost.code.html.twig', [
'newCode' => $newCode,
'account' => $account,
'nick' => $nick,
]);
$accountEMail = $account->getCustomField('email');
if(_mail($accountEMail, configLua('serverName') . ' - Recover your account', $mailBody)) {
$account->setCustomField('email_code', $newCode);
$account->setCustomField('email_next', (time() + setting('core.mail_lost_account_interval')));
$twig->display('success.html.twig', [
'title' => 'Email has been sent',
'description' => 'Details about steps required to recover your account has been sent to <b>' . $accountEMail . '</b>. You should receive this email within 15 minutes. Please check your inbox/spam directory.',
'custom_buttons' => '',
]);
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('news'),
]);
return;
}
$account->setCustomField('email_next', (time() + 60));
error('An error occurred while sending email! Try again later or contact with admin. For Admin: More info can be found in system/logs/mailer-error.log</p>');
}
else {
$errors[] = 'Invalid e-mail to account of character <b>' . escapeHtml($nick) . '</b>. Try again.';
}
}
else {
lostAccountWriteCooldown($nick, (int)$account->getCustomField('email_next'));
}
}
else {
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
}
if (!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
}
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/step-1') . '?action=email&nick=' . urlencode($nick),
]);

View File

@@ -0,0 +1,128 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$newPassword = $_POST['password'] ?? '';
$passwordRepeat = $_POST['password_repeat'] ?? '';
$code = $_POST['code'] ?? '';
$character = $_POST['character'] ?? '';
if(empty($code) || empty($character)) {
$errors[] = 'Please enter code from e-mail and name of one character from account.';
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
$twig->display('account/lost/check-code.html.twig', [
'code' => $code,
'character' => $character,
]);
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/check-code')
]);
return;
}
if (empty($newPassword) || empty($passwordRepeat)) {
$errors[] = 'Please enter both passwords.';
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
$twig->display('account/lost/check-code.finish.html.twig', [
'character' => $character,
'code' => $code,
]);
return;
}
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($character);
if($player->isLoaded()) {
$account = $player->getAccount();
}
$passwordFailed = false;
if($account->isLoaded()) {
if($account->getCustomField('email_code') == $code) {
if ($newPassword == $passwordRepeat) {
if (Validator::password($newPassword)) {
$hooks->trigger(HOOK_ACCOUNT_LOST_EMAIL_SET_NEW_PASSWORD_POST);
if (empty($errors)) {
$tmp_new_pass = $newPassword;
if (USE_ACCOUNT_SALT) {
$salt = generateRandomString(10, false, true, true);
$tmp_new_pass = $salt . $newPassword;
$account->setCustomField('salt', $salt);
}
$account->setPassword(encrypt($tmp_new_pass));
$account->save();
$account->setCustomField('email_code', '');
$mailBody = $twig->render('mail.account.lost.new-password.html.twig', [
'account' => $account,
'newPassword' => $newPassword,
]);
$statusMsg = '';
if (_mail($account->getCustomField('email'), configLua('serverName') . ' - Your new password', $mailBody)) {
$statusMsg = '<br /><small>New password work! Sent e-mail with your password and account name. You should receive this e-mail in 15 minutes. You can login now with new password!';
} else {
$statusMsg = '<br /><p class="error">New password work! An error occurred while sending email! You will not receive e-mail with new password. For Admin: More info can be found in system/logs/mailer-error.log';
}
$twig->display('account/lost/finish.new-password.html.twig', [
'statusMsg' => $statusMsg,
'newPassword' => $newPassword,
]);
}
} else {
$passwordFailed = true;
$errors[] = Validator::getLastError();
}
}
else {
$passwordFailed = true;
$errors[] = 'Passwords are not the same!';
}
}
else {
$errors[] = 'Wrong code to change password.';
}
}
else {
$errors[] = "Account of this character or this character doesn't exist.";
}
if(!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
echo '<br/>';
$template = 'account/lost/check-code.html.twig';
if($passwordFailed) {
$template = 'account/lost/check-code.finish.html.twig';
}
$twig->display($template, [
'code' => $code,
'character' => $character,
]);
}

View File

@@ -0,0 +1,36 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
require __DIR__ . '/../base.php';
csrfProtect();
$title = 'Lost Account';
$nick = $_REQUEST['nick'] ?? '';
if($account->isLoaded()) {
if($account->getCustomField('email_next') < time()) {
$twig->display('account/lost/email.html.twig', [
'nick' => $nick,
]);
}
else {
lostAccountWriteCooldown($nick, (int)$account->getCustomField('email_next'));
}
}
else {
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
}
if (!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
}
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost'),
]);

View File

@@ -0,0 +1,38 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$nick = $_REQUEST['nick'] ?? '';
$key = $_REQUEST['key'] ?? '';
if($account->isLoaded()) {
$account_key = $account->getCustomField('key');
if(!empty($account_key)) {
$twig->display('account/lost/recovery-key.step-1.html.twig', [
'nick' => $nick,
'key' => $key,
]);
}
else {
$errors[] = 'Account of this character has no recovery key!';
}
}
else {
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
}
if (!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
}
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost'),
]);

View File

@@ -0,0 +1,49 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$key = $_REQUEST['key'] ?? '';
$nick = $_REQUEST['nick'] ?? '';
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($nick);
if($player->isLoaded()) {
$account = $player->getAccount();
}
if($account->isLoaded()) {
$accountKey = $account->getCustomField('key');
if(!empty($accountKey)) {
if($accountKey == $key) {
$twig->display('account/lost/recovery-key.step-2.html.twig', [
'nick' => $nick,
'key' => $key,
]);
}
else {
$errors[] = 'Wrong recovery key!';
}
}
else {
$errors[] = 'Account of this character has no recovery key!';
}
}
else {
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
}
if (!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
}
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/step-1') . '?action=recovery-key&nick=' . urlencode($nick) . '&key=' . urlencode($key),
]);

View File

@@ -0,0 +1,117 @@
<?php
use MyAAC\Models\Account as AccountModel;
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$key = $_POST['key'];
$nick = $_POST['nick'] ?? '';
$newPassword = $_POST['password'] ?? '';
$passwordRepeat = $_POST['password_repeat'] ?? '';
$newEmail = $_POST['email'] ?? '';
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($nick);
if($player->isLoaded()) {
$account = $player->getAccount();
}
if($account->isLoaded()) {
$accountKey = $account->getCustomField('key');
if(!empty($accountKey)) {
if($accountKey == $key) {
if(Validator::password($newPassword)) {
if ($newPassword == $passwordRepeat) {
if (Validator::email($newEmail)) {
$emailExists = AccountModel::where('email', $newEmail)->count() > 0;
if (!$emailExists) {
$hooks->trigger(HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_3_POST);
if (empty($errors)) {
$account->setEMail($newEmail);
$tmp_new_pass = $newPassword;
if (USE_ACCOUNT_SALT) {
$salt = generateRandomString(10, false, true, true);
$tmp_new_pass = $salt . $newPassword;
}
$account->setPassword(encrypt($tmp_new_pass));
$account->save();
if (USE_ACCOUNT_SALT) {
$account->setCustomField('salt', $salt);
}
$statusMsg = '';
if ($account->getCustomField('email_next') < time()) {
$mailBody = $twig->render('mail.account.lost.new-email.html.twig', [
'account' => $account,
'newPassword' => $newPassword,
'newEmail' => $newEmail,
]);
if (_mail($account->getCustomField('email'), configLua('serverName') . ' - New password to your account', $mailBody)) {
$statusMsg = '<br /><small>Sent e-mail with your account name and password to new e-mail. You should receive this e-mail in 15 minutes. You can login now with new password!</small>';
} else {
$statusMsg = '<br /><p class="error">An error occurred while sending email! You will not receive e-mail with this informations. For Admin: More info can be found in system/logs/mailer-error.log</p>';
}
} else {
$statusMsg = '<br /><small>You will not receive e-mail with this informations.</small>';
}
$twig->display('account/lost/finish.new-email.html.twig', [
'statusMsg' => $statusMsg,
'account' => $account,
'newPassword' => $newPassword,
'newEmail' => $newEmail,
]);
return;
}
}
else {
$errors[] = 'This email is already registered!';
}
} else {
$errors[] = Validator::getLastError();
}
}
else {
$errors[] = 'Passwords are not the same!';
}
}
else {
$errors[] = Validator::getLastError();
}
}
else {
$errors[] = 'Wrong recovery key!';
}
}
else {
$errors[] = 'Account of this character has no recovery key!';
}
}
else {
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
}
if (!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
}
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/recovery-key/step-2') . '?nick=' . urlencode($nick) . '&key=' . urlencode($key),
]);

View File

@@ -0,0 +1,26 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$nick = $_REQUEST['nick'] ?? '';
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($nick);
if($player->isLoaded()) {
$account = $player->getAccount();
}
if (ACTION == 'email') {
require __DIR__ . '/email/step-1.php';
}
else if (ACTION == 'recovery-key') {
require __DIR__ . '/recovery-key/step-1.php';
}
else {
$twig->display('account/lost/no-action.html.twig');
}