diff --git a/core/core.services.yml b/core/core.services.yml index c43da8c..0063b51 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -664,7 +664,7 @@ services: class: Drupal\Core\Access\AccessArgumentsResolver access_manager: class: Drupal\Core\Access\AccessManager - arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager', '@access_arguments_resolver', '@request_stack'] + arguments: ['@router.route_provider', '@paramconverter_manager', '@access_arguments_resolver'] calls: - [setContainer, ['@service_container']] access_route_subscriber: diff --git a/core/lib/Drupal/Core/Access/AccessArgumentsResolver.php b/core/lib/Drupal/Core/Access/AccessArgumentsResolver.php index b2c5285..b919a15 100644 --- a/core/lib/Drupal/Core/Access/AccessArgumentsResolver.php +++ b/core/lib/Drupal/Core/Access/AccessArgumentsResolver.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Access; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; +use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -19,10 +19,10 @@ class AccessArgumentsResolver implements AccessArgumentsResolverInterface { /** * {@inheritdoc} */ - public function getArguments(callable $callable, Route $route, Request $request, AccountInterface $account) { + public function getArguments(callable $callable, RouteMatchInterface $route_match, AccountInterface $account) { $arguments = array(); foreach ($this->getReflector($callable)->getParameters() as $parameter) { - $arguments[] = $this->getArgument($parameter, $route, $request, $account); + $arguments[] = $this->getArgument($parameter, $route_match, $account); } return $arguments; } @@ -32,10 +32,8 @@ public function getArguments(callable $callable, Route $route, Request $request, * * @param \ReflectionParameter $parameter * The parameter of a callable to get the value for. - * @param \Symfony\Component\Routing\Route $route - * The access checked route. - * @param \Symfony\Component\HttpFoundation\Request $request - * The current request. + * @param \Drupal\Core\Routing\RouteMatchInterface + * The parametrized route to being checked. * @param \Drupal\Core\Session\AccountInterface $account * The current user. * @@ -45,9 +43,9 @@ public function getArguments(callable $callable, Route $route, Request $request, * @throws \RuntimeException * Thrown when there is a missing parameter. */ - protected function getArgument(\ReflectionParameter $parameter, Route $route, Request $request, AccountInterface $account) { - $upcasted_route_arguments = $request->attributes->all(); - $raw_route_arguments = isset($upcasted_route_arguments['_raw_variables']) ? $upcasted_route_arguments['_raw_variables']->all() : $upcasted_route_arguments; + protected function getArgument(\ReflectionParameter $parameter, RouteMatchInterface $route_match, AccountInterface $account) { + $upcasted_route_arguments = $route_match->getParameters()->all(); + $raw_route_arguments = $route_match->getRawParameters()->all(); $parameter_type_hint = $parameter->getClass(); $parameter_name = $parameter->getName(); @@ -67,10 +65,10 @@ protected function getArgument(\ReflectionParameter $parameter, Route $route, Re if (isset($upcasted_route_arguments[$parameter_name]) && is_object($upcasted_route_arguments[$parameter_name]) && $parameter_type_hint->isInstance($upcasted_route_arguments[$parameter_name])) { return $upcasted_route_arguments[$parameter_name]; } - // Otherwise, resolve $request, $route, and $account by type matching - // only. This way, the callable may rename them in case the route - // defines other parameters with these names. - foreach (array($request, $route, $account) as $special_argument) { + // Otherwise, resolve $route, $route_match, and $account by type matching + // only. This way, the callable may rename them in case the route defines + // other parameters with these names. + foreach (array($route_match->getRouteObject(), $route_match, $account) as $special_argument) { if ($parameter_type_hint->isInstance($special_argument)) { return $special_argument; } diff --git a/core/lib/Drupal/Core/Access/AccessArgumentsResolverInterface.php b/core/lib/Drupal/Core/Access/AccessArgumentsResolverInterface.php index ce0ecf8..aee25ff 100644 --- a/core/lib/Drupal/Core/Access/AccessArgumentsResolverInterface.php +++ b/core/lib/Drupal/Core/Access/AccessArgumentsResolverInterface.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Access; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; +use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -21,10 +21,8 @@ * * @param callable $callable * A PHP callable. - * @param \Symfony\Component\Routing\Route $route - * The route to check access to. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * @@ -34,6 +32,6 @@ * @throws \RuntimeException * When a value for an argument given is not provided. */ - public function getArguments(callable $callable, Route $route, Request $request, AccountInterface $account); + public function getArguments(callable $callable, RouteMatchInterface $route_match, AccountInterface $account); } diff --git a/core/lib/Drupal/Core/Access/AccessManager.php b/core/lib/Drupal/Core/Access/AccessManager.php index 54d13de..b969c55 100644 --- a/core/lib/Drupal/Core/Access/AccessManager.php +++ b/core/lib/Drupal/Core/Access/AccessManager.php @@ -9,17 +9,15 @@ use Drupal\Core\ParamConverter\ParamConverterManagerInterface; use Drupal\Core\ParamConverter\ParamNotConvertedException; -use Drupal\Core\Routing\RequestHelper; +use Drupal\Core\Routing\RouteMatch; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\ParameterBag; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Cmf\Component\Routing\RouteObjectInterface; @@ -75,13 +73,6 @@ class AccessManager implements ContainerAwareInterface, AccessManagerInterface { protected $routeProvider; /** - * The url generator. - * - * @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface - */ - protected $urlGenerator; - - /** * The paramconverter manager. * * @var \Drupal\Core\ParamConverter\ParamConverterManagerInterface @@ -96,32 +87,19 @@ class AccessManager implements ContainerAwareInterface, AccessManagerInterface { protected $argumentsResolver; /** - * A request stack object. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - - /** * Constructs a AccessManager instance. * * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider * The route provider. - * @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $url_generator - * The url generator. * @param \Drupal\Core\ParamConverter\ParamConverterManagerInterface $paramconverter_manager * The param converter manager. * @param \Drupal\Core\Access\AccessArgumentsResolverInterface $arguments_resolver * The access arguments resolver. - * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack - * The request stack object. */ - public function __construct(RouteProviderInterface $route_provider, UrlGeneratorInterface $url_generator, ParamConverterManagerInterface $paramconverter_manager, AccessArgumentsResolverInterface $arguments_resolver, RequestStack $requestStack) { + public function __construct(RouteProviderInterface $route_provider, ParamConverterManagerInterface $paramconverter_manager, AccessArgumentsResolverInterface $arguments_resolver) { $this->routeProvider = $route_provider; - $this->urlGenerator = $url_generator; $this->paramConverterManager = $paramconverter_manager; $this->argumentsResolver = $arguments_resolver; - $this->requestStack = $requestStack; } /** @@ -182,22 +160,19 @@ protected function applies(Route $route) { /** * {@inheritdoc} */ - public function checkNamedRoute($route_name, array $parameters = array(), AccountInterface $account, Request $route_request = NULL) { + public function checkNamedRoute($route_name, array $parameters, AccountInterface $account) { try { $route = $this->routeProvider->getRouteByName($route_name, $parameters); - if (empty($route_request)) { - // Create a cloned request with fresh attributes. - $route_request = RequestHelper::duplicate($this->requestStack->getCurrentRequest(), $this->urlGenerator->generate($route_name, $parameters)); - $route_request->attributes->replace(array()); - - // Populate $route_request->attributes with both raw and converted - // parameters. - $parameters += $route->getDefaults(); - $route_request->attributes->set('_raw_variables', new ParameterBag($parameters)); - $parameters[RouteObjectInterface::ROUTE_OBJECT] = $route; - $route_request->attributes->add($this->paramConverterManager->convert($parameters, $route_request)); - } - return $this->check($route, $route_request, $account); + + // Populate a new route match with both raw and converted parameters. + $parameters += $route->getDefaults(); + + // @todo: do we really need this? and why not ROUTE_NAME? + $parameters[RouteObjectInterface::ROUTE_OBJECT] = $route; + + $upcasted_parameters = $this->paramConverterManager->convert($parameters, new Request()); + $route_match = new RouteMatch($route_name, $route, $upcasted_parameters, $parameters); + return $this->check($route_match, $account); } catch (RouteNotFoundException $e) { return FALSE; @@ -210,15 +185,27 @@ public function checkNamedRoute($route_name, array $parameters = array(), Accoun /** * {@inheritdoc} */ - public function check(Route $route, Request $request, AccountInterface $account) { + public function checkRequest(Request $request, AccountInterface $account) { + // @todo: Temporary workaround: Ensure that the request is available as an + // upcasted route argument in the AccessArgumentsResolver. + $route_match = RouteMatch::createFromRequest($request); + $route_match->getParameters()->set('request', $request); + return $this->check($route_match, $account, $request); + } + + /** + * {@inheritdoc} + */ + public function check(RouteMatchInterface $route_match, AccountInterface $account, Request $request = NULL) { + $route = $route_match->getRouteObject(); $checks = $route->getOption('_access_checks') ?: array(); $conjunction = $route->getOption('_access_mode') ?: static::ACCESS_MODE_ALL; if ($conjunction == static::ACCESS_MODE_ALL) { - return $this->checkAll($checks, $route, $request, $account); + return $this->checkAll($checks, $route_match, $account); } else { - return $this->checkAny($checks, $route, $request, $account); + return $this->checkAny($checks, $route_match, $account); } } @@ -227,17 +214,15 @@ public function check(Route $route, Request $request, AccountInterface $account) * * @param array $checks * Contains the list of checks on the route definition. - * @param \Symfony\Component\Routing\Route $route - * The route to check access to. - * @param \Symfony\Component\HttpFoundation\Request $request - * The incoming request object. + * @param \Symfony\Component\Routing\RouteMatchInterface $route_match + * The parametrized route to check access to. * @param \Drupal\Core\Session\AccountInterface $account * The current user. * * @return bool * Returns TRUE if the user has access to the route, else FALSE. */ - protected function checkAll(array $checks, Route $route, Request $request, AccountInterface $account) { + protected function checkAll(array $checks, RouteMatchInterface $route_match, AccountInterface $account) { $access = FALSE; foreach ($checks as $service_id) { @@ -245,7 +230,7 @@ protected function checkAll(array $checks, Route $route, Request $request, Accou $this->loadCheck($service_id); } - $service_access = $this->performCheck($service_id, $route, $request, $account); + $service_access = $this->performCheck($service_id, $route_match, $account); if ($service_access === AccessInterface::ALLOW) { $access = TRUE; @@ -265,17 +250,15 @@ protected function checkAll(array $checks, Route $route, Request $request, Accou * * @param array $checks * Contains the list of checks on the route definition. - * @param \Symfony\Component\Routing\Route $route - * The route to check access to. - * @param \Symfony\Component\HttpFoundation\Request $request - * The incoming request object. + * @param \Symfony\Component\Routing\RouteMatchInterface $route_match + * The parametrized route to check access to. * @param \Drupal\Core\Session\AccountInterface $account * The current user. * * @return bool * Returns TRUE if the user has access to the route, else FALSE. */ - protected function checkAny(array $checks, $route, $request, AccountInterface $account) { + protected function checkAny(array $checks, RouteMatchInterface $route_match, AccountInterface $account) { // No checks == deny by default. $access = FALSE; @@ -284,7 +267,7 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a $this->loadCheck($service_id); } - $service_access = $this->performCheck($service_id, $route, $request, $account); + $service_access = $this->performCheck($service_id, $route_match, $account); if ($service_access === AccessInterface::ALLOW) { $access = TRUE; @@ -302,10 +285,8 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a * * @param string $service_id * The access check service ID to use. - * @param \Symfony\Component\Routing\Route $route + * @param \Symfony\Component\Routing\RouteMatchInterface $route_match * The route to check access to. - * @param \Symfony\Component\HttpFoundation\Request $request - * The incoming request object. * @param \Drupal\Core\Session\AccountInterface $account * The current user. * @@ -315,9 +296,9 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - protected function performCheck($service_id, $route, $request, $account) { + protected function performCheck($service_id, RouteMatchInterface $route_match, AccountInterface $account) { $callable = array($this->checks[$service_id], $this->checkMethods[$service_id]); - $arguments = $this->argumentsResolver->getArguments($callable, $route, $request, $account); + $arguments = $this->argumentsResolver->getArguments($callable, $route_match, $account); $service_access = call_user_func_array($callable, $arguments); if (!in_array($service_access, array(AccessInterface::ALLOW, AccessInterface::DENY, AccessInterface::KILL), TRUE)) { diff --git a/core/lib/Drupal/Core/Access/AccessManagerInterface.php b/core/lib/Drupal/Core/Access/AccessManagerInterface.php index df88ba6..8ac32d9 100644 --- a/core/lib/Drupal/Core/Access/AccessManagerInterface.php +++ b/core/lib/Drupal/Core/Access/AccessManagerInterface.php @@ -7,10 +7,11 @@ namespace Drupal\Core\Access; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; -use Symfony\Component\HttpFoundation\Request; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Routing\RouteMatchInterface; /** * Provides an interface for attaching and running access check services. @@ -46,14 +47,21 @@ * Optional array of values to substitute into the route path patern. * @param \Drupal\Core\Session\AccountInterface $account * The current user. - * @param \Symfony\Component\HttpFoundation\Request $route_request - * Optional incoming request object. If not provided, one will be built - * using the route information and the current request from the container. * * @return bool * Returns TRUE if the user has access to the route, otherwise FALSE. */ - public function checkNamedRoute($route_name, array $parameters = array(), AccountInterface $account, Request $route_request = NULL); + public function checkNamedRoute($route_name, array $parameters = array(), AccountInterface $account); + + /** + * Execute access checks against the incomming request. + * + * @param Request $request + * The incomming request. + * @param \Drupal\Core\Session\AccountInterface $account + * The current user. + */ + public function checkRequest(Request $request, AccountInterface $account); /** * For each route, saves a list of applicable access checks to the route. @@ -81,16 +89,17 @@ public function addCheckService($service_id, $service_method, array $applies_che * * Determines whether the route is accessible or not. * - * @param \Symfony\Component\Routing\Route $route - * The route to check access to. - * @param \Symfony\Component\HttpFoundation\Request $request - * The incoming request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. * @param \Drupal\Core\Session\AccountInterface $account * The current account. + * @param \Symfony\Component\HttpFoundation\Request $request + * Optional, a request. Only supply this parameter when checking the + * incomming request, do not specify when checking routes on output. * * @return bool * Returns TRUE if the user has access to the route, otherwise FALSE. */ - public function check(Route $route, Request $request, AccountInterface $account); + public function check(RouteMatchInterface $route_match, AccountInterface $account, Request $request = NULL); } diff --git a/core/lib/Drupal/Core/Access/CsrfAccessCheck.php b/core/lib/Drupal/Core/Access/CsrfAccessCheck.php index f6b60d3..f721c68 100644 --- a/core/lib/Drupal/Core/Access/CsrfAccessCheck.php +++ b/core/lib/Drupal/Core/Access/CsrfAccessCheck.php @@ -48,9 +48,9 @@ function __construct(CsrfTokenGenerator $csrf_token) { * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request) { + public function access(Route $route, Request $request = NULL) { // If this is the controller request, check CSRF access as normal. - if ($request->attributes->get('_controller_request')) { + if ($request) { // @todo Remove dependency on the internal _system_path attribute: // https://www.drupal.org/node/2293501. return $this->csrfToken->validate($request->query->get('token'), $request->attributes->get('_system_path')) ? static::ALLOW : static::KILL; diff --git a/core/lib/Drupal/Core/Access/CustomAccessCheck.php b/core/lib/Drupal/Core/Access/CustomAccessCheck.php index c128a5b..08260d4 100644 --- a/core/lib/Drupal/Core/Access/CustomAccessCheck.php +++ b/core/lib/Drupal/Core/Access/CustomAccessCheck.php @@ -9,8 +9,8 @@ use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Routing\Access\AccessInterface as RoutingAccessInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -65,9 +65,9 @@ public function __construct(ControllerResolverInterface $controller_resolver, Ac * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { $callable = $this->controllerResolver->getControllerFromDefinition($route->getRequirement('_custom_access')); - $arguments = $this->argumentsResolver->getArguments($callable, $route, $request, $account); + $arguments = $this->argumentsResolver->getArguments($callable, $route_match, $account); return call_user_func_array($callable, $arguments); } diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 5377c36..95bc618 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -778,8 +778,8 @@ public static function bootEnvironment() { } // Set the Drupal custom error handler. - set_error_handler('_drupal_error_handler'); - set_exception_handler('_drupal_exception_handler'); + #set_error_handler('_drupal_error_handler'); + #set_exception_handler('_drupal_exception_handler'); static::$isEnvironmentInitialized = TRUE; } diff --git a/core/lib/Drupal/Core/Entity/EntityAccessCheck.php b/core/lib/Drupal/Core/Entity/EntityAccessCheck.php index 951abb0..7e8f9a2 100644 --- a/core/lib/Drupal/Core/Entity/EntityAccessCheck.php +++ b/core/lib/Drupal/Core/Entity/EntityAccessCheck.php @@ -8,9 +8,9 @@ namespace Drupal\Core\Entity; use Drupal\Core\Routing\Access\AccessInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; /** * Provides a generic access checker for entities. @@ -32,21 +32,22 @@ class EntityAccessCheck implements AccessInterface { * * @param \Symfony\Component\Routing\Route $route * The route to check against. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { // Split the entity type and the operation. $requirement = $route->getRequirement('_entity_access'); list($entity_type, $operation) = explode('.', $requirement); // If there is valid entity of the given entity type, check its access. - if ($request->attributes->has($entity_type)) { - $entity = $request->attributes->get($entity_type); + $parameters = $route_match->getParameters(); + if ($parameters->has($entity_type)) { + $entity = $parameters->get($entity_type); if ($entity instanceof EntityInterface) { return $entity->access($operation, $account) ? static::ALLOW : static::DENY; } diff --git a/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php b/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php index e3fab39..fc68a97 100644 --- a/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php +++ b/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php @@ -8,8 +8,8 @@ namespace Drupal\Core\Entity; use Drupal\Core\Routing\Access\AccessInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -46,22 +46,22 @@ public function __construct(EntityManagerInterface $entity_manager) { * * @param \Symfony\Component\Routing\Route $route * The route to check against. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { list($entity_type, $bundle) = explode(':', $route->getRequirement($this->requirementsKey) . ':'); // The bundle argument can contain request argument placeholders like // {name}, loop over the raw variables and attempt to replace them in the // bundle name. If a placeholder does not exist, it won't get replaced. if ($bundle && strpos($bundle, '{') !== FALSE) { - foreach ($request->get('_raw_variables')->all() as $name => $value) { + foreach ($route_match->getRawParameters()->all() as $name => $value) { $bundle = str_replace('{' . $name . '}', $value, $bundle); } // If we were unable to replace all placeholders, deny access. diff --git a/core/lib/Drupal/Core/Routing/AccessAwareRouter.php b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php index b8409df..307cfc9 100644 --- a/core/lib/Drupal/Core/Routing/AccessAwareRouter.php +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php @@ -102,21 +102,8 @@ public function matchRequest(Request $request) { * The request to access check. */ protected function checkAccess(Request $request) { - // The controller is being handled by the HTTP kernel, so add an attribute - // to tell us this is the controller request. - $request->attributes->set('_controller_request', TRUE); - $e = FALSE; - try { - if (!$this->accessManager->check($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT), $request, $this->account)) { - $e = new AccessDeniedHttpException(); - } - } - catch (\Exception $e) { - } - // @todo Once PHP 5.5 is a requirement refactor this using finally. - $request->attributes->remove('_controller_request'); - if ($e) { - throw $e; + if (!$this->accessManager->checkRequest($request, $this->account)) { + throw new AccessDeniedHttpException(); } } diff --git a/core/lib/Drupal/Core/Routing/RequestHelper.php b/core/lib/Drupal/Core/Routing/RequestHelper.php index a56ed83..55d85aa 100644 --- a/core/lib/Drupal/Core/Routing/RequestHelper.php +++ b/core/lib/Drupal/Core/Routing/RequestHelper.php @@ -15,120 +15,6 @@ class RequestHelper { /** - * Duplicates a request for another path. - * - * This method does basically the same as Request::create() but keeping all - * the previous variables to speed it up. - * - * @param \Symfony\Component\HttpFoundation\Request $original_request - * The original request object to clone. - * @param string $uri - * The URI. - * @param string $method - * The HTTP method. - * @param array $parameters - * The query (GET) or request (POST) parameters. - * @param array $query - * The GET parameters. - * @param array $post - * The POST parameters. - * @param array $attributes - * The request attributes (parameters parsed from the PATH_INFO, ...). - * @param array $cookies - * The COOKIE parameters. - * @param array $files - * The FILES parameters. - * @param array $server - * The SERVER parameters. - * - * @return \Symfony\Component\HttpFoundation\Request - * The cloned request instance. - * - * @see \Symfony\Component\HttpFoundation\Request::create() - * @see \Symfony\Component\HttpFoundation\Request::duplicate() - */ - public static function duplicate(Request $original_request, $uri, $method = 'GET', $parameters = array(), array $query = NULL, array $post = NULL, array $attributes = NULL, array $cookies = NULL, array $files = NULL, array $server = NULL) { - $request = $original_request->duplicate($query, $post, $attributes, $cookies, $files, $server); - - $server = array(); - - $server['PATH_INFO'] = ''; - $server['REQUEST_METHOD'] = strtoupper($method); - - $components = parse_url($uri); - if (isset($components['host'])) { - $server['SERVER_NAME'] = $components['host']; - $server['HTTP_HOST'] = $components['host']; - } - - if (isset($components['scheme'])) { - if ('https' === $components['scheme']) { - $server['HTTPS'] = 'on'; - $server['SERVER_PORT'] = 443; - } - else { - unset($server['HTTPS']); - $server['SERVER_PORT'] = 80; - } - } - - if (isset($components['port'])) { - $server['SERVER_PORT'] = $components['port']; - $server['HTTP_HOST'] = $server['HTTP_HOST'] . ':' . $components['port']; - } - - if (isset($components['user'])) { - $server['PHP_AUTH_USER'] = $components['user']; - } - - if (isset($components['pass'])) { - $server['PHP_AUTH_PW'] = $components['pass']; - } - - if (!isset($components['path'])) { - $components['path'] = '/'; - } - - switch (strtoupper($method)) { - case 'POST': - case 'PUT': - case 'DELETE': - if (!isset($server['CONTENT_TYPE'])) { - $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - } - case 'PATCH': - $post = $parameters; - $query = array(); - break; - default: - $post = array(); - $query = $parameters; - break; - } - - if (isset($components['query'])) { - parse_str(html_entity_decode($components['query']), $query_string); - $query = array_replace($query_string, $query); - } - $query_string = http_build_query($query, '', '&'); - - // Prepend a ? if there is a query string. - if ($query_string !== '') { - $query_string = '?' . $query_string; - } - - $server['REQUEST_URI'] = $components['path'] . $query_string; - $server['QUERY_STRING'] = $query_string; - $request->server->add($server); - // The 'request' attribute name corresponds to $_REQUEST, but Symfony - // documents it as holding the POST parameters. - $request->request->add($post); - $request->query->add($query); - - return $request; - } - - /** * Returns whether the request is using a clean URL. * * A clean URL is one that does not include the script name. For example, diff --git a/core/modules/config_translation/config_translation.services.yml b/core/modules/config_translation/config_translation.services.yml index f4ed510..e07f097 100644 --- a/core/modules/config_translation/config_translation.services.yml +++ b/core/modules/config_translation/config_translation.services.yml @@ -5,15 +5,15 @@ services: tags: - { name: event_subscriber } - config_translation.access.overview: - class: Drupal\config_translation\Access\ConfigTranslationOverviewAccess - arguments: ['@plugin.manager.config_translation.mapper'] + config_translation.access.mapper: + class: Drupal\config_translation\Access\ConfigTranslationMapperAccess + arguments: ['@plugin.manager.config_translation.mapper', '@language_manager'] tags: - - { name: access_check, applies_to: _config_translation_overview_access } + - { name: access_check, applies_to: _config_translation_mapper_access } config_translation.access.form: class: Drupal\config_translation\Access\ConfigTranslationFormAccess - arguments: ['@plugin.manager.config_translation.mapper'] + arguments: ['@plugin.manager.config_translation.mapper', '@language_manager'] tags: - { name: access_check, applies_to: _config_translation_form_access } diff --git a/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php b/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php index 791ef85..6345977 100644 --- a/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php +++ b/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php @@ -7,37 +7,68 @@ namespace Drupal\config_translation\Access; -use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Route; +use Drupal\config_translation\ConfigMapperManagerInterface; +use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Access\AccessInterface; /** * Checks access for displaying the translation add, edit, and delete forms. */ -class ConfigTranslationFormAccess extends ConfigTranslationOverviewAccess { +class ConfigTranslationFormAccess implements AccessInterface { /** - * {@inheritdoc} + * The mapper plugin discovery service. + * + * @var \Drupal\config_translation\ConfigMapperManagerInterface */ - public function access(Route $route, Request $request, AccountInterface $account) { - // For the translation forms we have a target language, so we need some - // checks in addition to the checks performed for the translation overview. - $base_access = parent::access($route, $request, $account); - if ($base_access === static::ALLOW) { - $target_language = language_load($request->attributes->get('langcode')); - - // Make sure that the target language is not locked, and that the target - // language is not the original submission language. Although technically - // configuration can be overlaid with translations in the same language, - // that is logically not a good idea. - $access = - !empty($target_language) && - !$target_language->locked && - $target_language->id != $this->sourceLanguage->id; - - return $access ? static::ALLOW : static::DENY; - } - return static::DENY; + protected $configMapperManager; + + /** + * The language manager + * + * @var \Drupal\Core\Language\LanguageManagerInterface + */ + protected $languageManager; + + /** + * Constructs a ConfigTranslationFormAccess object. + * + * @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager + * The mapper plugin discovery service. + */ + public function __construct(ConfigMapperManagerInterface $config_mapper_manager, LanguageManagerInterface $language_manager) { + $this->configMapperManager = $config_mapper_manager; + $this->languageManager = $language_manager; + } + + /** + * Checks access to the translation form. + * + * @param string $plugin_id + * The config mapper plugin id. + * @param string $langcode + * If checking access to a translation form, the language code of the target + * language. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. + */ + public function access($plugin_id, $langcode) { + /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */ + $mapper = $this->configMapperManager->createInstance($plugin_id); + $source_langcode = $mapper->getLangcode(); + $target_language = $this->languageManager->getLanguage($langcode); + + // Make sure that the target language is not locked, and that the target + // language is not the original submission language. Although technically + // configuration can be overlaid with translations in the same language, + // that is logically not a good idea. + $access = + !empty($target_language) && + !$target_language->locked && + $langcode != $source_langcode; + + return $access ? static::ALLOW : static::DENY; } } diff --git a/core/modules/config_translation/src/Access/ConfigTranslationMapperAccess.php b/core/modules/config_translation/src/Access/ConfigTranslationMapperAccess.php new file mode 100644 index 0000000..cc083fa --- /dev/null +++ b/core/modules/config_translation/src/Access/ConfigTranslationMapperAccess.php @@ -0,0 +1,70 @@ +configMapperManager = $config_mapper_manager; + $this->languageManager = $language_manager; + } + + /** + * Checks access to a config mapper. + * + * @param string $plugin_id + * The config mapper plugin id. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. + */ + public function access($plugin_id) { + /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */ + $mapper = $this->configMapperManager->createInstance($plugin_id); + $source_langcode = $mapper->getLangcode(); + $source_language = $this->languageManager->getLanguage($source_langcode); + $source_language_present_and_locked = $source_language && $source_language->locked; + + // Allow access to the translation overview if the configuration has + // translatable pieces, and the source language is not locked. + $access = + $mapper->hasSchema() && + $mapper->hasTranslatable() && + !$source_language_present_and_locked; + + return $access ? static::ALLOW : static::DENY; + } + +} diff --git a/core/modules/config_translation/src/Access/ConfigTranslationOverviewAccess.php b/core/modules/config_translation/src/Access/ConfigTranslationOverviewAccess.php deleted file mode 100644 index 529bc4a..0000000 --- a/core/modules/config_translation/src/Access/ConfigTranslationOverviewAccess.php +++ /dev/null @@ -1,77 +0,0 @@ -configMapperManager = $config_mapper_manager; - } - - /** - * Checks access to the overview based on permissions and translatability. - * - * @param \Symfony\Component\Routing\Route $route - * The route to check against. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - * @param \Drupal\Core\Session\AccountInterface $account - * The currently logged in account. - * - * @return string - * A \Drupal\Core\Access\AccessInterface constant value. - */ - public function access(Route $route, Request $request, AccountInterface $account) { - /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */ - $mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id')); - $mapper->populateFromRequest($request); - - $this->sourceLanguage = $mapper->getLanguageWithFallback(); - - // Allow access to the translation overview if the proper permission is - // granted, the configuration has translatable pieces, and the source - // language is not locked. - $access = - $account->hasPermission('translate configuration') && - $mapper->hasSchema() && - $mapper->hasTranslatable() && - !$this->sourceLanguage->locked; - - return $access ? static::ALLOW : static::DENY; - } - -} diff --git a/core/modules/config_translation/src/ConfigNamesMapper.php b/core/modules/config_translation/src/ConfigNamesMapper.php index 57811d9..54376e4 100644 --- a/core/modules/config_translation/src/ConfigNamesMapper.php +++ b/core/modules/config_translation/src/ConfigNamesMapper.php @@ -227,7 +227,10 @@ public function getOverviewRoute() { '_content' => '\Drupal\config_translation\Controller\ConfigTranslationController::itemPage', 'plugin_id' => $this->getPluginId(), ), - array('_config_translation_overview_access' => 'TRUE') + array( + '_permission' => 'translate configuration', + '_config_translation_mapper_access' => 'TRUE', + ) ); $this->processRoute($route); return $route; @@ -268,7 +271,11 @@ public function getAddRoute() { '_form' => '\Drupal\config_translation\Form\ConfigTranslationAddForm', 'plugin_id' => $this->getPluginId(), ), - array('_config_translation_form_access' => 'TRUE') + array( + '_permission' => 'translate configuration', + '_config_translation_mapper_access' => 'TRUE', + '_config_translation_form_access' => 'TRUE', + ) ); $this->processRoute($route); return $route; @@ -298,7 +305,11 @@ public function getEditRoute() { '_form' => '\Drupal\config_translation\Form\ConfigTranslationEditForm', 'plugin_id' => $this->getPluginId(), ), - array('_config_translation_form_access' => 'TRUE') + array( + '_permission' => 'translate configuration', + '_config_translation_mapper_access' => 'TRUE', + '_config_translation_form_access' => 'TRUE', + ) ); $this->processRoute($route); return $route; @@ -328,7 +339,11 @@ public function getDeleteRoute() { '_form' => '\Drupal\config_translation\Form\ConfigTranslationDeleteForm', 'plugin_id' => $this->getPluginId(), ), - array('_config_translation_form_access' => 'TRUE') + array( + '_permission' => 'translate configuration', + '_config_translation_mapper_access' => 'TRUE', + '_config_translation_form_access' => 'TRUE', + ) ); $this->processRoute($route); return $route; diff --git a/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php b/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php index 5f12452..b8a22a6 100644 --- a/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php +++ b/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php @@ -11,8 +11,8 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Routing\Access\AccessInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -52,8 +52,8 @@ public function __construct(EntityManagerInterface $manager, LanguageManagerInte * * @param \Symfony\Component\Routing\Route $route * The route to check against. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * @param string $source @@ -69,9 +69,9 @@ public function __construct(EntityManagerInterface $manager, LanguageManagerInte * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account, $source = NULL, $target = NULL, $language = NULL, $entity_type_id = NULL) { + public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, $source = NULL, $target = NULL, $language = NULL, $entity_type_id = NULL) { /* @var \Drupal\Core\Entity\ContentEntityInterface $entity */ - if ($entity = $request->attributes->get($entity_type_id)) { + if ($entity = $route_match->getParameter($entity_type_id)) { $operation = $route->getRequirement('_access_content_translation_manage'); diff --git a/core/modules/content_translation/src/Access/ContentTranslationOverviewAccess.php b/core/modules/content_translation/src/Access/ContentTranslationOverviewAccess.php index 893b8b1..932d2c9 100644 --- a/core/modules/content_translation/src/Access/ContentTranslationOverviewAccess.php +++ b/core/modules/content_translation/src/Access/ContentTranslationOverviewAccess.php @@ -9,8 +9,8 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Routing\Access\AccessInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; /** * Access check for entity translation overview. @@ -37,17 +37,17 @@ public function __construct(EntityManagerInterface $manager) { /** * Checks access to the translation overview for the entity and bundle. * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Request $request, AccountInterface $account) { - $entity_type = $request->attributes->get('entity_type_id'); - $entity = $request->attributes->get($entity_type); + public function access(RouteMatchInterface $route_match, AccountInterface $account) { + $entity_type = $route_match->getParameter('entity_type_id'); + $entity = $route_match->getParameter($entity_type); if ($entity && $entity->isTranslatable()) { // Get entity base info. $bundle = $entity->bundle(); diff --git a/core/modules/field_ui/src/Access/FormModeAccessCheck.php b/core/modules/field_ui/src/Access/FormModeAccessCheck.php index d35dd15..3a7e57a 100644 --- a/core/modules/field_ui/src/Access/FormModeAccessCheck.php +++ b/core/modules/field_ui/src/Access/FormModeAccessCheck.php @@ -9,9 +9,9 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Routing\Access\AccessInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; /** * Defines an access check for entity form mode routes. @@ -42,8 +42,8 @@ public function __construct(EntityManagerInterface $entity_manager) { * * @param \Symfony\Component\Routing\Route $route * The route to check against. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * @param string $form_mode_name @@ -60,11 +60,11 @@ public function __construct(EntityManagerInterface $entity_manager) { * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account, $form_mode_name = 'default', $bundle = NULL) { + public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, $form_mode_name = 'default', $bundle = NULL) { if ($entity_type_id = $route->getDefault('entity_type_id')) { if (!isset($bundle)) { $entity_type = $this->entityManager->getDefinition($entity_type_id); - $bundle = $request->attributes->get('_raw_variables')->get($entity_type->getBundleEntityType()); + $bundle = $route_match->getRawParameter($entity_type->getBundleEntityType()); } $visibility = FALSE; diff --git a/core/modules/field_ui/src/Access/ViewModeAccessCheck.php b/core/modules/field_ui/src/Access/ViewModeAccessCheck.php index 8e93040..4fef23e 100644 --- a/core/modules/field_ui/src/Access/ViewModeAccessCheck.php +++ b/core/modules/field_ui/src/Access/ViewModeAccessCheck.php @@ -9,9 +9,9 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Routing\Access\AccessInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; /** * Defines an access check for entity view mode routes. @@ -42,8 +42,8 @@ public function __construct(EntityManagerInterface $entity_manager) { * * @param \Symfony\Component\Routing\Route $route * The route to check against. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * @param string $view_mode_name @@ -60,11 +60,11 @@ public function __construct(EntityManagerInterface $entity_manager) { * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account, $view_mode_name = 'default', $bundle = NULL) { + public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, $view_mode_name = 'default', $bundle = NULL) { if ($entity_type_id = $route->getDefault('entity_type_id')) { if (!isset($bundle)) { $entity_type = $this->entityManager->getDefinition($entity_type_id); - $bundle = $request->attributes->get('_raw_variables')->get($entity_type->getBundleEntityType()); + $bundle = $route_match->getRawParameter($entity_type->getBundleEntityType()); } $visibility = FALSE; diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php index 7f1bec6..1075ee7 100644 --- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php @@ -139,11 +139,7 @@ public function build(RouteMatchInterface $route_match) { // Copy the path elements for up-casting. $route_request = $this->getRequestForPath(implode('/', $path_elements), $exclude); if ($route_request) { - $route_name = $route_request->attributes->get(RouteObjectInterface::ROUTE_NAME); - // Note that the parameters don't really matter here since we're - // passing in the request which already has the upcast attributes. - $parameters = array(); - $access = $this->accessManager->checkNamedRoute($route_name, $parameters, $this->currentUser, $route_request); + $access = $this->accessManager->checkRequest($route_request, $this->currentUser); if ($access) { $title = $this->titleResolver->getTitle($route_request, $route_request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)); } diff --git a/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php index fce97dc..fee5280 100644 --- a/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php +++ b/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php @@ -325,7 +325,7 @@ public function testBuildWithUserPath() { */ public function setupAccessManagerWithTrue() { $this->accessManager->expects($this->any()) - ->method('checkNamedRoute') + ->method('checkRequest') ->will($this->returnValue(TRUE)); } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityAccessCheckTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityAccessCheckTest.php index ba5ffcf..3b70d5e 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityAccessCheckTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityAccessCheckTest.php @@ -7,7 +7,7 @@ namespace Drupal\Tests\Core\Entity; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\Routing\Route; use Drupal\Core\Access\AccessCheckInterface; use Drupal\Core\Entity\EntityAccessCheck; @@ -25,7 +25,7 @@ class EntityAccessCheckTest extends UnitTestCase { */ public function testAccess() { $route = new Route('/foo', array(), array('_entity_access' => 'node.update')); - $request = new Request(); + $upcasted_arguments = new ParameterBag(); $node = $this->getMockBuilder('Drupal\node\Entity\Node') ->disableOriginalConstructor() ->getMock(); @@ -33,9 +33,9 @@ public function testAccess() { ->method('access') ->will($this->returnValue(TRUE)); $access_check = new EntityAccessCheck(); - $request->attributes->set('node', $node); + $upcasted_arguments->set('node', $node); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $access = $access_check->access($route, $request, $account); + $access = $access_check->access($route, $upcasted_arguments, $account); $this->assertSame(AccessCheckInterface::ALLOW, $access); }