123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- <?php
- /**
- * This file is part of the ForkBB <https://github.com/forkbb>.
- *
- * @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
- * @license The MIT License (MIT)
- */
- declare(strict_types=1);
- namespace ForkBB\Models\Pages;
- use ForkBB\Core\Validator;
- use ForkBB\Core\Exceptions\MailException;
- use ForkBB\Models\Page;
- use ForkBB\Models\User\User;
- use SensitiveParameter;
- use function \ForkBB\__;
- class Auth extends Page
- {
- /**
- * Выход пользователя
- */
- public function logout(array $args): Page
- {
- if (! $this->c->Csrf->verify($args['token'], 'Logout', $args)) {
- $this->c->Log->warning('Logout: fail', [
- 'user' => $this->user->fLog(),
- ]);
- return $this->c->Redirect->page('Index')->message($this->c->Csrf->getError());
- }
- $this->c->Cookie->deleteUser();
- $this->c->Online->delete($this->user);
- $this->c->users->updateLastVisit($this->user);
- $this->c->Log->info('Logout: ok', [
- 'user' => $this->user->fLog(),
- ]);
- $this->c->Lang->load('auth');
- return $this->c->Redirect->page('Index')->message('Logout redirect');
- }
- /**
- * Вход на форум
- */
- public function login(array $args, string $method, string $username = ''): Page
- {
- $this->c->Lang->load('validator');
- $this->c->Lang->load('auth');
- $v = null;
- if ('POST' === $method) {
- $v = $this->c->Validator->reset()
- ->addValidators([
- 'login_check' => [$this, 'vLoginCheck'],
- ])->addRules([
- 'token' => 'token:Login',
- 'redirect' => 'required|referer:Index',
- 'username' => 'required|string',
- 'password' => 'required|string|max:100000|login_check',
- 'save' => 'checkbox',
- 'login' => 'required|string',
- ])->addAliases([
- 'username' => 'Username',
- 'password' => 'Passphrase',
- ]);
- $v = $this->c->Test->beforeValidation($v);
- if ($v->validation($_POST, true)) {
- $this->loginEnd($v);
- return $this->c->Redirect->url($v->redirect)->message('Login redirect');
- }
- $this->fIswev = $v->getErrors();
- $this->c->Log->warning('Login: fail', [
- 'user' => $this->user->fLog(),
- 'errors' => $v->getErrorsWithoutType(),
- 'form' => $v->getData(false, ['token', 'password']),
- ]);
- $this->httpStatus = 400;
- }
- $ref = $this->c->Secury->replInvalidChars($_SERVER['HTTP_REFERER'] ?? '');
- $this->hhsLevel = 'secure';
- $this->fIndex = self::FI_LOGIN;
- $this->nameTpl = 'login';
- $this->onlinePos = 'login';
- $this->onlineDetail = null;
- $this->robots = 'noindex';
- $this->titles = 'Login';
- $this->regLink = 1 === $this->c->config->b_regs_allow ? $this->c->Router->link('Register') : null;
- $username = $v->username ?? $username;
- $save = $v->save ?? 1;
- $redirect = $v->redirect ?? $this->c->Router->validate($ref, 'Index');
- $this->form = $this->formLogin($username, $save, $redirect);
- return $this;
- }
- /**
- * Обрабатывает вход пользователя
- */
- protected function loginEnd(Validator $v): void
- {
- $this->c->users->updateLoginIpCache($this->userAfterLogin, true); // ????
- // сбросить запрос на смену кодовой фразы
- if (32 === \strlen($this->userAfterLogin->activate_string)) {
- $this->userAfterLogin->activate_string = '';
- }
- // изменения юзера в базе
- $this->c->users->update($this->userAfterLogin);
- $this->c->Online->delete($this->user);
- $this->c->Cookie->setUser($this->userAfterLogin, (bool) $v->save);
- $this->c->Log->info('Login: ok', [
- 'user' => $this->userAfterLogin->fLog(),
- 'form' => $v->getData(false, ['token', 'password']),
- 'headers' => true,
- ]);
- }
- /**
- * Подготавливает массив данных для формы
- */
- protected function formLogin(string $username, /* mixed */ $save, string $redirect): array
- {
- return [
- 'action' => $this->c->Router->link('Login'),
- 'hidden' => [
- 'token' => $this->c->Csrf->create('Login'),
- 'redirect' => $redirect,
- ],
- 'sets' => [
- 'login' => [
- 'fields' => [
- 'username' => [
- 'autofocus' => true,
- 'type' => 'text',
- 'value' => $username,
- 'caption' => 'Username',
- 'required' => true,
- ],
- 'password' => [
- 'id' => 'passinlogin',
- 'type' => 'password',
- 'caption' => 'Passphrase',
- 'help' => ['<a href="%s">Forgotten?</a>', $this->c->Router->link('Forget')],
- 'required' => true,
- ],
- 'save' => [
- 'type' => 'checkbox',
- 'label' => 'Remember me',
- 'checked' => $save,
- ],
- ],
- ],
- ],
- 'btns' => [
- 'login' => [
- 'type' => 'submit',
- 'value' => __('Sign in'),
- ],
- ],
- ];
- }
- /**
- * Проверка пользователя по базе
- */
- public function vLoginCheck(
- Validator $v,
- #[SensitiveParameter]
- string $password
- ): string {
- if (empty($v->getErrors())) {
- $this->userAfterLogin = $this->c->users->loadByName($v->username);
- if (
- ! $this->userAfterLogin instanceof User
- || $this->userAfterLogin->isGuest
- ) {
- $v->addError('Wrong user/pass');
- } elseif ($this->userAfterLogin->isUnverified) {
- $v->addError('Account is not activated', 'w');
- } elseif (! \password_verify($password, $this->userAfterLogin->password)) {
- $v->addError('Wrong user/pass');
- }
- }
- if (! empty($v->getErrors())) {
- $this->userAfterLogin = null;
- }
- return $password;
- }
- /**
- * Запрос на смену кодовой фразы
- */
- public function forget(array $args, string $method, string $email = ''): Page
- {
- $this->c->Lang->load('validator');
- $this->c->Lang->load('auth');
- $v = null;
- if ('POST' === $method) {
- $v = $this->c->Validator->reset()
- ->addValidators([
- ])->addRules([
- 'token' => 'token:Forget',
- 'email' => 'required|string:trim|email',
- 'submit' => 'required|string',
- ])->addAliases([
- ])->addMessages([
- 'email.email' => 'Invalid email',
- ])->addArguments([
- ]);
- $v = $this->c->Test->beforeValidation($v);
- $isValid = $v->validation($_POST, true);
- $context = [
- 'user' => $this->user->fLog(), // ???? Guest only?
- 'errors' => $v->getErrorsWithoutType(),
- 'form' => $v->getData(false, ['token']),
- 'headers' => true,
- ];
- if ($isValid) {
- $tmpUser = $this->c->users->create();
- $isSent = false;
- $v = $v->reset()
- ->addRules([
- 'email' => 'required|string:trim|email:nosoloban,exists,flood',
- ])->addArguments([
- 'email.email' => $tmpUser, // сюда идет возрат данных по найденному пользователю
- ]);
- if (
- $v->validation($_POST)
- && 0 === $this->c->bans->banFromName($tmpUser->username)
- ) {
- $this->c->Csrf->setHashExpiration(259200); // ???? хэш действует 72 часа
- $key = $this->c->Secury->randomPass(32);
- $link = $this->c->Router->link(
- 'ChangePassword',
- [
- 'id' => $tmpUser->id,
- 'key' => $key,
- ]
- );
- $tplData = [
- 'fRootLink' => $this->c->Router->link('Index'),
- 'fMailer' => __(['Mailer', $this->c->config->o_board_title]),
- 'username' => $tmpUser->username,
- 'link' => $link,
- ];
- try {
- $isSent = $this->c->Mail
- ->reset()
- ->setMaxRecipients(1)
- ->setFolder($this->c->DIR_LANG)
- ->setLanguage($tmpUser->language)
- ->setTo($tmpUser->email, $tmpUser->username)
- ->setFrom($this->c->config->o_webmaster_email, $tplData['fMailer'])
- ->setTpl('passphrase_reset.tpl', $tplData)
- ->send();
- } catch (MailException $e) {
- $this->c->Log->error('Passphrase reset: email form, MailException', [
- 'exception' => $e,
- 'headers' => false,
- ]);
- }
- if ($isSent) {
- $tmpUser->activate_string = $key;
- $tmpUser->last_email_sent = \time();
- $this->c->users->update($tmpUser);
- $this->c->Log->info('Passphrase reset: email form, ok', $context);
- }
- }
- if (! $isSent) {
- $context['errors'] = $v->getErrorsWithoutType();
- $this->c->Log->warning('Passphrase reset: email form, fail', $context);
- }
- return $this->c->Message->message(['Forget mail', $this->c->config->o_admin_email], false, 0);
- }
- $this->fIswev = $v->getErrors();
- $this->c->Log->warning('Passphrase reset: email form, fail', $context);
- $this->httpStatus = 400;
- }
- $this->hhsLevel = 'secure';
- $this->fIndex = self::FI_LOGIN;
- $this->nameTpl = 'passphrase_reset';
- $this->onlinePos = 'passphrase_reset';
- $this->onlineDetail = null;
- $this->robots = 'noindex';
- $this->titles = 'Passphrase reset';
- $this->form = $this->formForget($v->email ?? $email);
- return $this;
- }
- /**
- * Подготавливает массив данных для формы
- */
- protected function formForget(string $email): array
- {
- return [
- 'action' => $this->c->Router->link('Forget'),
- 'hidden' => [
- 'token' => $this->c->Csrf->create('Forget'),
- ],
- 'sets' => [
- 'forget' => [
- 'fields' => [
- 'email' => [
- 'autofocus' => true,
- 'type' => 'text',
- 'maxlength' => '80',
- 'value' => $email,
- 'caption' => 'Email',
- 'help' => 'Passphrase reset info',
- 'required' => true,
- 'pattern' => '.+@.+',
- ],
- ],
- ],
- ],
- 'btns' => [
- 'submit' => [
- 'type' => 'submit',
- 'value' => __('Send email'),
- ],
- ],
- ];
- }
- /**
- * Смена кодовой фразы
- */
- public function changePass(array $args, string $method): Page
- {
- if (
- ! $this->c->Csrf->verify($args['hash'], 'ChangePassword', $args)
- || ! ($user = $this->c->users->load($args['id'])) instanceof User
- || ! \hash_equals($user->activate_string, $args['key'])
- ) {
- $this->c->Log->warning('Passphrase reset: confirmation, fail', [
- 'user' => $user instanceof User ? $user->fLog() : $this->user->fLog(),
- 'args' => $args,
- ]);
- // что-то пошло не так
- return $this->c->Message->message('Bad request', false);
- }
- $this->c->Lang->load('validator');
- $this->c->Lang->load('auth');
- if ('POST' === $method) {
- $v = $this->c->Validator->reset()
- ->addRules([
- 'token' => 'token:ChangePassword',
- 'password' => 'required|string|min:16|max:100000|password',
- 'password2' => 'required|same:password',
- 'submit' => 'required|string',
- ])->addAliases([
- 'password' => 'New pass',
- 'password2' => 'Confirm new pass',
- ])->addArguments([
- 'token' => $args,
- ])->addMessages([
- 'password.password' => 'Pass format',
- 'password2.same' => 'Pass not match',
- ]);
- $v = $this->c->Test->beforeValidation($v);
- if ($v->validation($_POST, true)) {
- $user->password = \password_hash($v->password, \PASSWORD_DEFAULT);
- $user->email_confirmed = 1;
- $user->activate_string = '';
- $this->c->users->update($user);
- $this->fIswev = ['s', 'Pass updated'];
- $this->c->Log->info('Passphrase reset: ok', [
- 'user' => $user->fLog(),
- ]);
- return $this->login([], 'GET');
- }
- $this->fIswev = $v->getErrors();
- $this->c->Log->warning('Passphrase reset: change form, fail', [
- 'user' => $user->fLog(),
- 'errors' => $v->getErrorsWithoutType(),
- 'form' => $v->getData(false, ['token', 'password', 'password2']),
- ]);
- $this->httpStatus = 400;
- }
- // активация аккаунта (письмо активации не дошло, заказали восстановление)
- if ($user->isUnverified) {
- $user->group_id = $this->c->config->i_default_user_group;
- $user->email_confirmed = 1;
- $this->c->users->update($user);
- $this->fIswev = ['i', 'Account activated'];
- $this->c->Log->info('Account activation: ok', [
- 'user' => $user->fLog(),
- ]);
- }
- $this->hhsLevel = 'secure';
- $this->fIndex = self::FI_LOGIN;
- $this->nameTpl = 'change_passphrase';
- $this->onlinePos = 'change_passphrase';
- $this->onlineDetail = null;
- $this->robots = 'noindex';
- $this->titles = 'Passphrase reset';
- $this->form = $this->formChange($args);
- return $this;
- }
- /**
- * Подготавливает массив данных для формы
- */
- protected function formChange(array $args): array
- {
- return [
- 'action' => $this->c->Router->link('ChangePassword', $args),
- 'hidden' => [
- 'token' => $this->c->Csrf->create('ChangePassword', $args),
- ],
- 'sets' => [
- 'forget' => [
- 'fields' => [
- 'password' => [
- 'autofocus' => true,
- 'type' => 'password',
- 'caption' => 'New pass',
- 'required' => true,
- 'pattern' => '^.{16,}$',
- ],
- 'password2' => [
- 'type' => 'password',
- 'caption' => 'Confirm new pass',
- 'help' => 'Passphrase help',
- 'required' => true,
- 'pattern' => '^.{16,}$',
- ],
- ],
- ],
- ],
- 'btns' => [
- 'submit' => [
- 'type' => 'submit',
- 'value' => __('Change passphrase'),
- ],
- ],
- ];
- }
- }
|