Passed
Push — master ( f16b47...733353 )
by
unknown
13:48
created

BackendUserAuthenticator   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
eloc 43
c 1
b 0
f 0
dl 0
loc 110
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A enrichResponseWithHeadersAndCookieInformation() 0 16 3
A process() 0 34 4
A isLoggedInBackendUserRequired() 0 3 1
A sessionGarbageCollection() 0 3 1
1
<?php
2
3
declare(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
18
namespace TYPO3\CMS\Backend\Middleware;
19
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use Psr\Http\Server\RequestHandlerInterface;
23
use TYPO3\CMS\Backend\Routing\Route;
24
use TYPO3\CMS\Backend\Routing\UriBuilder;
25
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
26
use TYPO3\CMS\Core\Http\ImmediateResponseException;
27
use TYPO3\CMS\Core\Http\RedirectResponse;
28
use TYPO3\CMS\Core\Localization\LanguageService;
29
use TYPO3\CMS\Core\Session\UserSessionManager;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32
/**
33
 * Initializes the backend user authentication object (BE_USER) and the global LANG object.
34
 *
35
 * @internal
36
 */
37
class BackendUserAuthenticator extends \TYPO3\CMS\Core\Middleware\BackendUserAuthenticator
38
{
39
    /**
40
     * List of requests that don't need a valid BE user
41
     *
42
     * @var array
43
     */
44
    protected $publicRoutes = [
45
        '/login',
46
        '/login/frame',
47
        '/login/password-reset/forget',
48
        '/login/password-reset/initiate-reset',
49
        '/login/password-reset/validate',
50
        '/login/password-reset/finish',
51
        '/ajax/login',
52
        '/ajax/logout',
53
        '/ajax/login/refresh',
54
        '/ajax/login/timedout',
55
        '/ajax/rsa/publickey',
56
        '/ajax/core/requirejs',
57
    ];
58
59
    /**
60
     * Calls the bootstrap process to set up $GLOBALS['BE_USER'] AND $GLOBALS['LANG']
61
     *
62
     * @param ServerRequestInterface $request
63
     * @param RequestHandlerInterface $handler
64
     * @return ResponseInterface
65
     */
66
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
67
    {
68
        $route = $request->getAttribute('route');
69
70
        // The global must be available very early, because methods below
71
        // might trigger code which relies on it. See: #45625
72
        $GLOBALS['BE_USER'] = GeneralUtility::makeInstance(BackendUserAuthentication::class);
73
        $GLOBALS['BE_USER']->start();
74
        // Register the backend user as aspect and initializing workspace once for TSconfig conditions
75
        $this->setBackendUserAspect($GLOBALS['BE_USER'], (int)$GLOBALS['BE_USER']->user['workspace_id']);
76
        if ($this->isLoggedInBackendUserRequired($route) && !$this->context->getAspect('backend.user')->isLoggedIn()) {
0 ignored issues
show
Bug introduced by
The method isLoggedIn() does not exist on TYPO3\CMS\Core\Context\AspectInterface. It seems like you code against a sub-type of TYPO3\CMS\Core\Context\AspectInterface such as TYPO3\CMS\Core\Context\UserAspect. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

76
        if ($this->isLoggedInBackendUserRequired($route) && !$this->context->getAspect('backend.user')->/** @scrutinizer ignore-call */ isLoggedIn()) {
Loading history...
77
            $uri = GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('login');
78
            $response = new RedirectResponse($uri);
79
            return $this->enrichResponseWithHeadersAndCookieInformation($response, $GLOBALS['BE_USER']);
80
        }
81
        try {
82
            $proceedIfNoUserIsLoggedIn = $this->isLoggedInBackendUserRequired($route) === false;
83
            // @todo: Ensure that the runtime exceptions are caught
84
            $GLOBALS['BE_USER']->backendCheckLogin($proceedIfNoUserIsLoggedIn);
85
            $GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
86
            // Re-setting the user and take the workspace from the user object now
87
            $this->setBackendUserAspect($GLOBALS['BE_USER']);
88
            $response = $handler->handle($request);
89
90
            $this->sessionGarbageCollection();
91
        } catch (ImmediateResponseException $e) {
92
            $response = $this->enrichResponseWithHeadersAndCookieInformation(
93
                $e->getResponse(),
94
                $GLOBALS['BE_USER']
95
            );
96
            // Re-throw this exception
97
            throw new ImmediateResponseException($response, $e->getCode());
98
        }
99
        return $this->enrichResponseWithHeadersAndCookieInformation($response, $GLOBALS['BE_USER']);
100
    }
101
102
    /**
103
     * Backend requests should always apply Set-Cookie information and never be cacheable.
104
     * This is also needed if there is a redirect from somewhere in the code.
105
     *
106
     * @param ResponseInterface $response
107
     * @param BackendUserAuthentication|null $userAuthentication
108
     * @return ResponseInterface
109
     * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
110
     */
111
    protected function enrichResponseWithHeadersAndCookieInformation(
112
        ResponseInterface $response,
113
        ?BackendUserAuthentication $userAuthentication
114
    ): ResponseInterface {
115
        if ($userAuthentication) {
116
            // If no backend user is logged-in, the cookie should be removed
117
            if (!$this->context->getAspect('backend.user')->isLoggedIn()) {
118
                $userAuthentication->removeCookie();
119
            }
120
            // Ensure to always apply a cookie
121
            $response = $userAuthentication->appendCookieToResponse($response);
122
        }
123
        // Additional headers to never cache any PHP request should be sent at any time when
124
        // accessing the TYPO3 Backend
125
        $response = $this->applyHeadersToResponse($response);
126
        return $response;
127
    }
128
129
    /**
130
     * Garbage collection for be_sessions (with a probability)
131
     */
132
    protected function sessionGarbageCollection(): void
133
    {
134
        UserSessionManager::create('BE')->collectGarbage();
135
    }
136
137
    /**
138
     * Check if the user is required for the request.
139
     * If we're trying to do a login or an ajax login, don't require a user.
140
     *
141
     * @param Route $route the Route path to check against, something like '
142
     * @return bool true when the Route requires an authenticated backend user
143
     */
144
    protected function isLoggedInBackendUserRequired(Route $route): bool
145
    {
146
        return in_array($route->getPath(), $this->publicRoutes, true) === false;
147
    }
148
}
149