1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It is free software; you can redistribute it and/or modify it under 9 * the terms of the GNU General Public License, either version 2 10 * of the License, or any later version. 11 * 12 * For the full copyright and license information, please read the 13 * LICENSE.txt file that was distributed with this source code. 14 * 15 * The TYPO3 project - inspiring people to share! 16 */ 17 18namespace TYPO3\CMS\FrontendLogin\Controller; 19 20use Psr\EventDispatcher\EventDispatcherInterface; 21use Psr\Http\Message\ResponseInterface; 22use TYPO3\CMS\Core\Authentication\LoginType; 23use TYPO3\CMS\Core\Context\Context; 24use TYPO3\CMS\Core\Context\UserAspect; 25use TYPO3\CMS\Core\Utility\GeneralUtility; 26use TYPO3\CMS\Extbase\Http\ForwardResponse; 27use TYPO3\CMS\FrontendLogin\Configuration\RedirectConfiguration; 28use TYPO3\CMS\FrontendLogin\Event\BeforeRedirectEvent; 29use TYPO3\CMS\FrontendLogin\Event\LoginConfirmedEvent; 30use TYPO3\CMS\FrontendLogin\Event\LoginErrorOccurredEvent; 31use TYPO3\CMS\FrontendLogin\Event\LogoutConfirmedEvent; 32use TYPO3\CMS\FrontendLogin\Event\ModifyLoginFormViewEvent; 33use TYPO3\CMS\FrontendLogin\Redirect\RedirectHandler; 34use TYPO3\CMS\FrontendLogin\Redirect\ServerRequestHandler; 35use TYPO3\CMS\FrontendLogin\Service\UserService; 36 37/** 38 * Used for plugin login 39 */ 40class LoginController extends AbstractLoginFormController 41{ 42 /** 43 * @var string 44 */ 45 public const MESSAGEKEY_DEFAULT = 'welcome'; 46 47 /** 48 * @var string 49 */ 50 public const MESSAGEKEY_ERROR = 'error'; 51 52 /** 53 * @var string 54 */ 55 public const MESSAGEKEY_LOGOUT = 'logout'; 56 57 /** 58 * @var RedirectHandler 59 */ 60 protected $redirectHandler; 61 62 /** 63 * @var string 64 */ 65 protected $loginType = ''; 66 67 /** 68 * @var string 69 */ 70 protected $redirectUrl = ''; 71 72 /** 73 * @var ServerRequestHandler 74 */ 75 protected $requestHandler; 76 77 /** 78 * @var UserService 79 */ 80 protected $userService; 81 82 /** 83 * @var RedirectConfiguration 84 */ 85 protected $configuration; 86 87 /** 88 * @var EventDispatcherInterface 89 */ 90 protected $eventDispatcher; 91 92 /** 93 * @var UserAspect 94 */ 95 protected $userAspect; 96 97 /** 98 * @var bool 99 */ 100 protected $showCookieWarning = false; 101 102 public function __construct( 103 RedirectHandler $redirectHandler, 104 ServerRequestHandler $requestHandler, 105 UserService $userService, 106 EventDispatcherInterface $eventDispatcher 107 ) { 108 $this->redirectHandler = $redirectHandler; 109 $this->requestHandler = $requestHandler; 110 $this->userService = $userService; 111 $this->eventDispatcher = $eventDispatcher; 112 $this->userAspect = GeneralUtility::makeInstance(Context::class)->getAspect('frontend.user'); 113 } 114 115 /** 116 * Initialize redirects 117 */ 118 public function initializeAction(): void 119 { 120 $this->loginType = (string)$this->requestHandler->getPropertyFromGetAndPost('logintype'); 121 $this->configuration = RedirectConfiguration::fromSettings($this->settings); 122 123 if ($this->isLoginOrLogoutInProgress() && !$this->isRedirectDisabled()) { 124 if ($this->userAspect->isLoggedIn() && $this->userService->cookieWarningRequired()) { 125 $this->showCookieWarning = true; 126 return; 127 } 128 129 $this->redirectUrl = $this->redirectHandler->processRedirect( 130 $this->loginType, 131 $this->configuration, 132 $this->request->hasArgument('redirectReferrer') ? $this->request->getArgument('redirectReferrer') : '' 133 ); 134 } 135 } 136 137 /** 138 * Show login form 139 */ 140 public function loginAction(): ResponseInterface 141 { 142 if ($this->isLogoutSuccessful()) { 143 $this->eventDispatcher->dispatch(new LogoutConfirmedEvent($this, $this->view)); 144 } elseif ($this->hasLoginErrorOccurred()) { 145 $this->eventDispatcher->dispatch(new LoginErrorOccurredEvent()); 146 } 147 148 if (($forwardResponse = $this->handleLoginForwards()) !== null) { 149 return $forwardResponse; 150 } 151 $this->handleRedirect(); 152 153 $this->eventDispatcher->dispatch(new ModifyLoginFormViewEvent($this->view)); 154 155 $this->view->assignMultiple( 156 [ 157 'cookieWarning' => $this->showCookieWarning, 158 'messageKey' => $this->getStatusMessageKey(), 159 'storagePid' => implode(',', $this->getStorageFolders()), 160 'permaloginStatus' => $this->getPermaloginStatus(), 161 'redirectURL' => $this->redirectHandler->getLoginFormRedirectUrl($this->configuration, $this->isRedirectDisabled()), 162 'redirectReferrer' => $this->request->hasArgument('redirectReferrer') ? (string)$this->request->getArgument('redirectReferrer'): '', 163 'referer' => $this->requestHandler->getPropertyFromGetAndPost('referer'), 164 'noRedirect' => $this->isRedirectDisabled(), 165 ] 166 ); 167 168 return $this->htmlResponse(); 169 } 170 171 /** 172 * User overview for logged in users 173 * 174 * @param bool $showLoginMessage 175 * @return ResponseInterface 176 */ 177 public function overviewAction(bool $showLoginMessage = false): ResponseInterface 178 { 179 if (!$this->userAspect->isLoggedIn()) { 180 return new ForwardResponse('login'); 181 } 182 183 $this->eventDispatcher->dispatch(new LoginConfirmedEvent($this, $this->view)); 184 $this->handleRedirect(); 185 186 $this->view->assignMultiple( 187 [ 188 'cookieWarning' => $this->showCookieWarning, 189 'user' => $this->userService->getFeUserData(), 190 'showLoginMessage' => $showLoginMessage, 191 ] 192 ); 193 194 return $this->htmlResponse(); 195 } 196 197 /** 198 * Show logout form 199 * @param int $redirectPageLogout 200 * @return ResponseInterface 201 */ 202 public function logoutAction(int $redirectPageLogout = 0): ResponseInterface 203 { 204 $this->handleRedirect(); 205 206 $this->view->assignMultiple( 207 [ 208 'cookieWarning' => $this->showCookieWarning, 209 'user' => $this->userService->getFeUserData(), 210 'storagePid' => implode(',', $this->getStorageFolders()), 211 'noRedirect' => $this->isRedirectDisabled(), 212 'actionUri' => $this->redirectHandler->getLogoutFormRedirectUrl($this->configuration, $redirectPageLogout, $this->isRedirectDisabled()), 213 ] 214 ); 215 216 return $this->htmlResponse(); 217 } 218 219 /** 220 * Handles the redirect when $this->redirectUrl is not empty 221 */ 222 protected function handleRedirect(): void 223 { 224 if ($this->redirectUrl !== '') { 225 $this->eventDispatcher->dispatch(new BeforeRedirectEvent($this->loginType, $this->redirectUrl)); 226 $this->redirectToUri($this->redirectUrl); 227 } 228 } 229 230 /** 231 * Handle forwards to overview and logout actions from login action 232 */ 233 protected function handleLoginForwards(): ?ResponseInterface 234 { 235 if ($this->shouldRedirectToOverview()) { 236 return (new ForwardResponse('overview'))->withArguments(['showLoginMessage' => true]); 237 } 238 239 if ($this->userAspect->isLoggedIn()) { 240 return (new ForwardResponse('logout'))->withArguments(['redirectPageLogout' => $this->settings['redirectPageLogout']]); 241 } 242 243 return null; 244 } 245 246 /** 247 * The permanent login checkbox should only be shown if permalogin is not deactivated (-1), 248 * not forced to be always active (2) and lifetime is greater than 0 249 * 250 * @return int 251 */ 252 protected function getPermaloginStatus(): int 253 { 254 $permaLogin = (int)$GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin']; 255 256 return $this->isPermaloginDisabled($permaLogin) ? -1 : $permaLogin; 257 } 258 259 protected function isPermaloginDisabled(int $permaLogin): bool 260 { 261 return $permaLogin > 1 262 || (int)($this->settings['showPermaLogin'] ?? 0) === 0 263 || $GLOBALS['TYPO3_CONF_VARS']['FE']['lifetime'] === 0; 264 } 265 266 /** 267 * Redirect to overview on login successful and setting showLogoutFormAfterLogin disabled 268 * 269 * @return bool 270 */ 271 protected function shouldRedirectToOverview(): bool 272 { 273 return $this->userAspect->isLoggedIn() 274 && ($this->loginType === LoginType::LOGIN) 275 && !($this->settings['showLogoutFormAfterLogin'] ?? 0); 276 } 277 278 /** 279 * Return message key based on user login status 280 * 281 * @return string 282 */ 283 protected function getStatusMessageKey(): string 284 { 285 $messageKey = self::MESSAGEKEY_DEFAULT; 286 if ($this->hasLoginErrorOccurred()) { 287 $messageKey = self::MESSAGEKEY_ERROR; 288 } elseif ($this->loginType === LoginType::LOGOUT) { 289 $messageKey = self::MESSAGEKEY_LOGOUT; 290 } 291 292 return $messageKey; 293 } 294 295 protected function isLoginOrLogoutInProgress(): bool 296 { 297 return $this->loginType === LoginType::LOGIN || $this->loginType === LoginType::LOGOUT; 298 } 299 300 /** 301 * Is redirect disabled by setting or noredirect parameter 302 * 303 * @return bool 304 */ 305 public function isRedirectDisabled(): bool 306 { 307 return 308 $this->request->hasArgument('noredirect') 309 || ($this->settings['noredirect'] ?? false) 310 || ($this->settings['redirectDisable'] ?? false); 311 } 312 313 protected function isLogoutSuccessful(): bool 314 { 315 return $this->loginType === LoginType::LOGOUT && !$this->userAspect->isLoggedIn(); 316 } 317 318 protected function hasLoginErrorOccurred(): bool 319 { 320 return $this->loginType === LoginType::LOGIN && !$this->userAspect->isLoggedIn(); 321 } 322} 323