getAttribute('route'); $enforceReferrerResponse = $this->enforceReferrer($request, $route); if ($enforceReferrerResponse !== null) { return $enforceReferrerResponse; } // Ensure that a token exists, and the token is requested, if the route requires a valid token $this->assertRequestToken($request, $route); // Ensure that sudo-mode is active, if the route requires it $this->assertSudoMode($request); $targetIdentifier = $route->getOption('target'); $target = $this->getCallableFromTarget($targetIdentifier); $arguments = [$request]; try { return $target(...$arguments); } catch (MethodNotAllowedException $exception) { return $exception->createResponse(); } } /** * Evaluates HTTP `Referer` header (which is denied by client to be a custom * value) - attempts to ensure the value is given using a HTML client refresh. * see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer */ protected function enforceReferrer(ServerRequestInterface $request, Route $route): ?ResponseInterface { if (!$this->features->isFeatureEnabled('security.backend.enforceReferrer')) { return null; } $referrerFlags = GeneralUtility::trimExplode(',', $route->getOption('referrer') ?? '', true); if (!in_array('required', $referrerFlags, true)) { return null; } return $this->referrerEnforcer->handle( $request, [ 'flags' => $referrerFlags, 'subject' => $route->getPath(), ] ); } /** * Checks if the request token is valid. This is checked to see if the route is really * created by the same instance. Should be called for all routes in the backend except * for the ones that don't require a login. * * @see UriBuilder where the token is generated. */ protected function assertRequestToken(ServerRequestInterface $request, Route $route): void { if ($route->getOption('access') === 'public') { return; } $token = (string)($request->getParsedBody()['token'] ?? $request->getQueryParams()['token'] ?? ''); if (empty($token)) { throw new MissingRequestTokenException( sprintf('Invalid request for route "%s"', $route->getPath()), 1627905246 ); } $formProtection = $this->formProtectionFactory->createFromRequest($request); if (!$formProtection->validateToken($token, 'route', $route->getOption('_identifier'))) { throw new InvalidRequestTokenException( sprintf('Invalid request for route "%s"', $route->getPath()), 1425389455 ); } } /** * Asserts that sudo mode verification was processed for this route before * and that it did not expire, yet. In case (re-)verification is required, * a corresponding `AccessClaim` is persisted in the user session storage, * and the process of showing the verification dialogs is initiated. */ protected function assertSudoMode(ServerRequestInterface $request): void { // #93160: [TASK] Do not require sudo mode in development context if (Environment::getContext()->isDevelopment()) { return; } /** @var ?Route $route */ $route = $request->getAttribute('route'); $settings = $route?->getOption('sudoMode') ?? null; if (!is_array($settings)) { return; } // sudo mode settings for subject are fetched from the request again $subject = $this->factory->buildRouteAccessSubject($request); if ($this->storage->findGrantsBySubject($subject)) { return; } // reuse existing matching claim, or create a new one $claim = $this->storage->findClaimBySubject($subject) ?? $this->factory->buildClaimForSubjectRequest($request, self::class, $subject); $event = $this->eventDispatcher->dispatch(new SudoModeRequiredEvent($claim)); if ($event->isVerificationRequired()) { throw (new VerificationRequiredException( 'Sudo Mode Confirmation Required', 1605812020 ))->withClaim($claim); } } }