b85ba120730b4e50f7ccf8b0016818587e9421b1
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Http / AjaxRequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Backend\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Core\Bootstrap;
18 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use Psr\Http\Message\ServerRequestInterface;
21
22 /**
23 * AJAX dispatcher
24 *
25 * Main entry point for AJAX calls in the TYPO3 Backend. Based on ?ajaxId of the outside application.
26 * Before doing the basic BE-related set up of this request (see the additional calls on $this->bootstrap inside
27 * handleRequest()), some AJAX-calls can be made without a valid user, which is determined here.
28 * See $GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'] and the Core APIs on how to register an AJAX call in the TYPO3 Backend.
29 *
30 * Due to legacy reasons, the actual logic is in EXT:core/Http/AjaxRequestHandler which will eventually
31 * be moved into this class.
32 * In the future, the logic for "TYPO3_PROCEED_IF_NO_USER" will be moved in here as well.
33 */
34 class AjaxRequestHandler implements RequestHandlerInterface {
35
36 /**
37 * Instance of the current TYPO3 bootstrap
38 * @var Bootstrap
39 */
40 protected $bootstrap;
41
42 /**
43 * List of requests that don't need a valid BE user
44 * @var array
45 */
46 protected $publicAjaxIds = array(
47 'BackendLogin::login',
48 'BackendLogin::logout',
49 'BackendLogin::refreshLogin',
50 'BackendLogin::isTimedOut',
51 'BackendLogin::getChallenge',
52 'BackendLogin::getRsaPublicKey',
53 'RsaEncryption::getRsaPublicKey'
54 );
55
56 /**
57 * Constructor handing over the bootstrap and the original request
58 *
59 * @param Bootstrap $bootstrap
60 */
61 public function __construct(Bootstrap $bootstrap) {
62 $this->bootstrap = $bootstrap;
63 }
64
65 /**
66 * Handles any AJAX request in the TYPO3 Backend
67 *
68 * @param ServerRequestInterface $request
69 * @return NULL|\Psr\Http\Message\ResponseInterface
70 */
71 public function handleRequest(ServerRequestInterface $request) {
72 // First get the ajaxID
73 $ajaxID = isset($request->getParsedBody()['ajaxID']) ? $request->getParsedBody()['ajaxID'] : $request->getQueryParams()['ajaxID'];
74
75 // used for backwards-compatibility
76 $GLOBALS['ajaxID'] = $ajaxID;
77 $this->boot($ajaxID);
78
79 // Finding the script path from the registry
80 $ajaxRegistryEntry = isset($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID]) ? $GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID] : NULL;
81 $ajaxScript = NULL;
82 $csrfTokenCheck = FALSE;
83 if ($ajaxRegistryEntry !== NULL && is_array($ajaxRegistryEntry) && isset($ajaxRegistryEntry['callbackMethod'])) {
84 $ajaxScript = $ajaxRegistryEntry['callbackMethod'];
85 $csrfTokenCheck = $ajaxRegistryEntry['csrfTokenCheck'];
86 }
87
88 // Instantiating the AJAX object
89 $ajaxObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\AjaxRequestHandler::class, $ajaxID);
90 $ajaxParams = array();
91
92 // Evaluating the arguments and calling the AJAX method/function
93 if (empty($ajaxID)) {
94 $ajaxObj->setError('No valid ajaxID parameter given.');
95 } elseif (empty($ajaxScript)) {
96 $ajaxObj->setError('No backend function registered for ajaxID "' . $ajaxID . '".');
97 } else {
98 $success = TRUE;
99 $tokenIsValid = TRUE;
100 if ($csrfTokenCheck) {
101 $ajaxToken = $request->getParsedBody()['ajaxToken'] ?: $request->getQueryParams()['ajaxToken'];
102 $tokenIsValid = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get()->validateToken($ajaxToken, 'ajaxCall', $ajaxID);
103 }
104 if ($tokenIsValid) {
105 // Cleanup global variable space
106 unset($csrfTokenCheck, $ajaxRegistryEntry, $tokenIsValid, $success);
107 $success = GeneralUtility::callUserFunction($ajaxScript, $ajaxParams, $ajaxObj, FALSE, TRUE);
108 } else {
109 $ajaxObj->setError('Invalid CSRF token detected for ajaxID "' . $ajaxID . '"!');
110 }
111 if ($success === FALSE) {
112 $ajaxObj->setError('Registered backend function for ajaxID "' . $ajaxID . '" was not found.');
113 }
114 }
115
116 // Outputting the content (and setting the X-JSON-Header)
117 $ajaxObj->render();
118
119 return NULL;
120 }
121
122 /**
123 * This request handler can handle any backend request coming from ajax.php or having
124 * a ajaxID as parameter (see Application.php in EXT:backend)
125 *
126 * @param ServerRequestInterface $request
127 * @return bool If the request is an AJAX backend request, TRUE otherwise FALSE
128 */
129 public function canHandleRequest(ServerRequestInterface $request) {
130 return $request->getAttribute('isAjaxRequest', FALSE);
131 }
132
133 /**
134 * Returns the priority - how eager the handler is to actually handle the request.
135 *
136 * @return int The priority of the request handler.
137 */
138 public function getPriority() {
139 return 80;
140 }
141
142 /**
143 * Start the Backend bootstrap part
144 *
145 * @param string $ajaxId Contains the string of the ajaxId used
146 */
147 protected function boot($ajaxId) {
148 // If we're trying to do an ajax login, don't require a user
149 $proceedIfNoUserIsLoggedIn = in_array($ajaxId, $this->publicAjaxIds, TRUE);
150
151 $this->bootstrap
152 ->checkLockedBackendAndRedirectOrDie($proceedIfNoUserIsLoggedIn)
153 ->checkBackendIpOrDie()
154 ->checkSslBackendAndRedirectIfNeeded()
155 ->loadExtensionTables(TRUE)
156 ->initializeSpriteManager()
157 ->initializeBackendUser()
158 ->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
159 ->initializeLanguageObject()
160 ->initializeBackendTemplate()
161 ->endOutputBufferingAndCleanPreviousOutput()
162 ->initializeOutputCompression()
163 ->sendHttpHeaders();
164 }
165 }