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:
parent
2f563ab520
commit
f1c8716f57
1732 changed files with 52334 additions and 11780 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
36
core/lib/Drupal/Core/Access/AccessResultReasonInterface.php
Normal file
36
core/lib/Drupal/Core/Access/AccessResultReasonInterface.php
Normal 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);
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
112
core/lib/Drupal/Core/Access/CsrfRequestHeaderAccessCheck.php
Normal file
112
core/lib/Drupal/Core/Access/CsrfRequestHeaderAccessCheck.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue