From ceee1287a04c03fb304f300db8a45c4f196b91e9 Mon Sep 17 00:00:00 2001 From: Benni Mack <benni@typo3.org> Date: Thu, 13 Jul 2017 16:38:44 +0200 Subject: [PATCH] [TASK] Use &route=/ajax/ instead of &ajaxId for BE AJAX calls In order to streamline backend usage even further, the AJAX Request Handler is now using the "route" parameter the same way. This way, the RouteDispatcher does not have to distinguish between the BE calls anymore, and the Form Protections are streamlined. Resolves: #81899 Releases: master Change-Id: I48bf2406eaff2316d3f0fe5dc631a51067a570f6 Reviewed-on: https://review.typo3.org/53459 Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Joerg Boesche <typo3@joergboesche.de> Tested-by: Joerg Boesche <typo3@joergboesche.de> Reviewed-by: Susanne Moog <susanne.moog@typo3.org> Tested-by: Susanne Moog <susanne.moog@typo3.org> --- .../Classes/Http/AjaxRequestHandler.php | 24 +++++++------- .../backend/Classes/Http/Application.php | 7 ++-- .../backend/Classes/Http/RouteDispatcher.php | 8 ++--- .../backend/Classes/Routing/UriBuilder.php | 32 +++++-------------- .../Resources/Public/JavaScript/LegacyTree.js | 4 +-- .../Tests/Unit/Routing/UriBuilderTest.php | 6 ++-- ...esUseRouteajaxInsteadOfAjaxIdParameter.rst | 18 +++++++++++ 7 files changed, 48 insertions(+), 51 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Important-81899-BackendAJAXRoutesUseRouteajaxInsteadOfAjaxIdParameter.rst diff --git a/typo3/sysext/backend/Classes/Http/AjaxRequestHandler.php b/typo3/sysext/backend/Classes/Http/AjaxRequestHandler.php index cedc02bce046..e2145c7a3e67 100644 --- a/typo3/sysext/backend/Classes/Http/AjaxRequestHandler.php +++ b/typo3/sysext/backend/Classes/Http/AjaxRequestHandler.php @@ -26,7 +26,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; /** * AJAX dispatcher * - * Main entry point for AJAX calls in the TYPO3 Backend. Based on ?ajaxId of the outside application. + * Main entry point for AJAX calls in the TYPO3 Backend. Based on ?route=/ajax/* of the outside application. * 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. * @@ -44,7 +44,7 @@ class AjaxRequestHandler implements RequestHandlerInterface * List of requests that don't need a valid BE user * @var array */ - protected $publicAjaxIds = [ + protected $publicAjaxRoutes = [ '/ajax/login', '/ajax/logout', '/ajax/login/refresh', @@ -70,10 +70,11 @@ class AjaxRequestHandler implements RequestHandlerInterface */ public function handleRequest(ServerRequestInterface $request) { - // First get the ajaxID - $ajaxID = isset($request->getParsedBody()['ajaxID']) ? $request->getParsedBody()['ajaxID'] : $request->getQueryParams()['ajaxID']; - $request = $request->withAttribute('routePath', $ajaxID); - $proceedIfNoUserIsLoggedIn = $this->isLoggedInBackendUserRequired($ajaxID); + // First get the name of the route + $routePath = isset($request->getParsedBody()['route']) ? $request->getParsedBody()['route'] : $request->getQueryParams()['route']; + $request = $request->withAttribute('routePath', $routePath); + + $proceedIfNoUserIsLoggedIn = $this->isLoggedInBackendUserRequired($routePath); $this->boot($proceedIfNoUserIsLoggedIn); // Backend Routing - check if a valid route is there, and dispatch @@ -82,14 +83,15 @@ class AjaxRequestHandler implements RequestHandlerInterface /** * This request handler can handle any backend request having - * an ajaxID as parameter (see Application.php in EXT:backend) + * an /ajax/ request * * @param ServerRequestInterface $request * @return bool If the request is an AJAX backend request, TRUE otherwise FALSE */ public function canHandleRequest(ServerRequestInterface $request) { - return $request->getAttribute('isAjaxRequest', false); + $routePath = isset($request->getParsedBody()['route']) ? $request->getParsedBody()['route'] : $request->getQueryParams()['route']; + return strpos($routePath, '/ajax/') === 0; } /** @@ -106,12 +108,12 @@ class AjaxRequestHandler implements RequestHandlerInterface * Check if the user is required for the request * If we're trying to do an ajax login, don't require a user * - * @param string $ajaxId the Ajax ID to check against + * @param string $routePath the Route path to check against, something like ' * @return bool whether the request can proceed without a login required */ - protected function isLoggedInBackendUserRequired($ajaxId) + protected function isLoggedInBackendUserRequired($routePath) { - return in_array($ajaxId, $this->publicAjaxIds, true); + return in_array($routePath, $this->publicAjaxRoutes, true); } /** diff --git a/typo3/sysext/backend/Classes/Http/Application.php b/typo3/sysext/backend/Classes/Http/Application.php index 859e382e4b2f..683a1b35f0dd 100644 --- a/typo3/sysext/backend/Classes/Http/Application.php +++ b/typo3/sysext/backend/Classes/Http/Application.php @@ -59,7 +59,7 @@ class Application implements ApplicationInterface $this->bootstrap = Bootstrap::getInstance() ->initializeClassLoader($classLoader) - ->setRequestType(TYPO3_REQUESTTYPE_BE | (!empty($_GET['ajaxID']) ? TYPO3_REQUESTTYPE_AJAX : 0)) + ->setRequestType(TYPO3_REQUESTTYPE_BE | (isset($_REQUEST['route']) && strpos($_REQUEST['route'], '/ajax/') === 0 ? TYPO3_REQUESTTYPE_AJAX : 0)) ->baseSetup($this->entryPointLevel); // Redirect to install tool if base configuration is not found @@ -82,10 +82,7 @@ class Application implements ApplicationInterface public function run(callable $execute = null) { $this->request = \TYPO3\CMS\Core\Http\ServerRequestFactory::fromGlobals(); - // see below when this option is set and Bootstrap::defineTypo3RequestTypes() for more details - if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) { - $this->request = $this->request->withAttribute('isAjaxRequest', true); - } elseif (isset($this->request->getQueryParams()['M'])) { + if (isset($this->request->getQueryParams()['M'])) { $this->request = $this->request->withAttribute('isModuleRequest', true); } diff --git a/typo3/sysext/backend/Classes/Http/RouteDispatcher.php b/typo3/sysext/backend/Classes/Http/RouteDispatcher.php index 66a32b8a11b1..957f85c785c7 100644 --- a/typo3/sysext/backend/Classes/Http/RouteDispatcher.php +++ b/typo3/sysext/backend/Classes/Http/RouteDispatcher.php @@ -78,12 +78,8 @@ class RouteDispatcher extends Dispatcher implements DispatcherInterface $route = $request->getAttribute('route'); if ($route->getOption('access') === 'public') { return true; - } elseif ($route->getOption('ajax')) { - $token = (string)(isset($request->getParsedBody()['ajaxToken']) ? $request->getParsedBody()['ajaxToken'] : $request->getQueryParams()['ajaxToken']); - return $this->getFormProtection()->validateToken($token, 'ajaxCall', $route->getOption('_identifier')); - } else { - $token = (string)(isset($request->getParsedBody()['token']) ? $request->getParsedBody()['token'] : $request->getQueryParams()['token']); - return $this->getFormProtection()->validateToken($token, 'route', $route->getOption('_identifier')); } + $token = (string)(isset($request->getParsedBody()['token']) ? $request->getParsedBody()['token'] : $request->getQueryParams()['token']); + return $this->getFormProtection()->validateToken($token, 'route', $route->getOption('_identifier')); } } diff --git a/typo3/sysext/backend/Classes/Routing/UriBuilder.php b/typo3/sysext/backend/Classes/Routing/UriBuilder.php index 7f546e5015be..93e5fadabb38 100644 --- a/typo3/sysext/backend/Classes/Routing/UriBuilder.php +++ b/typo3/sysext/backend/Classes/Routing/UriBuilder.php @@ -79,34 +79,18 @@ class UriBuilder $parameters ); - // The Route is an AJAX route, so the parameters are different in order - // for the AjaxRequestHandler to be triggered - if ($route->getOption('ajax')) { - // If the route has the "public" option set, no token is generated. - if ($route->getOption('access') !== 'public') { - $parameters = [ - 'ajaxToken' => FormProtectionFactory::get('backend')->generateToken('ajaxCall', $name) - ] + $parameters; - } - - // Add the Route path as &ajaxID=XYZ - $parameters = [ - 'ajaxID' => $route->getPath() - ] + $parameters; - } else { - // If the route has the "public" option set, no token is generated. - if ($route->getOption('access') !== 'public') { - $parameters = [ - 'token' => FormProtectionFactory::get('backend')->generateToken('route', $name) - ] + $parameters; - } - - // Add the Route path as &route=XYZ + // If the route has the "public" option set, no token is generated. + if ($route->getOption('access') !== 'public') { $parameters = [ - 'route' => $route->getPath() + 'token' => FormProtectionFactory::get('backend')->generateToken('route', $name) ] + $parameters; } + // Add the Route path as &route=XYZ + $parameters = [ + 'route' => $route->getPath() + ] + $parameters; + return $this->buildUri($parameters, $referenceType); } diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/LegacyTree.js b/typo3/sysext/backend/Resources/Public/JavaScript/LegacyTree.js index 13074e444184..2f1db7452ba5 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/LegacyTree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/LegacyTree.js @@ -107,7 +107,7 @@ define(['jquery'], function($) { }; Tree = { - ajaxID: 'sc_alt_file_navframe_expandtoggle', + ajaxRoute: 'sc_alt_file_navframe_expandtoggle', frameSetModule: null, activateDragDrop: true, highlightClass: 'active', @@ -133,7 +133,7 @@ define(['jquery'], function($) { $obj.css({cursor: 'wait'}); } $.ajax({ - url: TYPO3.settings.ajaxUrls[this.ajaxID], + url: TYPO3.settings.ajaxUrls[this.ajaxRoute], data: { PM: params, scopeData: scopeData, diff --git a/typo3/sysext/backend/Tests/Unit/Routing/UriBuilderTest.php b/typo3/sysext/backend/Tests/Unit/Routing/UriBuilderTest.php index 667c3487a63c..e4cf27da1fcd 100644 --- a/typo3/sysext/backend/Tests/Unit/Routing/UriBuilderTest.php +++ b/typo3/sysext/backend/Tests/Unit/Routing/UriBuilderTest.php @@ -57,7 +57,7 @@ class UriBuilderTest extends UnitTestCase [ 'route' => new Route('/test/route', [ 'ajax' => true ]) ], 'route', [], - '/typo3/index.php?ajaxID=%2Ftest%2Froute&ajaxToken=dummyToken', + '/typo3/index.php?route=%2Ftest%2Froute&token=dummyToken', ], 'plain route with default parameters' => [ [ 'route' => new Route('/test/route', [ 'parameters' => [ 'key' => 'value' ] ]) ], @@ -69,7 +69,7 @@ class UriBuilderTest extends UnitTestCase [ 'route' => new Route('/test/route', [ 'ajax' => true, 'parameters' => [ 'key' => 'value' ] ]) ], 'route', [], - '/typo3/index.php?ajaxID=%2Ftest%2Froute&ajaxToken=dummyToken&key=value', + '/typo3/index.php?route=%2Ftest%2Froute&token=dummyToken&key=value', ], 'plain route with overridden parameters' => [ [ 'route' => new Route('/test/route', [ 'parameters' => [ 'key' => 'value' ] ]) ], @@ -81,7 +81,7 @@ class UriBuilderTest extends UnitTestCase [ 'route' => new Route('/test/route', [ 'ajax' => true, 'parameters' => [ 'key' => 'value' ] ]) ], 'route', ['key' => 'overridden'], - '/typo3/index.php?ajaxID=%2Ftest%2Froute&ajaxToken=dummyToken&key=overridden', + '/typo3/index.php?route=%2Ftest%2Froute&token=dummyToken&key=overridden', ], ]; } diff --git a/typo3/sysext/core/Documentation/Changelog/master/Important-81899-BackendAJAXRoutesUseRouteajaxInsteadOfAjaxIdParameter.rst b/typo3/sysext/core/Documentation/Changelog/master/Important-81899-BackendAJAXRoutesUseRouteajaxInsteadOfAjaxIdParameter.rst new file mode 100644 index 000000000000..e5c9fbe065ce --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Important-81899-BackendAJAXRoutesUseRouteajaxInsteadOfAjaxIdParameter.rst @@ -0,0 +1,18 @@ +.. include:: ../../Includes.txt + +========================================================================================= +Important: #81899 - Backend AJAX routes use "&route=/ajax/" instead of "ajaxId" parameter +========================================================================================= + +See :issue:`81899` + +Description +=========== + +The TYPO3 Backend uses AJAX calls by calling routes with the ``&route=/ajax/*`` GET/POST parameter +now instead of the "&ajaxId" GET/POST parameter. + +Although this is not a breaking change, some PHP code might rely on GET/POST parameters being +set, and must check for the route parameter instead. + +.. index:: Backend \ No newline at end of file -- GitLab