annotate core/lib/Drupal/Core/Access/CsrfRequestHeaderAccessCheck.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Access;
Chris@0 4
Chris@0 5 use Drupal\Core\Session\AccountInterface;
Chris@0 6 use Drupal\Core\Session\SessionConfigurationInterface;
Chris@0 7 use Symfony\Component\Routing\Route;
Chris@0 8 use Symfony\Component\HttpFoundation\Request;
Chris@0 9
Chris@0 10 /**
Chris@0 11 * Access protection against CSRF attacks.
Chris@0 12 */
Chris@0 13 class CsrfRequestHeaderAccessCheck implements AccessCheckInterface {
Chris@0 14
Chris@0 15 /**
Chris@0 16 * A string key that will used to designate the token used by this class.
Chris@0 17 */
Chris@0 18 const TOKEN_KEY = 'X-CSRF-Token request header';
Chris@0 19
Chris@0 20 /**
Chris@0 21 * The session configuration.
Chris@0 22 *
Chris@0 23 * @var \Drupal\Core\Session\SessionConfigurationInterface
Chris@0 24 */
Chris@0 25 protected $sessionConfiguration;
Chris@0 26
Chris@0 27 /**
Chris@0 28 * The token generator.
Chris@0 29 *
Chris@0 30 * @var \Drupal\Core\Access\CsrfTokenGenerator
Chris@0 31 */
Chris@0 32 protected $csrfToken;
Chris@0 33
Chris@0 34 /**
Chris@0 35 * Constructs a new rest CSRF access check.
Chris@0 36 *
Chris@0 37 * @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
Chris@0 38 * The session configuration.
Chris@0 39 * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
Chris@0 40 * The token generator.
Chris@0 41 */
Chris@0 42 public function __construct(SessionConfigurationInterface $session_configuration, CsrfTokenGenerator $csrf_token) {
Chris@0 43 $this->sessionConfiguration = $session_configuration;
Chris@0 44 $this->csrfToken = $csrf_token;
Chris@0 45 }
Chris@0 46
Chris@0 47 /**
Chris@0 48 * {@inheritdoc}
Chris@0 49 */
Chris@0 50 public function applies(Route $route) {
Chris@0 51 $requirements = $route->getRequirements();
Chris@0 52 // Check for current requirement _csrf_request_header_token and deprecated
Chris@0 53 // REST requirement.
Chris@0 54 $applicable_requirements = [
Chris@0 55 '_csrf_request_header_token',
Chris@0 56 // @todo Remove _access_rest_csrf in Drupal 9.0.0.
Chris@0 57 '_access_rest_csrf',
Chris@0 58 ];
Chris@0 59 $requirement_keys = array_keys($requirements);
Chris@0 60
Chris@0 61 if (array_intersect($applicable_requirements, $requirement_keys)) {
Chris@0 62 if (isset($requirements['_method'])) {
Chris@0 63 // There could be more than one method requirement separated with '|'.
Chris@0 64 $methods = explode('|', $requirements['_method']);
Chris@0 65 // CSRF protection only applies to write operations, so we can filter
Chris@0 66 // out any routes that require reading methods only.
Chris@0 67 $write_methods = array_diff($methods, ['GET', 'HEAD', 'OPTIONS', 'TRACE']);
Chris@0 68 if (empty($write_methods)) {
Chris@0 69 return FALSE;
Chris@0 70 }
Chris@0 71 }
Chris@0 72 // No method requirement given, so we run this access check to be on the
Chris@0 73 // safe side.
Chris@0 74 return TRUE;
Chris@0 75 }
Chris@0 76 }
Chris@0 77
Chris@0 78 /**
Chris@0 79 * Checks access.
Chris@0 80 *
Chris@0 81 * @param \Symfony\Component\HttpFoundation\Request $request
Chris@0 82 * The request object.
Chris@0 83 * @param \Drupal\Core\Session\AccountInterface $account
Chris@0 84 * The currently logged in account.
Chris@0 85 *
Chris@0 86 * @return \Drupal\Core\Access\AccessResultInterface
Chris@0 87 * The access result.
Chris@0 88 */
Chris@0 89 public function access(Request $request, AccountInterface $account) {
Chris@0 90 $method = $request->getMethod();
Chris@0 91
Chris@17 92 // Read-only operations are always allowed.
Chris@17 93 if (in_array($method, ['GET', 'HEAD', 'OPTIONS', 'TRACE'], TRUE)) {
Chris@17 94 return AccessResult::allowed();
Chris@17 95 }
Chris@17 96
Chris@0 97 // This check only applies if
Chris@17 98 // 1. the user was successfully authenticated and
Chris@17 99 // 2. the request comes with a session cookie.
Chris@17 100 if ($account->isAuthenticated()
Chris@0 101 && $this->sessionConfiguration->hasSession($request)
Chris@0 102 ) {
Chris@0 103 if (!$request->headers->has('X-CSRF-Token')) {
Chris@0 104 return AccessResult::forbidden()->setReason('X-CSRF-Token request header is missing')->setCacheMaxAge(0);
Chris@0 105 }
Chris@0 106 $csrf_token = $request->headers->get('X-CSRF-Token');
Chris@0 107 // @todo Remove validate call using 'rest' in 8.3.
Chris@0 108 // Kept here for sessions active during update.
Chris@0 109 if (!$this->csrfToken->validate($csrf_token, self::TOKEN_KEY)
Chris@0 110 && !$this->csrfToken->validate($csrf_token, 'rest')) {
Chris@0 111 return AccessResult::forbidden()->setReason('X-CSRF-Token request header is invalid')->setCacheMaxAge(0);
Chris@0 112 }
Chris@0 113 }
Chris@0 114 // Let other access checkers decide if the request is legit.
Chris@0 115 return AccessResult::allowed()->setCacheMaxAge(0);
Chris@0 116 }
Chris@0 117
Chris@0 118 }