passwordReset->isEnabled()) { return $this->redirectToLoginForm($request); } $this->initialize($request); $this->initializeForgetPasswordView($request); $this->pageRenderer->setBodyContent('' . $this->view->render('Login/ForgetPasswordForm')); return $this->pageRenderer->renderResponse($request); } /** * Validate the email address. * * Restricted to POST method in Configuration/Backend/Routes.php */ public function initiatePasswordResetAction(ServerRequestInterface $request): ResponseInterface { if (!$this->passwordReset->isEnabled()) { return $this->redirectToLoginForm($request); } $this->initialize($request); $this->initializeForgetPasswordView($request); $emailAddress = $request->getParsedBody()['email'] ?? ''; if (!is_string($emailAddress)) { $emailAddress = ''; } $this->view->assign('email', $emailAddress); if (!GeneralUtility::validEmail($emailAddress)) { $this->view->assign('invalidEmail', true); } else { $this->passwordReset->initiateReset($request, $this->context, $emailAddress); $this->view->assign('resetInitiated', true); } $this->pageRenderer->setBodyContent('' . $this->view->render('Login/ForgetPasswordForm')); // Prevent time based information disclosure by waiting a random time // before sending a response. This prevents that the response time // can be an indicator if the used email exists or not. Wait a random // time between 200 milliseconds and 3 seconds. usleep(random_int(200000, 3000000)); return $this->pageRenderer->renderResponse($request); } /** * Validates the link and show a form to enter the new password. */ public function passwordResetAction(ServerRequestInterface $request): ResponseInterface { if (!$this->passwordReset->isEnabled()) { return $this->redirectToLoginForm($request); } $this->initialize($request); $this->initializeResetPasswordView($request); if (!$this->passwordReset->isValidResetTokenFromRequest($request)) { $this->view->assign('invalidToken', true); } $this->pageRenderer->setBodyContent('' . $this->view->render('Login/ResetPasswordForm')); return $this->pageRenderer->renderResponse($request); } /** * Updates the password in the database. * * Restricted to POST method in Configuration/Backend/Routes.php */ public function passwordResetFinishAction(ServerRequestInterface $request): ResponseInterface { if (!$this->passwordReset->isEnabled()) { return $this->redirectToLoginForm($request); } // Token is invalid if (!$this->passwordReset->isValidResetTokenFromRequest($request)) { return $this->passwordResetAction($request); } $this->initialize($request); $this->initializeResetPasswordView($request); if ($this->passwordReset->resetPassword($request, $this->context)) { $this->view->assign('resetExecuted', true); } else { $this->view->assign('error', true); } $this->pageRenderer->setBodyContent('' . $this->view->render('Login/ResetPasswordForm')); return $this->pageRenderer->renderResponse($request); } private function redirectToLoginForm(ServerRequestInterface $request): ResponseInterface { return new RedirectResponse( $this->uriBuilder->buildUriWithRedirect('login', [], RouteRedirect::createFromRequest($request)), 303 ); } protected function initializeForgetPasswordView(ServerRequestInterface $request): void { $parameters = array_filter(['loginProvider' => $this->loginProvider]); $this->view->assignMultiple([ 'formUrl' => $this->uriBuilder->buildUriWithRedirect('password_forget_initiate_reset', $parameters, RouteRedirect::createFromRequest($request)), 'returnUrl' => $this->uriBuilder->buildUriWithRedirect('login', $parameters, RouteRedirect::createFromRequest($request)), ]); } protected function initializeResetPasswordView(ServerRequestInterface $request): void { $token = $request->getQueryParams()['t'] ?? ''; $identity = $request->getQueryParams()['i'] ?? ''; $expirationDate = $request->getQueryParams()['e'] ?? ''; $parameters = array_filter(['loginProvider' => $this->loginProvider]); $formUrl = $this->uriBuilder->buildUriWithRedirect( 'password_reset_finish', array_filter(array_merge($parameters, [ 't' => $token, 'i' => $identity, 'e' => $expirationDate, ])), RouteRedirect::createFromRequest($request) ); $this->view->assignMultiple([ 'token' => $token, 'identity' => $identity, 'expirationDate' => $expirationDate, 'formUrl' => $formUrl, 'restartUrl' => $this->uriBuilder->buildUriWithRedirect('password_forget', $parameters, RouteRedirect::createFromRequest($request)), 'passwordRequirements' => $this->getPasswordRequirements(), ]); } protected function initialize(ServerRequestInterface $request): void { $languageService = $this->getLanguageService(); // Only allow to execute this if not logged in as a user right now if ($this->context->getAspect('backend.user')->isLoggedIn()) { throw new PropagateResponseException( new RedirectResponse($this->uriBuilder->buildUriFromRoute('login'), 303), 1618342858 ); } // Fetch login provider from the request $this->loginProvider = $request->getQueryParams()['loginProvider'] ?? ''; // Try to get the preferred browser language $httpAcceptLanguage = $request->getServerParams()['HTTP_ACCEPT_LANGUAGE'] ?? ''; $preferredBrowserLanguage = $this->locales->getPreferredClientLanguage($httpAcceptLanguage); // If we found a $preferredBrowserLanguage, which is not the default language // initialize $this->getLanguageService() again with $preferredBrowserLanguage. // Additionally, set the language to the backend user object, so labels in fluid views are translated if ($preferredBrowserLanguage !== 'default') { $languageService->init($preferredBrowserLanguage); $this->getBackendUserAuthentication()->user['lang'] = $preferredBrowserLanguage; } $this->setUpBasicPageRendererForBackend($this->pageRenderer, $this->extensionConfiguration, $request, $languageService); $this->pageRenderer->setTitle('TYPO3 CMS Login: ' . ($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?? '')); $this->pageRenderer->loadJavaScriptModule('bootstrap'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/login.js'); $this->view = $this->backendViewFactory->create($request); $this->view->assignMultiple([ 'enablePasswordReset' => $this->passwordReset->isEnabled(), 'referrerCheckEnabled' => $this->features->isFeatureEnabled('security.backend.enforceReferrer'), 'loginUrl' => (string)$request->getUri(), ]); $this->provideCustomLoginStyling($request); } protected function provideCustomLoginStyling(ServerRequestInterface $request): void { if (($backgroundImageStyles = $this->authenticationStyleInformation->getBackgroundImageStyles($request)) !== '') { $this->pageRenderer->addCssInlineBlock('loginBackgroundImage', $backgroundImageStyles, null, false, true); } if (($footerNote = $this->authenticationStyleInformation->getFooterNote()) !== '') { $this->view->assign('loginFootnote', $footerNote); } if (($highlightColorStyles = $this->authenticationStyleInformation->getHighlightColorStyles()) !== '') { $this->pageRenderer->addCssInlineBlock('loginHighlightColor', $highlightColorStyles, null, false, true); } $this->view->assignMultiple([ 'copyright' => $this->typo3Information->getCopyrightNotice(), ]); } protected function getPasswordRequirements(): array { $passwordPolicy = $GLOBALS['TYPO3_CONF_VARS']['BE']['passwordPolicy'] ?? 'default'; $passwordPolicyValidator = GeneralUtility::makeInstance( PasswordPolicyValidator::class, PasswordPolicyAction::UPDATE_USER_PASSWORD, is_string($passwordPolicy) ? $passwordPolicy : '' ); return $passwordPolicyValidator->getRequirements(); } protected function getLanguageService(): LanguageService { return $GLOBALS['LANG']; } protected function getBackendUserAuthentication(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } }