. */ declare(strict_types=1); namespace Fisharebest\Webtrees\Http\RequestHandlers; use Exception; use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Contracts\UserInterface; use Fisharebest\Webtrees\FlashMessages; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Log; use Fisharebest\Webtrees\Services\UpgradeService; use Fisharebest\Webtrees\Services\UserService; use Fisharebest\Webtrees\Session; use Fisharebest\Webtrees\Tree; use Fisharebest\Webtrees\Validator; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use function route; use function time; /** * Perform a login. */ class LoginAction implements RequestHandlerInterface { private UpgradeService $upgrade_service; private UserService $user_service; /** * LoginController constructor. * * @param UpgradeService $upgrade_service * @param UserService $user_service */ public function __construct(UpgradeService $upgrade_service, UserService $user_service) { $this->upgrade_service = $upgrade_service; $this->user_service = $user_service; } /** * Perform a login. * * @param ServerRequestInterface $request * * @return ResponseInterface */ public function handle(ServerRequestInterface $request): ResponseInterface { $tree = Validator::attributes($request)->treeOptional(); $default_url = route(HomePage::class); $username = Validator::parsedBody($request)->string('username'); $password = Validator::parsedBody($request)->string('password'); $url = Validator::parsedBody($request)->isLocalUrl()->string('url', $default_url); try { $this->doLogin($username, $password); if (Auth::isAdmin() && $this->upgrade_service->isUpgradeAvailable()) { FlashMessages::addMessage(I18N::translate('A new version of webtrees is available.') . ' ' . I18N::translate('Upgrade to webtrees %s.', '' . $this->upgrade_service->latestVersion() . '') . ''); } // Redirect to the target URL return redirect($url); } catch (Exception $ex) { // Failed to log in. FlashMessages::addMessage($ex->getMessage(), 'danger'); return redirect(route(LoginPage::class, [ 'tree' => $tree instanceof Tree ? $tree->name() : null, 'username' => $username, 'url' => $url, ])); } } /** * Log in, if we can. Throw an exception, if we can't. * * @param string $username * @param string $password * * @return void * @throws Exception */ private function doLogin(string $username, #[\SensitiveParameter] string $password): void { if ($_COOKIE === []) { Log::addAuthenticationLog('Login failed (no session cookies): ' . $username); throw new Exception(I18N::translate('You cannot sign in because your browser does not accept cookies.')); } $user = $this->user_service->findByIdentifier($username); if ($user === null) { Log::addAuthenticationLog('Login failed (no such user/email): ' . $username); throw new Exception(I18N::translate('The username or password is incorrect.')); } if (!$user->checkPassword($password)) { Log::addAuthenticationLog('Login failed (incorrect password): ' . $username); throw new Exception(I18N::translate('The username or password is incorrect.')); } if ($user->getPreference(UserInterface::PREF_IS_EMAIL_VERIFIED) !== '1') { Log::addAuthenticationLog('Login failed (not verified by user): ' . $username); throw new Exception(I18N::translate('This account has not been verified. Please check your email for a verification message.')); } if ($user->getPreference(UserInterface::PREF_IS_ACCOUNT_APPROVED) !== '1') { Log::addAuthenticationLog('Login failed (not approved by admin): ' . $username); throw new Exception(I18N::translate('This account has not been approved. Please wait for an administrator to approve it.')); } Auth::login($user); Log::addAuthenticationLog('Login: ' . Auth::user()->userName() . '/' . Auth::user()->realName()); Auth::user()->setPreference(UserInterface::PREF_TIMESTAMP_ACTIVE, (string) time()); Session::put('language', Auth::user()->getPreference(UserInterface::PREF_LANGUAGE)); Session::put('theme', Auth::user()->getPreference(UserInterface::PREF_THEME)); I18N::init(Auth::user()->getPreference(UserInterface::PREF_LANGUAGE)); } }