[FEATURE] Add Contexts for storing data access modes
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Middleware / FrontendUserAuthenticator.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\Middleware;
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
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use Psr\Http\Server\MiddlewareInterface;
21 use Psr\Http\Server\RequestHandlerInterface;
22 use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
23 use TYPO3\CMS\Core\Context\Context;
24 use TYPO3\CMS\Core\Context\UserAspect;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
27
28 /**
29 * This middleware authenticates a Frontend User (fe_users).
30 * A valid $GLOBALS['TSFE'] object is needed for the time being, being fully backwards-compatible.
31 */
32 class FrontendUserAuthenticator implements MiddlewareInterface
33 {
34 /**
35 * Creates a frontend user authentication object, tries to authenticate a user
36 * and stores the object in $GLOBALS['TSFE']->fe_user.
37 *
38 * @param ServerRequestInterface $request
39 * @param RequestHandlerInterface $handler
40 * @return ResponseInterface
41 */
42 public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
43 {
44 $frontendUser = GeneralUtility::makeInstance(FrontendUserAuthentication::class);
45
46 // List of page IDs where to look for frontend user records
47 $pid = $request->getParsedBody()['pid'] ?? $request->getQueryParams()['pid'] ?? 0;
48 if ($pid) {
49 $frontendUser->checkPid_value = implode(',', GeneralUtility::intExplode(',', $pid));
50 }
51
52 // Check if a session is transferred, and update the cookie parameters
53 $frontendSessionKey = $request->getParsedBody()['FE_SESSION_KEY'] ?? $request->getQueryParams()['FE_SESSION_KEY'] ?? '';
54 if ($frontendSessionKey) {
55 $request = $this->transferFrontendUserSession($frontendUser, $request, $frontendSessionKey);
56 }
57
58 // Authenticate now
59 $frontendUser->start();
60 $frontendUser->unpack_uc();
61
62 // Keep the backwards-compatibility for TYPO3 v9, to have the fe_user within the global TSFE object
63 $GLOBALS['TSFE']->fe_user = $frontendUser;
64
65 // Call hook for possible manipulation of frontend user object
66 // This hook is kept for compatibility reasons, however, it should be fairly simple to add a custom middleware
67 // for this purpose
68 $_params = ['pObj' => &$GLOBALS['TSFE']];
69 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'] ?? [] as $_funcRef) {
70 GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']);
71 }
72
73 // Register the frontend user as aspect
74 $this->setFrontendUserAspect(GeneralUtility::makeInstance(Context::class), $frontendUser);
75
76 return $handler->handle($request);
77 }
78
79 /**
80 * It's possible to transfer a frontend user session via a GET/POST parameter 'FE_SESSION_KEY'.
81 * In the future, this logic should be moved into the FrontendUserAuthentication object directly,
82 * but only if FrontendUserAuthentication does not request superglobals (like $_COOKIE) anymore.
83 *
84 * @param FrontendUserAuthentication $frontendUser
85 * @param ServerRequestInterface $request
86 * @param string $frontendSessionKey
87 * @return ServerRequestInterface
88 */
89 protected function transferFrontendUserSession(
90 FrontendUserAuthentication $frontendUser,
91 ServerRequestInterface $request,
92 string $frontendSessionKey
93 ): ServerRequestInterface {
94 list($sessionId, $hash) = explode('-', $frontendSessionKey);
95 // If the session key hash check is OK, set the cookie
96 if (md5($sessionId . '/' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) === (string)$hash) {
97 $cookieName = FrontendUserAuthentication::getCookieName();
98
99 // keep the global cookie overwriting for now, as long as FrontendUserAuthentication does not
100 // use the request object for fetching the cookie information.
101 $_COOKIE[$cookieName] = $sessionId;
102 if (isset($_SERVER['HTTP_COOKIE'])) {
103 // See http://forge.typo3.org/issues/27740
104 $_SERVER['HTTP_COOKIE'] .= ';' . $cookieName . '=' . $sessionId;
105 }
106 // Add the cookie to the Server Request object
107 $cookieParams = $request->getCookieParams();
108 $cookieParams[$cookieName] = $sessionId;
109 $request = $request->withCookieParams($cookieParams);
110 // @deprecated: we override the current request because it was enriched by cookie information here.
111 $GLOBALS['TYPO3_REQUEST'] = $request;
112 $frontendUser->forceSetCookie = true;
113 $frontendUser->dontSetCookie = false;
114 }
115 return $request;
116 }
117
118 /**
119 * Register the frontend user as aspect
120 *
121 * @param Context $context
122 * @param AbstractUserAuthentication $user
123 */
124 protected function setFrontendUserAspect(Context $context, AbstractUserAuthentication $user)
125 {
126 $context->setAspect('frontend.user', GeneralUtility::makeInstance(UserAspect::class, $user, $user === null ? [0, -1] : null));
127 }
128 }