display email form * 2. submit email form > send mail with recovery link * 3. click recovery link from mail > display password reset form * 4. submit password reset form > update password */ class AccountresetpasswordResponse extends TemplateResponse { use TrRecoveryHelper, TrGetNext; protected string $template = 'text-page-generic'; protected string $pageName = 'reset-password'; protected array $expectedGET = array( 'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']], 'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/' ]] ); protected array $expectedPOST = array( 'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']], 'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW ], 'password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ], 'c_password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ] ); private bool $success = false; public function __construct() { $this->title[] = Lang::account('title'); parent::__construct(); // don't redirect logged in users // you can be forgetful AND logged in if (Cfg::get('ACC_EXT_RECOVER_URL')) $this->forward(Cfg::get('ACC_EXT_RECOVER_URL')); if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) $this->generateError(); } protected function generate() : void { parent::generate(); $errMsg = ''; if (!$this->assertGET('key') && !$this->assertPOST('key')) $errMsg = Lang::account('inputbox', 'error', 'passTokenLost'); else if ($this->_get['key'] && !DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `token` = %s AND `status` = %i AND `statusTimer` > UNIX_TIMESTAMP()', $this->_get['key'], ACC_STATUS_RECOVER_PASS)) $errMsg = Lang::account('inputbox', 'error', 'passTokenUsed'); if ($errMsg) { $this->inputbox = ['inputbox-status', array( 'head' => Lang::account('inputbox', 'head', 'error'), 'error' => $errMsg )]; return; } // step "2.5" $errMsg = $this->doResetPass(); if ($this->success) $this->forward('?account=signin'); // step 2 $this->inputbox = ['inputbox-form-password', array( 'head' => Lang::account('inputbox', 'head', 'recoverPass', [2]), 'token' => $this->_post['key'] ?? $this->_get['key'], 'action' => '?account=reset-password&next=account=signin', 'error' => $errMsg, )]; } private function doResetPass() : string { // no input yet. show clean form if (!$this->assertPOST('key', 'password', 'c_password') && is_null($this->_post['email'])) return ''; // truncated due to validation fail if (!$this->_post['email']) return Lang::account('emailInvalid'); if ($this->_post['password'] != $this->_post['c_password']) return Lang::account('passCheckFail'); $userData = DB::Aowow()->selectRow('SELECT `id`, `passHash` FROM ::account WHERE `token` = %s AND `email` = %s AND `status` = %i AND `statusTimer` > UNIX_TIMESTAMP()', $this->_post['key'], $this->_post['email'], ACC_STATUS_RECOVER_PASS ); if (!$userData) return Lang::account('inputbox', 'error', 'emailNotFound'); if (!User::verifyCrypt($this->_post['c_password'], $userData['passHash'])) return Lang::account('newPassDiff'); if (!DB::Aowow()->qry('UPDATE ::account SET `passHash` = %s, `status` = %i WHERE `id` = %i', User::hashCrypt($this->_post['c_password']), ACC_STATUS_NONE, $userData['id'])) return Lang::main('intError'); $this->success = true; return ''; } } ?>