mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Template/Update (Part 46 - IV)
* account management rework: Personal Settings functionality * email, password, username update * email updates now also mails the old address for confirmation
This commit is contained in:
@@ -28,6 +28,7 @@ class AccountBaseResponse extends TemplateResponse
|
||||
public string $curEmail = '';
|
||||
public string $curName = '';
|
||||
public string $renameCD = '';
|
||||
public string $activeCD = '';
|
||||
public array $description = [];
|
||||
public array $signature = [];
|
||||
public int $avMode = 0;
|
||||
@@ -51,7 +52,7 @@ class AccountBaseResponse extends TemplateResponse
|
||||
{
|
||||
array_unshift($this->title, Lang::account('settings'));
|
||||
|
||||
$user = DB::Aowow()->selectRow('SELECT `debug`, `email`, `description`, `avatar`, `wowicon` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
$user = DB::Aowow()->selectRow('SELECT `debug`, `email`, `description`, `avatar`, `wowicon`, `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
|
||||
Lang::sort('game', 'ra');
|
||||
|
||||
@@ -108,10 +109,13 @@ class AccountBaseResponse extends TemplateResponse
|
||||
$this->curEmail = $user['email'] ?? '';
|
||||
|
||||
// Username
|
||||
$this->curName = User::$username;
|
||||
|
||||
// todo localize date format; store time
|
||||
// $this->renameCD = date('F j, o', time() + 7 * DAY);
|
||||
$this->curName = User::$username;
|
||||
$this->renameCD = Util::formatTime(Cfg::get('ACC_RENAME_DECAY') * 1000);
|
||||
if ($user['renameCooldown'] > time())
|
||||
{
|
||||
$locCode = implode('_', str_split(Lang::getLocale()->json(), 2)); // ._.
|
||||
$this->activeCD = (new \IntlDateFormatter($locCode, pattern: Lang::main('dateFmtIntl')))->format($user['renameCooldown']);
|
||||
}
|
||||
|
||||
/* COMMUNITY */
|
||||
|
||||
|
||||
62
endpoints/account/confirm-email-address.php
Normal file
62
endpoints/account/confirm-email-address.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via confirmation email link
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
// ?auth=email-change
|
||||
class AccountConfirmemailaddressResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'confirm-email-address';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
parent::generate();
|
||||
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
$msg = $this->change();
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
|
||||
'message' => $this->success ? $msg : '',
|
||||
'error' => $this->success ? '' : $msg,
|
||||
)];
|
||||
}
|
||||
|
||||
// this should probably leave change info intact for revert
|
||||
// todo - move personal settings changes to separate table
|
||||
private function change() : string
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
return Lang::main('intError');
|
||||
|
||||
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
|
||||
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
|
||||
return Lang::account('inputbox', 'error', 'mailTokenUsed');
|
||||
|
||||
// 0 changes == error
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `email` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'mailChangeOk');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
60
endpoints/account/confirm-password.php
Normal file
60
endpoints/account/confirm-password.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via confirmation email link
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
// 2025 - no longer in use?
|
||||
class AccountConfirmpasswordResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'confirm-password';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
parent::generate();
|
||||
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
$msg = $this->confirm();
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
|
||||
'message' => $this->success ? $msg : '',
|
||||
'error' => $this->success ? '' : $msg,
|
||||
)];
|
||||
}
|
||||
|
||||
private function confirm() : string
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
return Lang::main('intError');
|
||||
|
||||
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
|
||||
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_PASS || $acc['statusTimer'] < time())
|
||||
return Lang::account('inputbox', 'error', 'passTokenUsed');
|
||||
|
||||
// 0 changes == error
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `passHash` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'passChangeOk');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
62
endpoints/account/revert-email-address.php
Normal file
62
endpoints/account/revert-email-address.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via revert email link
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
// ?auth=email-revert
|
||||
class AccountRevertemailaddressResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'revert-email-address';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
parent::generate();
|
||||
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
$msg = $this->revert();
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
|
||||
'message' => $this->success ? $msg : '',
|
||||
'error' => $this->success ? '' : $msg,
|
||||
)];
|
||||
}
|
||||
|
||||
// this should probably take precedence over email-change
|
||||
// todo - move personal settings changes to separate table
|
||||
private function revert() : string
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
return Lang::main('intError');
|
||||
|
||||
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
|
||||
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
|
||||
return Lang::account('inputbox', 'error', 'mailTokenUsed');
|
||||
|
||||
// 0 changes == error
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'mailRevertOk');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
80
endpoints/account/update-email.php
Normal file
80
endpoints/account/update-email.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdateemailResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#personal';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'newemail' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
(new TemplateResponse())->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($msg = $this->updateMail())
|
||||
$_SESSION['msg'] = ['email', $this->success, $msg];
|
||||
}
|
||||
|
||||
private function updateMail() : string
|
||||
{
|
||||
// no input yet
|
||||
if (is_null($this->_post['newemail']))
|
||||
return Lang::main('intError');
|
||||
// truncated due to validation fail
|
||||
if (!$this->_post['newemail'])
|
||||
return Lang::account('emailInvalid');
|
||||
|
||||
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ? AND `id` <> ?d', $this->_post['newemail'], User::$id))
|
||||
return Lang::account('mailInUse');
|
||||
|
||||
$status = DB::Aowow()->selectCell('SELECT `status` FROM ?_account WHERE `statusTimer` > UNIX_TIMESTAMP() AND `id` = ?d', User::$id);
|
||||
if ($status != ACC_STATUS_NONE && $status != ACC_STATUS_CHANGE_EMAIL)
|
||||
return Lang::account('isRecovering', [Util::formatTime(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
|
||||
|
||||
$oldEmail = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
if ($this->_post['newemail'] == $oldEmail)
|
||||
return Lang::account('newMailDiff');
|
||||
|
||||
$token = Util::createHash();
|
||||
|
||||
// store new mail in updateValue field, exchange when confirmation mail gets confirmed
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
|
||||
$this->_post['newemail'], ACC_STATUS_CHANGE_EMAIL, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
|
||||
return Lang::main('intError');
|
||||
|
||||
if (!Util::sendMail($this->_post['newemail'], 'change-email', [$token, $this->_post['newemail']], Cfg::get('ACC_RECOVERY_DECAY')))
|
||||
return Lang::main('intError2', ['send mail']);
|
||||
|
||||
if (!Util::sendMail($oldEmail, 'revert-email', [$token, $oldEmail], Cfg::get('ACC_RECOVERY_DECAY')))
|
||||
return Lang::main('intError2', ['send mail']);
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'personal', [$this->_post['newemail']]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
86
endpoints/account/update-password.php
Normal file
86
endpoints/account/update-password.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdatepasswordResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#personal';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'currentPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'newPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'confirmPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'globalLogout' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCheckbox']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
(new TemplateResponse())->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($msg = $this->updatePassword())
|
||||
$_SESSION['msg'] = ['password', $this->success, $msg];
|
||||
}
|
||||
|
||||
private function updatePassword() : string
|
||||
{
|
||||
if (!$this->assertPOST('currentPassword', 'newPassword', 'confirmPassword'))
|
||||
return Lang::main('intError');
|
||||
|
||||
if (!Util::validatePassword($this->_post['newPassword'], $e))
|
||||
return Lang::account($e == 1 ? 'errPassLength' : 'errPassChars');
|
||||
|
||||
if ($this->_post['newPassword'] !== $this->_post['confirmPassword'])
|
||||
return Lang::account('passMismatch');
|
||||
|
||||
$userData = DB::Aowow()->selectRow('SELECT `status`, `passHash`, `statusTimer` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
if ($userData['status'] != ACC_STATUS_NONE && $userData['status'] != ACC_STATUS_CHANGE_PASS && $userData['statusTimer'] > time())
|
||||
return Lang::account('isRecovering', [Util::formatTime(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
|
||||
|
||||
if (!User::verifyCrypt($this->_post['currentPassword'], $userData['passHash']))
|
||||
return Lang::account('wrongPass');
|
||||
|
||||
if (User::verifyCrypt($this->_post['newPassword'], $userData['passHash']))
|
||||
return Lang::account('newPassDiff');
|
||||
|
||||
$token = Util::createHash();
|
||||
|
||||
// store new hash in updateValue field, exchange when confirmation mail gets confirmed
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
|
||||
User::hashCrypt($this->_post['newPassword']), ACC_STATUS_CHANGE_PASS, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
|
||||
return Lang::main('intError');
|
||||
|
||||
$email = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
if (!Util::sendMail($email, 'update-password', [$token, $email], Cfg::get('ACC_RECOVERY_DECAY')))
|
||||
return Lang::main('intError2', ['send mail']);
|
||||
|
||||
// logout all other active sessions
|
||||
if ($this->_post['globalLogout'])
|
||||
DB::Aowow()->query('UPDATE ?_account_sessions SET `status` = ?d, `touched` = ?d WHERE `userId` = ?d AND `sessionId` <> ? AND `status` = ?d', SESSION_FORCED_LOGOUT, time(), User::$id, session_id(), SESSION_ACTIVE);
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'personal', [User::$email]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
61
endpoints/account/update-username.php
Normal file
61
endpoints/account/update-username.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdateusernameResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#personal';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'newUsername' => ['filter' => FILTER_CALLBACK, 'options' => [Util::class, 'validateUsername']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
(new TemplateResponse())->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($msg = $this->updateUsername())
|
||||
$_SESSION['msg'] = ['username', $this->success, $msg];
|
||||
}
|
||||
|
||||
private function updateUsername() : string
|
||||
{
|
||||
if (!$this->assertPOST('newUsername'))
|
||||
return Lang::main('intError');
|
||||
|
||||
if (DB::Aowow()->selectCell('SELECT `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id) > time())
|
||||
return Lang::main('intError'); // should have grabbed the error response..
|
||||
|
||||
// yes, including your current name. you don't want to change into your current name, right?
|
||||
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_post['newUsername']))
|
||||
return Lang::account('nameInUse');
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_account SET `username` = ?, `renameCooldown` = ?d WHERE `id` = ?d', $this->_post['newUsername'], time() + Cfg::get('acc_rename_decay'), User::$id);
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'username', [User::$username, $this->_post['newUsername']]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user