Update to Drupal 8.2.0. For more information, see https://www.drupal.org/project/drupal/releases/8.2.0

This commit is contained in:
Pantheon Automation 2016-10-06 15:16:20 -07:00 committed by Greg Anderson
parent 2f563ab520
commit f1c8716f57
1732 changed files with 52334 additions and 11780 deletions

View file

@ -51,11 +51,16 @@ abstract class AccessResult implements AccessResultInterface, RefinableCacheable
/**
* Creates an AccessResultInterface object with isForbidden() === TRUE.
*
* @param string|null $reason
* (optional) The reason why access is forbidden. Intended for developers,
* hence not translatable.
*
* @return \Drupal\Core\Access\AccessResult
* isForbidden() will be TRUE.
*/
public static function forbidden() {
return new AccessResultForbidden();
public static function forbidden($reason = NULL) {
assert('is_string($reason) || is_null($reason)');
return new AccessResultForbidden($reason);
}
/**
@ -334,8 +339,16 @@ abstract class AccessResult implements AccessResultInterface, RefinableCacheable
if ($this->isForbidden() || $other->isForbidden()) {
$result = static::forbidden();
if (!$this->isForbidden()) {
if ($other instanceof AccessResultReasonInterface) {
$result->setReason($other->getReason());
}
$merge_other = TRUE;
}
else {
if ($this instanceof AccessResultReasonInterface) {
$result->setReason($this->getReason());
}
}
}
elseif ($this->isAllowed() && $other->isAllowed()) {
$result = static::allowed();

View file

@ -5,7 +5,25 @@ namespace Drupal\Core\Access;
/**
* Value object indicating a forbidden access result, with cacheability metadata.
*/
class AccessResultForbidden extends AccessResult {
class AccessResultForbidden extends AccessResult implements AccessResultReasonInterface {
/**
* The reason why access is forbidden. For use in error messages.
*
* @var string|null
*/
protected $reason;
/**
* Constructs a new AccessResultForbidden instance.
*
* @param null|string $reason
* (optional) a message to provide details about this access result
*/
public function __construct($reason = NULL) {
$this->reason = $reason;
}
/**
* {@inheritdoc}
@ -14,4 +32,19 @@ class AccessResultForbidden extends AccessResult {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getReason() {
return $this->reason;
}
/**
* {@inheritdoc}
*/
public function setReason($reason) {
$this->reason = $reason;
return $this;
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Core\Access;
/**
* Interface for access result value objects with stored reason for developers.
*
* For example, a developer can specify the reason for forbidden access:
* @code
* new AccessResultForbidden('You are not authorized to hack core');
* @endcode
*
* @see \Drupal\Core\Access\AccessResultInterface
*/
interface AccessResultReasonInterface extends AccessResultInterface {
/**
* Gets the reason for this access result.
*
* @return string|null
* The reason of this access result or NULL if no reason is provided.
*/
public function getReason();
/**
* Sets the reason for this access result.
*
* @param $reason string|null
* The reason of this access result or NULL if no reason is provided.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result instance.
*/
public function setReason($reason);
}

View file

@ -3,14 +3,17 @@
namespace Drupal\Core\Access;
use Drupal\Core\Routing\Access\AccessInterface;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Loads access checkers from the container.
*/
class CheckProvider extends ContainerAware implements CheckProviderInterface {
class CheckProvider implements CheckProviderInterface, ContainerAwareInterface {
use ContainerAwareTrait;
/**
* Array of registered access check service ids.

View file

@ -0,0 +1,112 @@
<?php
namespace Drupal\Core\Access;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\SessionConfigurationInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* Access protection against CSRF attacks.
*/
class CsrfRequestHeaderAccessCheck implements AccessCheckInterface {
/**
* A string key that will used to designate the token used by this class.
*/
const TOKEN_KEY = 'X-CSRF-Token request header';
/**
* The session configuration.
*
* @var \Drupal\Core\Session\SessionConfigurationInterface
*/
protected $sessionConfiguration;
/**
* The token generator.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $csrfToken;
/**
* Constructs a new rest CSRF access check.
*
* @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
* The session configuration.
* @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
* The token generator.
*/
public function __construct(SessionConfigurationInterface $session_configuration, CsrfTokenGenerator $csrf_token) {
$this->sessionConfiguration = $session_configuration;
$this->csrfToken = $csrf_token;
}
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
$requirements = $route->getRequirements();
// Check for current requirement _csrf_request_header_token and deprecated
// REST requirement.
$applicable_requirements = [
'_csrf_request_header_token',
// @todo Remove _access_rest_csrf in Drupal 9.0.0.
'_access_rest_csrf',
];
$requirement_keys = array_keys($requirements);
if (array_intersect($applicable_requirements, $requirement_keys)) {
if (isset($requirements['_method'])) {
// There could be more than one method requirement separated with '|'.
$methods = explode('|', $requirements['_method']);
// CSRF protection only applies to write operations, so we can filter
// out any routes that require reading methods only.
$write_methods = array_diff($methods, array('GET', 'HEAD', 'OPTIONS', 'TRACE'));
if (empty($write_methods)) {
return FALSE;
}
}
// No method requirement given, so we run this access check to be on the
// safe side.
return TRUE;
}
}
/**
* Checks access.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(Request $request, AccountInterface $account) {
$method = $request->getMethod();
// This check only applies if
// 1. this is a write operation
// 2. the user was successfully authenticated and
// 3. the request comes with a session cookie.
if (!in_array($method, array('GET', 'HEAD', 'OPTIONS', 'TRACE'))
&& $account->isAuthenticated()
&& $this->sessionConfiguration->hasSession($request)
) {
$csrf_token = $request->headers->get('X-CSRF-Token');
// @todo Remove validate call using 'rest' in 8.3.
// Kept here for sessions active during update.
if (!$this->csrfToken->validate($csrf_token, self::TOKEN_KEY)
&& !$this->csrfToken->validate($csrf_token, 'rest')) {
return AccessResult::forbidden()->setReason('X-CSRF-Token request header is missing')->setCacheMaxAge(0);
}
}
// Let other access checkers decide if the request is legit.
return AccessResult::allowed()->setCacheMaxAge(0);
}
}