only in patch2: unchanged: --- a/core/core.services.yml +++ b/core/core.services.yml @@ -499,6 +499,8 @@ services: csrf_token: class: Drupal\Core\Access\CsrfTokenGenerator arguments: ['@private_key'] + calls: + - [setCurrentUser, ['@?current_user']] access_manager: class: Drupal\Core\Access\AccessManager arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager'] only in patch2: unchanged: --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -2893,17 +2893,20 @@ function drupal_get_token($value = '') { * The token to be validated. * @param string $value * An additional value to base the token on. + * @param bool $skip_anonymous + * Set to true to skip token validation for anonymous users. * * @return bool - * True for a valid token, false for an invalid token. + * True for a valid token, false for an invalid token. When $skip_anonymous + * is true, the return value will always be true for anonymous users. * * @see \Drupal\Core\Access\CsrfTokenGenerator * * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. * Use return \Drupal::csrfToken()->validate(). */ -function drupal_valid_token($token, $value = '') { - return \Drupal::csrfToken()->validate($token, $value); +function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) { + return \Drupal::csrfToken()->validate($token, $value, $skip_anonymous); } /** only in patch2: unchanged: --- a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php +++ b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php @@ -26,6 +26,13 @@ class CsrfTokenGenerator { protected $privateKey; /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** * Constructs the token generator. * * @param \Drupal\Core\PrivateKey $private_key @@ -36,6 +43,16 @@ public function __construct(PrivateKey $private_key) { } /** + * Sets the current user. + * + * @param \Drupal\Core\Session\AccountInterface|null $current_user + * The current user service. + */ + public function setCurrentUser(AccountInterface $current_user = NULL) { + $this->currentUser = $current_user; + } + + /** * Generates a token based on $value, the user session, and the private key. * * The generated token is based on the session ID of the current user. Normally, @@ -65,12 +82,15 @@ public function get($value = '') { * The token to be validated. * @param string $value * (optional) An additional value to base the token on. + * @param bool $skip_anonymous + * (optional) Set to TRUE to skip token validation for anonymous users. * * @return bool - * TRUE for a valid token, FALSE for an invalid token. + * TRUE for a valid token, FALSE for an invalid token. When $skip_anonymous + * is TRUE, the return value will always be TRUE for anonymous users. */ - public function validate($token, $value = '') { - return $token === $this->get($value); + public function validate($token, $value = '', $skip_anonymous = FALSE) { + return ($skip_anonymous && $this->currentUser->isAnonymous()) || ($token === $this->get($value)); } } only in patch2: unchanged: --- a/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php @@ -71,6 +71,23 @@ public function testValidate() { $token = $this->generator->get('bar'); $this->assertTrue($this->generator->validate($token, 'bar')); + + // Check the skip_anonymous option with both a anonymous user and a real + // user. + $account = $this->getMock('Drupal\Core\Session\AccountInterface'); + $account->expects($this->once()) + ->method('isAnonymous') + ->will($this->returnValue(TRUE)); + $this->generator->setCurrentUser($account); + $this->assertTrue($this->generator->validate($token, 'foo', TRUE)); + + $account = $this->getMock('Drupal\Core\Session\AccountInterface'); + $account->expects($this->once()) + ->method('isAnonymous') + ->will($this->returnValue(FALSE)); + $this->generator->setCurrentUser($account); + + $this->assertFalse($this->generator->validate($token, 'foo', TRUE)); } /**