diff --git a/core/core.services.yml b/core/core.services.yml index 367aec1..592b011 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -467,7 +467,7 @@ services: - [setCurrentUser, ['@?current_user']] access_manager: class: Drupal\Core\Access\AccessManager - arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager'] + arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager', '@controller_resolver'] calls: - [setContainer, ['@service_container']] - [setRequest, ['@?request']] diff --git a/core/lib/Drupal/Core/Access/AccessManager.php b/core/lib/Drupal/Core/Access/AccessManager.php index 42fc39a..f76dc24 100644 --- a/core/lib/Drupal/Core/Access/AccessManager.php +++ b/core/lib/Drupal/Core/Access/AccessManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Access; +use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\ParamConverter\ParamConverterManagerInterface; use Drupal\Core\ParamConverter\ParamNotConvertedException; use Drupal\Core\Routing\Access\AccessInterface as RoutingAccessInterface; @@ -85,6 +86,13 @@ class AccessManager extends ContainerAware { protected $request; /** + * The controller resolver. + * + * @var \Drupal\Core\Controller\ControllerResolverInterface + */ + protected $controllerResolver; + + /** * Constructs a AccessManager instance. * * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider @@ -93,11 +101,14 @@ class AccessManager extends ContainerAware { * The url generator. * @param \Drupal\Core\ParamConverter\ParamConverterManagerInterface $paramconverter_manager * The param converter manager. + * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver + * The controller resolver. */ - public function __construct(RouteProviderInterface $route_provider, UrlGeneratorInterface $url_generator, ParamConverterManagerInterface $paramconverter_manager) { + public function __construct(RouteProviderInterface $route_provider, UrlGeneratorInterface $url_generator, ParamConverterManagerInterface $paramconverter_manager, ControllerResolverInterface $controller_resolver) { $this->routeProvider = $route_provider; $this->urlGenerator = $url_generator; $this->paramConverterManager = $paramconverter_manager; + $this->controllerResolver = $controller_resolver; } /** @@ -264,11 +275,7 @@ protected function checkAll(array $checks, Route $route, Request $request, Accou $this->loadCheck($service_id); } - $service_access = $this->checks[$service_id]->access($route, $request, $account); - - if (!in_array($service_access, array(AccessInterface::ALLOW, AccessInterface::DENY, AccessInterface::KILL), TRUE)) { - throw new AccessException("Access error in $service_id. Access services can only return AccessInterface::ALLOW, AccessInterface::DENY, or AccessInterface::KILL constants."); - } + $service_access = $this->performCheck($service_id, $route, $request, $account); if ($service_access === AccessInterface::ALLOW) { $access = TRUE; @@ -298,7 +305,7 @@ protected function checkAll(array $checks, Route $route, Request $request, Accou * @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, Route $route, Request $request, AccountInterface $account) { // No checks == deny by default. $access = FALSE; @@ -307,11 +314,7 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a $this->loadCheck($service_id); } - $service_access = $this->checks[$service_id]->access($route, $request, $account); - - if (!in_array($service_access, array(AccessInterface::ALLOW, AccessInterface::DENY, AccessInterface::KILL), TRUE)) { - throw new AccessException("Access error in $service_id. Access services can only return AccessInterface::ALLOW, AccessInterface::DENY, or AccessInterface::KILL constants."); - } + $service_access = $this->performCheck($service_id, $route, $request, $account); if ($service_access === AccessInterface::ALLOW) { $access = TRUE; @@ -325,6 +328,41 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a } /** + * Performs the specified access check. + * + * @param string $service_id + * The access check service ID to use. + * @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\Session\AccountInterface $account + * The current user. + * + * @throws \Drupal\Core\Access\AccessException + * Thrown when the access check returns an invalid value. + * + * @return string + * Either the AccessInterface::ALLOW, AccessInterface::DENY, or + * AccessInterface::KILL constant. + */ + protected function performCheck($service_id, $route, $request, $account) { + // Add the route and account to trick the getArguments() method of the + // controller resolver. + $request->attributes->set('route', $route); + $request->attributes->set('account', $account); + + $controller = array($this->checks[$service_id], 'access'); + $arguments = $this->controllerResolver->getArguments($request, $controller); + $service_access = call_user_func_array($controller, $arguments); + + if (!in_array($service_access, array(AccessInterface::ALLOW, AccessInterface::DENY, AccessInterface::KILL), TRUE)) { + throw new AccessException("Access error in $service_id. Access services can only return AccessInterface::ALLOW, AccessInterface::DENY, or AccessInterface::KILL constants."); + } + return $service_access; + } + + /** * Lazy-loads access check services. * * @param string $service_id diff --git a/core/modules/contact/lib/Drupal/contact/Access/ContactPageAccess.php b/core/modules/contact/lib/Drupal/contact/Access/ContactPageAccess.php index 548d1fa..ac2fd75 100644 --- a/core/modules/contact/lib/Drupal/contact/Access/ContactPageAccess.php +++ b/core/modules/contact/lib/Drupal/contact/Access/ContactPageAccess.php @@ -11,6 +11,7 @@ use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; use Drupal\user\UserDataInterface; +use Drupal\user\UserInterface; use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; @@ -49,16 +50,14 @@ public function __construct(ConfigFactoryInterface $config_factory, UserDataInte /** * {@inheritdoc} */ - public function access(Route $route, Request $request, AccountInterface $account) { - $contact_account = $request->attributes->get('user'); - + public function access(Route $route, Request $request, AccountInterface $account, UserInterface $user = NULL) { // Anonymous users cannot have contact forms. - if ($contact_account->isAnonymous()) { + if ($user->isAnonymous()) { return static::DENY; } // Users may not contact themselves. - if ($account->id() == $contact_account->id()) { + if ($account->id() == $user->id()) { return static::DENY; } @@ -68,13 +67,13 @@ public function access(Route $route, Request $request, AccountInterface $account } // If requested user has been blocked, do not allow users to contact them. - if ($contact_account->isBlocked()) { + if ($user->isBlocked()) { return static::DENY; } // If the requested user has disabled their contact form, do not allow users // to contact them. - $account_data = $this->userData->get('contact', $contact_account->id(), 'enabled'); + $account_data = $this->userData->get('contact', $user->id(), 'enabled'); if (isset($account_data) && empty($account_data)) { return static::DENY; } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php index 8b115df..9b7b1b1 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php @@ -38,20 +38,18 @@ public function __construct(EntityManagerInterface $entity_manager) { /** * {@inheritdoc} */ - public function access(Route $route, Request $request, AccountInterface $account) { - if ($entity_type_id = $route->getDefault('entity_type_id')) { - $view_mode = $request->attributes->get('view_mode_name'); - - if (!($bundle = $request->attributes->get('bundle'))) { + public function access(Route $route, Request $request, AccountInterface $account, $view_mode_name = NULL, $bundle = NULL, $entity_type_id = NULL) { + if ($entity_type_id) { + if (!$bundle) { $entity_type = $this->entityManager->getDefinition($entity_type_id); $bundle = $request->attributes->get('_raw_variables')->get($entity_type->getBundleEntityType()); } $visibility = FALSE; - if (!$view_mode || $view_mode == 'default') { + if (!$view_mode_name || $view_mode_name == 'default') { $visibility = TRUE; } - elseif ($entity_display = $this->entityManager->getStorageController('entity_view_display')->load($entity_type_id . '.' . $bundle . '.' . $view_mode)) { + elseif ($entity_display = $this->entityManager->getStorageController('entity_view_display')->load($entity_type_id . '.' . $bundle . '.' . $view_mode_name)) { $visibility = $entity_display->status(); } diff --git a/core/modules/node/lib/Drupal/node/Access/NodeAddAccessCheck.php b/core/modules/node/lib/Drupal/node/Access/NodeAddAccessCheck.php index 8d72223..a0a14f1 100644 --- a/core/modules/node/lib/Drupal/node/Access/NodeAddAccessCheck.php +++ b/core/modules/node/lib/Drupal/node/Access/NodeAddAccessCheck.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\node\NodeTypeInterface; use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; @@ -38,11 +39,11 @@ public function __construct(EntityManagerInterface $entity_manager) { /** * {@inheritdoc} */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Route $route, Request $request, AccountInterface $account, NodeTypeInterface $node_type = NULL) { $access_controller = $this->entityManager->getAccessController('node'); // If a node type is set on the request, just check that. - if ($request->attributes->has('node_type')) { - return $access_controller->createAccess($request->attributes->get('node_type')->type, $account) ? static::ALLOW : static::DENY; + if ($node_type) { + return $access_controller->createAccess($node_type->id(), $account) ? static::ALLOW : static::DENY; } foreach (node_permissions_get_configured_types() as $type) { if ($access_controller->createAccess($type->type, $account)) { diff --git a/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php b/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php index d5fc878..293400d 100644 --- a/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php +++ b/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php @@ -10,7 +10,6 @@ use Drupal\Core\Access\AccessInterface; use Drupal\Core\Controller\ControllerBase; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; /** * Defines a controller for the toolbar module. @@ -33,8 +32,7 @@ public function subtreesJsonp() { /** * Checks access for the subtree controller. */ - public function checkSubTreeAccess(Request $request) { - $hash = $request->get('hash'); + public function checkSubTreeAccess($hash) { return ($this->currentUser()->hasPermission('access toolbar') && ($hash == _toolbar_get_subtrees_hash())) ? AccessInterface::ALLOW : AccessInterface::DENY; } diff --git a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php index 0e830e2..ce72e35 100644 --- a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php @@ -70,6 +70,13 @@ class AccessManagerTest extends UnitTestCase { protected $paramConverter; /** + * The controller resolver. + * + * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $controllerResolver; + + /** * The mocked account. * * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject @@ -99,14 +106,6 @@ protected function setUp() { $this->routeCollection->add('test_route_4', new Route('/test-route-4/{value}', array(), array('_access' => 'TRUE'))); $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); - $map = array(); - foreach ($this->routeCollection->all() as $name => $route) { - $map[] = array($name, array(), $route); - } - $map[] = array('test_route_4', array('value' => 'example'), $this->routeCollection->get('test_route_4')); - $this->routeProvider->expects($this->any()) - ->method('getRouteByName') - ->will($this->returnValueMap($map)); $map = array(); $map[] = array('test_route_1', array(), '/test-route-1'); @@ -122,12 +121,36 @@ protected function setUp() { $this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface'); $this->account = $this->getMock('Drupal\Core\Session\AccountInterface'); + $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface'); + $this->controllerResolver->expects($this->any()) + ->method('getArguments') + ->will($this->returnCallback(function (Request $request) { + return array( + $request->attributes->get('route'), + $request, + $request->attributes->get('account'), + ); + })); - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); + $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->controllerResolver); $this->accessManager->setContainer($this->container); } /** + * Sets up the route provider to return what is in the route collection. + */ + protected function setUpRouteProvider() { + $map = array(); + foreach ($this->routeCollection->all() as $name => $route) { + $map[] = array($name, array(), $route); + } + $map[] = array('test_route_4', array('value' => 'example'), $this->routeCollection->get('test_route_4')); + $this->routeProvider->expects($this->any()) + ->method('getRouteByName') + ->will($this->returnValueMap($map)); + } + + /** * Tests \Drupal\Core\Access\AccessManager::setChecks(). */ public function testSetChecks() { @@ -151,10 +174,6 @@ public function testSetChecks() { * Tests setChecks with a dynamic access checker. */ public function testSetChecksWithDynamicAccessChecker() { - // Setup the access manager. - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); - $this->accessManager->setContainer($this->container); - // Setup the dynamic access checker. $access_check = $this->getMock('Drupal\Core\Access\AccessCheckInterface'); $this->container->set('test_access', $access_check); @@ -190,6 +209,7 @@ public function testCheck() { } $this->setupAccessChecker(); + $this->setUpRouteProvider(); // An access checker got setup, but the routes haven't been setup using // setChecks. @@ -378,6 +398,7 @@ public function testCheckConjunctions($conjunction, $name, $condition_one, $cond */ public function testCheckNamedRoute() { $this->setupAccessChecker(); + $this->setUpRouteProvider(); $this->accessManager->setChecks($this->routeCollection); // Tests the access with routes without parameters. @@ -417,7 +438,6 @@ public function testCheckNamedRouteWithUpcastedValues() { $route = new Route('/test-route-1/{value}', array(), array('_test_access' => 'TRUE')); $this->routeCollection->add('test_route_1', $route); - $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $this->routeProvider->expects($this->any()) ->method('getRouteByName') ->with('test_route_1', array('value' => 'example')) @@ -426,12 +446,10 @@ public function testCheckNamedRouteWithUpcastedValues() { $map = array(); $map[] = array('test_route_1', array('value' => 'example'), '/test-route-1/example'); - $this->urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'); $this->urlGenerator->expects($this->any()) ->method('generate') ->will($this->returnValueMap($map)); - $this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface'); $this->paramConverter->expects($this->at(0)) ->method('convert') ->will($this->returnValue(array('value' => 'upcasted_value'))); @@ -444,8 +462,6 @@ public function testCheckNamedRouteWithUpcastedValues() { ->with('/test-route-1/example') ->will($this->returnValue($subrequest)); - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); - $this->accessManager->setContainer($this->container); $this->accessManager->setRequest(new Request()); $access_check = $this->getMock('Drupal\Core\Access\AccessCheckInterface'); @@ -476,7 +492,6 @@ public function testCheckNamedRouteWithDefaultValue() { $route = new Route('/test-route-1/{value}', array('value' => 'example'), array('_test_access' => 'TRUE')); $this->routeCollection->add('test_route_1', $route); - $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $this->routeProvider->expects($this->any()) ->method('getRouteByName') ->with('test_route_1', array()) @@ -485,13 +500,11 @@ public function testCheckNamedRouteWithDefaultValue() { $map = array(); $map[] = array('test_route_1', array('value' => 'example'), '/test-route-1/example'); - $this->urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'); $this->urlGenerator->expects($this->any()) ->method('generate') ->with('test_route_1', array('value' => 'example')) ->will($this->returnValueMap($map)); - $this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface'); $this->paramConverter->expects($this->at(0)) ->method('convert') ->with(array('value' => 'example', RouteObjectInterface::ROUTE_OBJECT => $route)) @@ -504,8 +517,6 @@ public function testCheckNamedRouteWithDefaultValue() { ->with('/test-route-1/example') ->will($this->returnValue($subrequest)); - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); - $this->accessManager->setContainer($this->container); $this->accessManager->setRequest(new Request()); $access_check = $this->getMock('Drupal\Core\Access\AccessCheckInterface'); @@ -530,8 +541,6 @@ public function testCheckNamedRouteWithDefaultValue() { * Tests checkNamedRoute given an invalid/non existing route name. */ public function testCheckNamedRouteWithNonExistingRoute() { - $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); - $this->routeProvider->expects($this->any()) ->method('getRouteByName') ->will($this->throwException(new RouteNotFoundException())); @@ -549,8 +558,6 @@ public function testCheckNamedRouteWithNonExistingRoute() { * @expectedException \Drupal\Core\Access\AccessException */ public function testCheckException($return_value, $access_mode) { - $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); - // Setup a test route for each access configuration. $requirements = array( '_test_incorrect_value' => 'TRUE', @@ -563,26 +570,22 @@ public function testCheckException($return_value, $access_mode) { ); $route = new Route('', array(), $requirements, $options); - $route_provider->expects($this->any()) + $this->routeProvider->expects($this->any()) ->method('getRouteByName') ->will($this->returnValue($route)); $request = new Request(); - $container = new ContainerBuilder(); - // Register a service that will return an incorrect value. $access_check = $this->getMock('Drupal\Core\Routing\Access\AccessInterface'); $access_check->expects($this->any()) ->method('access') ->will($this->returnValue($return_value)); - $container->set('test_incorrect_value', $access_check); + $this->container->set('test_incorrect_value', $access_check); - $access_manager = new AccessManager($route_provider, $this->urlGenerator, $this->paramConverter); - $access_manager->setContainer($container); - $access_manager->addCheckService('test_incorrect_value'); + $this->accessManager->addCheckService('test_incorrect_value'); - $access_manager->checkNamedRoute('test_incorrect_value', array(), $this->account, $request); + $this->accessManager->checkNamedRoute('test_incorrect_value', array(), $this->account, $request); } /** @@ -633,8 +636,6 @@ protected static function convertAccessCheckInterfaceToString($constant) { * Adds a default access check service to the container and the access manager. */ protected function setupAccessChecker() { - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); - $this->accessManager->setContainer($this->container); $access_check = new DefaultAccessCheck(); $this->container->register('test_access_default', $access_check); $this->accessManager->addCheckService('test_access_default', array('_access'));