RouteProcessorCsrf.php

Same filename in other branches
  1. 8.9.x core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
  2. 10 core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
  3. 11.x core/lib/Drupal/Core/Access/RouteProcessorCsrf.php

Namespace

Drupal\Core\Access

File

core/lib/Drupal/Core/Access/RouteProcessorCsrf.php

View source
<?php

namespace Drupal\Core\Access;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
use Symfony\Component\Routing\Route;

/**
 * Processes the outbound route to handle the CSRF token.
 */
class RouteProcessorCsrf implements OutboundRouteProcessorInterface, TrustedCallbackInterface {
    
    /**
     * The CSRF token generator.
     *
     * @var \Drupal\Core\Access\CsrfTokenGenerator
     */
    protected $csrfToken;
    
    /**
     * Constructs a RouteProcessorCsrf object.
     *
     * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
     *   The CSRF token generator.
     */
    public function __construct(CsrfTokenGenerator $csrf_token) {
        $this->csrfToken = $csrf_token;
    }
    
    /**
     * {@inheritdoc}
     */
    public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
        if ($route->hasRequirement('_csrf_token')) {
            $path = ltrim($route->getPath(), '/');
            // Replace the path parameters with values from the parameters array.
            foreach ($parameters as $param => $value) {
                $path = str_replace("{{$param}}", $value, $path);
            }
            // Adding this to the parameters means it will get merged into the query
            // string when the route is compiled.
            if (!$bubbleable_metadata) {
                $parameters['token'] = $this->csrfToken
                    ->get($path);
            }
            else {
                // Generate a placeholder and a render array to replace it.
                $placeholder = Crypt::hashBase64($path);
                $placeholder_render_array = [
                    '#lazy_builder' => [
                        'route_processor_csrf:renderPlaceholderCsrfToken',
                        [
                            $path,
                        ],
                    ],
                ];
                // Instead of setting an actual CSRF token as the query string, we set
                // the placeholder, which will be replaced at the very last moment. This
                // ensures links with CSRF tokens don't break cacheability.
                $parameters['token'] = $placeholder;
                $bubbleable_metadata->addAttachments([
                    'placeholders' => [
                        $placeholder => $placeholder_render_array,
                    ],
                ]);
            }
        }
    }
    
    /**
     * #lazy_builder callback; gets a CSRF token for the given path.
     *
     * @param string $path
     *   The path to get a CSRF token for.
     *
     * @return array
     *   A renderable array representing the CSRF token.
     */
    public function renderPlaceholderCsrfToken($path) {
        return [
            '#markup' => $this->csrfToken
                ->get($path),
            // Tokens are per session.
'#cache' => [
                'contexts' => [
                    'session',
                ],
            ],
        ];
    }
    
    /**
     * {@inheritdoc}
     */
    public static function trustedCallbacks() {
        return [
            'renderPlaceholderCsrfToken',
        ];
    }

}

Classes

Title Deprecated Summary
RouteProcessorCsrf Processes the outbound route to handle the CSRF token.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.