[!!!][TASK] Remove non-route variants for BE calls
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Http / RouteDispatcher.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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Routing\Exception\InvalidRequestTokenException;
20 use TYPO3\CMS\Backend\Routing\Route;
21 use TYPO3\CMS\Backend\Routing\Router;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
24 use TYPO3\CMS\Core\Http\Dispatcher;
25 use TYPO3\CMS\Core\Type\Bitmask\Permission;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\CMS\Core\Utility\MathUtility;
28
29 /**
30 * Dispatcher which resolves a route to call a controller and method (but also a callable)
31 */
32 class RouteDispatcher extends Dispatcher
33 {
34 /**
35 * Main method to resolve the route and checks the target of the route, and tries to call it.
36 *
37 * @param ServerRequestInterface $request the current server request
38 * @return ResponseInterface the filled response by the callable / controller/action
39 * @throws InvalidRequestTokenException if the route was not found
40 * @throws \InvalidArgumentException if the defined target for the route is invalid
41 */
42 public function dispatch(ServerRequestInterface $request): ResponseInterface
43 {
44 $router = GeneralUtility::makeInstance(Router::class);
45 $route = $router->matchRequest($request);
46 $request = $request->withAttribute('route', $route);
47 $request = $request->withAttribute('target', $route->getOption('target'));
48 if (!$this->isValidRequest($request)) {
49 throw new InvalidRequestTokenException('Invalid request for route "' . $route->getPath() . '"', 1425389455);
50 }
51
52 if ($route->getOption('module')) {
53 $this->addAndValidateModuleConfiguration($request, $route);
54 }
55 $targetIdentifier = $route->getOption('target');
56 $target = $this->getCallableFromTarget($targetIdentifier);
57 $arguments = [$request];
58 return call_user_func_array($target, $arguments);
59 }
60
61 /**
62 * Wrapper method for static form protection utility
63 *
64 * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection
65 */
66 protected function getFormProtection()
67 {
68 return FormProtectionFactory::get();
69 }
70
71 /**
72 * Checks if the request token is valid. This is checked to see if the route is really
73 * created by the same instance. Should be called for all routes in the backend except
74 * for the ones that don't require a login.
75 *
76 * @param \Psr\Http\Message\ServerRequestInterface $request
77 * @return bool
78 * @see \TYPO3\CMS\Backend\Routing\UriBuilder where the token is generated.
79 */
80 protected function isValidRequest($request)
81 {
82 $route = $request->getAttribute('route');
83 if ($route->getOption('access') === 'public') {
84 return true;
85 }
86 $token = (string)($request->getParsedBody()['token'] ?? $request->getQueryParams()['token']);
87 if ($token) {
88 return $this->getFormProtection()->validateToken($token, 'route', $route->getOption('_identifier'));
89 }
90 return false;
91 }
92
93 /**
94 * Adds configuration for a module and checks module permissions for the
95 * current user.
96 *
97 * @param ServerRequestInterface $request
98 * @param Route $route
99 * @throws \RuntimeException
100 */
101 protected function addAndValidateModuleConfiguration(ServerRequestInterface $request, Route $route)
102 {
103 $moduleName = $route->getOption('moduleName');
104 $moduleConfiguration = $this->getModuleConfiguration($moduleName);
105 $route->setOption('moduleConfiguration', $moduleConfiguration);
106
107 $backendUserAuthentication = $GLOBALS['BE_USER'];
108
109 // Check permissions and exit if the user has no permission for entry
110 $backendUserAuthentication->modAccess($moduleConfiguration);
111 $id = $request->getQueryParams()['id'] ?? $request->getParsedBody()['id'];
112 if (MathUtility::canBeInterpretedAsInteger($id) && $id > 0) {
113 $permClause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
114 // Check page access
115 if (!is_array(BackendUtility::readPageAccess($id, $permClause))) {
116 // Check if page has been deleted
117 $deleteField = $GLOBALS['TCA']['pages']['ctrl']['delete'];
118 $pageInfo = BackendUtility::getRecord('pages', $id, $deleteField, $permClause ? ' AND ' . $permClause : '', false);
119 if (!$pageInfo[$deleteField]) {
120 throw new \RuntimeException('You don\'t have access to this page', 1289917924);
121 }
122 }
123 }
124 }
125
126 /**
127 * Returns the module configuration which is provided during module registration
128 *
129 * @param string $moduleName
130 * @return array
131 * @throws \RuntimeException
132 */
133 protected function getModuleConfiguration($moduleName)
134 {
135 if (!isset($GLOBALS['TBE_MODULES']['_configuration'][$moduleName])) {
136 throw new \RuntimeException('Module ' . $moduleName . ' is not configured.', 1289918325);
137 }
138 return $GLOBALS['TBE_MODULES']['_configuration'][$moduleName];
139 }
140 }