AjaxRequestHandler.php 5.58 KB
Newer Older
1
<?php
2
namespace TYPO3\CMS\Backend\Http;
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

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
24
25
 * AJAX dispatcher
 *
 * Main entry point for AJAX calls in the TYPO3 Backend. Based on ?ajaxId of the outside application.
26
27
 * Before doing the basic BE-related set up of this request (see the additional calls on $this->bootstrap inside
 * 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
31
32
33
34
35
36
37
38
39
40
41
 *
 * Due to legacy reasons, the actual logic is in EXT:core/Http/AjaxRequestHandler which will eventually
 * be moved into this class.
 * In the future, the logic for "TYPO3_PROCEED_IF_NO_USER" will be moved in here as well.
 */
class AjaxRequestHandler implements RequestHandlerInterface {

	/**
	 * Instance of the current TYPO3 bootstrap
	 * @var Bootstrap
	 */
	protected $bootstrap;

42
43
44
45
46
47
48
49
50
51
	/**
	 * List of requests that don't need a valid BE user
	 * @var array
	 */
	protected $publicAjaxIds = array(
		'BackendLogin::login',
		'BackendLogin::logout',
		'BackendLogin::refreshLogin',
		'BackendLogin::isTimedOut',
		'BackendLogin::getChallenge',
52
53
		'BackendLogin::getRsaPublicKey',
		'RsaEncryption::getRsaPublicKey'
54
55
	);

56
	/**
57
	 * Constructor handing over the bootstrap and the original request
58
59
60
61
62
63
64
65
66
67
	 *
	 * @param Bootstrap $bootstrap
	 */
	public function __construct(Bootstrap $bootstrap) {
		$this->bootstrap = $bootstrap;
	}

	/**
	 * Handles any AJAX request in the TYPO3 Backend
	 *
68
69
	 * @param ServerRequestInterface $request
	 * @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
80
81
82

		// Finding the script path from the registry
		$ajaxRegistryEntry = isset($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID]) ? $GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID] : NULL;
		$ajaxScript = NULL;
		$csrfTokenCheck = FALSE;
83
84
85
		if ($ajaxRegistryEntry !== NULL && is_array($ajaxRegistryEntry) && isset($ajaxRegistryEntry['callbackMethod'])) {
			$ajaxScript = $ajaxRegistryEntry['callbackMethod'];
			$csrfTokenCheck = $ajaxRegistryEntry['csrfTokenCheck'];
86
87
88
89
		}

		// Instantiating the AJAX object
		$ajaxObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\AjaxRequestHandler::class, $ajaxID);
90
		$ajaxParams = array('request' => $request);
91
92
93
94
95
96
97
98
99
100

		// Evaluating the arguments and calling the AJAX method/function
		if (empty($ajaxID)) {
			$ajaxObj->setError('No valid ajaxID parameter given.');
		} elseif (empty($ajaxScript)) {
			$ajaxObj->setError('No backend function registered for ajaxID "' . $ajaxID . '".');
		} else {
			$success = TRUE;
			$tokenIsValid = TRUE;
			if ($csrfTokenCheck) {
101
102
				$ajaxToken = $request->getParsedBody()['ajaxToken'] ?: $request->getQueryParams()['ajaxToken'];
				$tokenIsValid = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get()->validateToken($ajaxToken, 'ajaxCall', $ajaxID);
103
104
105
106
107
108
109
110
111
112
113
114
115
116
			}
			if ($tokenIsValid) {
				// Cleanup global variable space
				unset($csrfTokenCheck, $ajaxRegistryEntry, $tokenIsValid, $success);
				$success = GeneralUtility::callUserFunction($ajaxScript, $ajaxParams, $ajaxObj, FALSE, TRUE);
			} else {
				$ajaxObj->setError('Invalid CSRF token detected for ajaxID "' . $ajaxID . '"!');
			}
			if ($success === FALSE) {
				$ajaxObj->setError('Registered backend function for ajaxID "' . $ajaxID . '" was not found.');
			}
		}

		// Outputting the content (and setting the X-JSON-Header)
117
		return $ajaxObj->render();
118
119
120
	}

	/**
121
	 * This request handler can handle any backend request coming from ajax.php or having
122
	 * an ajaxID as parameter (see Application.php in EXT:backend)
123
	 *
124
	 * @param ServerRequestInterface $request
125
126
	 * @return bool If the request is an AJAX backend request, TRUE otherwise FALSE
	 */
127
	public function canHandleRequest(ServerRequestInterface $request) {
128
		return $request->getAttribute('isAjaxRequest', FALSE);
129
130
131
132
133
134
135
136
137
138
	}

	/**
	 * Returns the priority - how eager the handler is to actually handle the request.
	 *
	 * @return int The priority of the request handler.
	 */
	public function getPriority() {
		return 80;
	}
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

	/**
	 * Start the Backend bootstrap part
	 *
	 * @param string $ajaxId Contains the string of the ajaxId used
	 */
	protected function boot($ajaxId) {
		// If we're trying to do an ajax login, don't require a user
		$proceedIfNoUserIsLoggedIn = in_array($ajaxId, $this->publicAjaxIds, TRUE);

		$this->bootstrap
			->checkLockedBackendAndRedirectOrDie($proceedIfNoUserIsLoggedIn)
			->checkBackendIpOrDie()
			->checkSslBackendAndRedirectIfNeeded()
			->loadExtensionTables(TRUE)
			->initializeSpriteManager()
			->initializeBackendUser()
			->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
			->initializeLanguageObject()
			->initializeBackendTemplate()
			->endOutputBufferingAndCleanPreviousOutput()
			->initializeOutputCompression()
			->sendHttpHeaders();
	}
163
}