From 450cc9891dc2c632867d54ab8321d8a35931364d Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Thu, 21 Aug 2014 12:36:06 +0100 Subject: [PATCH] Issue #2322809 by chx, dawehner, tim.plunkett: Tighten routing security by access checking in matchRequest. --- core/core.services.yml | 14 +- .../Core/EventSubscriber/AccessSubscriber.php | 113 ------------- core/lib/Drupal/Core/Path/PathValidator.php | 43 ++--- .../Drupal/Core/Routing/AccessAwareRouter.php | 148 +++++++++++++++++ .../Routing/AccessAwareRouterInterface.php | 35 ++++ core/lib/Drupal/Core/Url.php | 15 +- .../ConfigTranslationController.php | 44 +---- core/modules/simpletest/src/WebTestBase.php | 3 - .../system/src/PathBasedBreadcrumbBuilder.php | 4 + .../src/TestControllers.php | 2 +- .../LanguageNegotiationUserAdmin.php | 6 +- .../EventSubscriber/AccessSubscriberTest.php | 156 ------------------ .../Drupal/Tests/Core/ExternalUrlTest.php | 2 +- .../Core/Routing/AccessAwareRouterTest.php | 118 +++++++++++++ core/tests/Drupal/Tests/Core/UrlTest.php | 2 +- 15 files changed, 340 insertions(+), 365 deletions(-) delete mode 100644 core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php create mode 100644 core/lib/Drupal/Core/Routing/AccessAwareRouter.php create mode 100644 core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php delete mode 100644 core/tests/Drupal/Tests/Core/EventSubscriber/AccessSubscriberTest.php create mode 100644 core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php diff --git a/core/core.services.yml b/core/core.services.yml index b4e5fe324eb..b7dddb2f18b 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -431,12 +431,15 @@ services: link_generator: class: Drupal\Core\Utility\LinkGenerator arguments: ['@url_generator', '@module_handler'] + router: + class: Drupal\Core\Routing\AccessAwareRouter + arguments: ['@router.no_access_checks', '@access_manager', '@current_user'] router.dynamic: class: Symfony\Cmf\Component\Routing\DynamicRouter arguments: ['@router.request_context', '@router.matcher', '@url_generator'] tags: - { name: service_collector, tag: route_enhancer, call: addRouteEnhancer } - router: + router.no_access_checks: class: Symfony\Cmf\Component\Routing\ChainRouter calls: - [setContext, ['@router.request_context']] @@ -490,7 +493,7 @@ services: arguments: ['@config.factory'] path.validator: class: Drupal\Core\Path\PathValidator - arguments: ['@router', '@router.route_provider', '@request_stack', '@access_manager', '@current_user'] + arguments: ['@router', '@router.route_provider', '@request_stack'] # The argument to the hashing service defined in services.yml, to the # constructor of PhpassHashedPassword is the log2 number of iterations for @@ -633,13 +636,6 @@ services: arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager', '@access_arguments_resolver', '@request_stack'] calls: - [setContainer, ['@service_container']] - access_subscriber: - class: Drupal\Core\EventSubscriber\AccessSubscriber - arguments: ['@access_manager', '@current_user'] - calls: - - [setCurrentUser, ['@?current_user']] - tags: - - { name: event_subscriber } access_route_subscriber: class: Drupal\Core\EventSubscriber\AccessRouteSubscriber tags: diff --git a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php deleted file mode 100644 index 02a65edbae9..00000000000 --- a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php +++ /dev/null @@ -1,113 +0,0 @@ -accessManager = $access_manager; - $this->currentUser = $current_user; - } - - /** - * Verifies that the current user can access the requested path. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The Event to process. - * - * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException - * Thrown when the access got denied. - */ - public function onKernelRequestAccessCheck(GetResponseEvent $event) { - $request = $event->getRequest(); - - // 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); - - if (!$request->attributes->has(RouteObjectInterface::ROUTE_OBJECT)) { - // If no Route is available it is likely a static resource and access is - // handled elsewhere. - return; - } - - // Wrap this in a try/catch to ensure the '_controller_request' attribute - // can always be removed. - try { - $access = $this->accessManager->check($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT), $request, $this->currentUser); - } - catch (\Exception $e) { - $request->attributes->remove('_controller_request'); - throw $e; - } - - $request->attributes->remove('_controller_request'); - - if (!$access) { - throw new AccessDeniedHttpException(); - } - } - - /** - * 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; - } - - /** - * Registers the methods in this class that should be listeners. - * - * @return array - * An array of event listener definitions. - */ - static function getSubscribedEvents() { - $events[KernelEvents::REQUEST][] = array('onKernelRequestAccessCheck', 30); - - return $events; - } - -} diff --git a/core/lib/Drupal/Core/Path/PathValidator.php b/core/lib/Drupal/Core/Path/PathValidator.php index a90148b6960..d363417de82 100644 --- a/core/lib/Drupal/Core/Path/PathValidator.php +++ b/core/lib/Drupal/Core/Path/PathValidator.php @@ -8,12 +8,11 @@ namespace Drupal\Core\Path; use Drupal\Component\Utility\UrlHelper; -use Drupal\Core\Access\AccessManagerInterface; use Drupal\Core\ParamConverter\ParamNotConvertedException; use Drupal\Core\Routing\RequestHelper; use Drupal\Core\Routing\RouteProviderInterface; -use Drupal\Core\Session\AccountInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; /** @@ -42,20 +41,6 @@ class PathValidator implements PathValidatorInterface { */ protected $requestStack; - /** - * The access manager. - * - * @var \Drupal\Core\Access\AccessManagerInterface - */ - protected $accessManager; - - /** - * The user account. - * - * @var \Drupal\Core\Session\AccountInterface - */ - protected $account; - /** * Creates a new PathValidator. * @@ -65,17 +50,11 @@ class PathValidator implements PathValidatorInterface { * The route provider. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. - * @param \Drupal\Core\Access\AccessManagerInterface $access_manager - * The access manager. - * @param \Drupal\Core\Session\AccountInterface $account - * The user account. */ - public function __construct(RequestMatcherInterface $request_matcher, RouteProviderInterface $route_provider, RequestStack $request_stack, AccessManagerInterface $access_manager, AccountInterface $account) { + public function __construct(RequestMatcherInterface $request_matcher, RouteProviderInterface $route_provider, RequestStack $request_stack) { $this->requestMatcher = $request_matcher; $this->routeProvider = $route_provider; $this->requestStack = $request_stack; - $this->accessManager = $access_manager; - $this->account = $account; } /** @@ -93,25 +72,23 @@ class PathValidator implements PathValidatorInterface { return FALSE; } + // We can not use $this->requestMatcher->match() because we need to set + // the _menu_admin attribute to indicate a menu administrator is running + // the menu access check. $request = RequestHelper::duplicate($this->requestStack->getCurrentRequest(), '/' . $path); $request->attributes->set('_system_path', $path); - - // We indicate that a menu administrator is running the menu access check. $request->attributes->set('_menu_admin', TRUE); - // Attempt to match this path to provide a fully built request to the - // access checker. try { - $request->attributes->add($this->requestMatcher->matchRequest($request)); + $this->requestMatcher->matchRequest($request); } catch (ParamNotConvertedException $e) { return FALSE; } - - // Consult the access manager. - $routes = $collection->all(); - $route = reset($routes); - return $this->accessManager->check($route, $request, $this->account); + catch (AccessDeniedHttpException $e) { + return FALSE; + } + return TRUE; } } diff --git a/core/lib/Drupal/Core/Routing/AccessAwareRouter.php b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php new file mode 100644 index 00000000000..b8409dfe26c --- /dev/null +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php @@ -0,0 +1,148 @@ +chainRouter = $chain_router; + $this->accessManager = $access_manager; + $this->account = $account; + } + + /** + * {@inheritdoc} + */ + public function __call($name, $arguments) { + // Ensure to call every other function to the chained router. + // @todo Sadly does the ChainRouter not implement an interface in CMF. + return call_user_func_array([$this->chainRouter, $name], $arguments); + } + + /** + * {@inheritdoc} + */ + public function setContext(RequestContext $context) { + $this->chainRouter->setContext($context); + } + + /** + * {@inheritdoc} + */ + public function getContext() { + return $this->chainRouter->getContext(); + } + + /** + * {@inheritdoc} + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + * Thrown when access checking failed. + */ + public function matchRequest(Request $request) { + $parameters = $this->chainRouter->matchRequest($request); + $request->attributes->add($parameters); + $this->checkAccess($request); + // We can not return $parameters because the access check can change the + // request attributes. + return $request->attributes->all(); + } + + /** + * Apply access check service to the route and parameters in the request. + * + * @param \Symfony\Component\HttpFoundation\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; + } + } + + /** + * {@inheritdoc} + */ + public function getRouteCollection() { + return $this->chainRouter->getRouteCollection(); + } + + /** + * {@inheritdoc} + */ + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) { + return $this->chainRouter->generate($name, $parameters, $referenceType); + } + + /** + * {@inheritdoc} + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + * Thrown when access checking failed. + */ + public function match($pathinfo) { + return $this->matchRequest(Request::create($pathinfo)); + } + +} + diff --git a/core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php b/core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php new file mode 100644 index 00000000000..34fa75af22c --- /dev/null +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php @@ -0,0 +1,35 @@ +match('/' . $path); + // We use the router without access checks because URL objects might be + // created and stored for different users. + $result = \Drupal::service('router.no_access_checks')->match('/' . $path); } catch (ResourceNotFoundException $e) { throw new MatchingRouteNotFoundException(sprintf('No matching route could be found for the path "%s"', $path), 0, $e); @@ -141,14 +144,18 @@ class Url { * A request object. * * @return static - * A Url object. + * A Url object. Warning: the object is created even if the current user + * would get an access denied running the same request via the normal page + * flow. * * @throws \Drupal\Core\Routing\MatchingRouteNotFoundException * Thrown when the request cannot be matched. */ public static function createFromRequest(Request $request) { try { - $result = \Drupal::service('router')->matchRequest($request); + // We use the router without access checks because URL objects might be + // created and stored for different users. + $result = \Drupal::service('router.no_access_checks')->matchRequest($request); } catch (ResourceNotFoundException $e) { throw new MatchingRouteNotFoundException(sprintf('No matching route could be found for the request: %s', $request), 0, $e); diff --git a/core/modules/config_translation/src/Controller/ConfigTranslationController.php b/core/modules/config_translation/src/Controller/ConfigTranslationController.php index cb7f6b792ab..d6d9e34fcf6 100644 --- a/core/modules/config_translation/src/Controller/ConfigTranslationController.php +++ b/core/modules/config_translation/src/Controller/ConfigTranslationController.php @@ -12,13 +12,10 @@ use Drupal\Core\Access\AccessManagerInterface; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManagerInterface; -use Drupal\Core\ParamConverter\ParamNotConvertedException; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; /** @@ -167,15 +164,7 @@ class ConfigTranslationController extends ControllerBase { // Check access for the path/route for editing, so we can decide to // include a link to edit or not. - $route_request = $this->getRequestForPath($request, $mapper->getBasePath()); - $edit_access = FALSE; - if (!empty($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(); - $edit_access = $this->accessManager->checkNamedRoute($route_name, $parameters, $this->account, $route_request); - } + $edit_access = $this->accessManager->checkNamedRoute($mapper->getBaseRouteName(), $request->attributes->get('_raw_variables')->all(), $this->account); // Build list of operations. $operations = array(); @@ -228,35 +217,4 @@ class ConfigTranslationController extends ControllerBase { return $page; } - /** - * Matches a path in the router. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * Page request object. - * @param string $path - * Path to look up. - * - * @return \Symfony\Component\HttpFoundation\Request|null - * A populated request object or NULL if the patch could not be matched. - */ - protected function getRequestForPath(Request $request, $path) { - // @todo Use RequestHelper::duplicate once https://drupal.org/node/2090293 - // is fixed. - $route_request = Request::create($request->getBaseUrl() . '/' . $path); - // Find the system path by resolving aliases, language prefix, etc. - $processed = $this->pathProcessor->processInbound($path, $route_request); - $route_request->attributes->set('_system_path', $processed); - // Attempt to match this path to provide a fully built request. - try { - $route_request->attributes->add($this->router->matchRequest($route_request)); - return $route_request; - } - catch (ParamNotConvertedException $e) { - return NULL; - } - catch (ResourceNotFoundException $e) { - return NULL; - } - } - } diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index 89ff9db866c..a12c31020ec 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -704,9 +704,6 @@ abstract class WebTestBase extends TestBase { if ($pass) { $this->loggedInUser = $account; $this->container->get('current_user')->setAccount($account); - // @todo Temporary workaround for not being able to use synchronized - // services in non dumped container. - $this->container->get('access_subscriber')->setCurrentUser($account); } } diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php index c2765aa02bc..46c4ca0f44d 100644 --- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php @@ -20,6 +20,7 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Component\Utility\Unicode; use Symfony\Component\HttpFoundation\Request; use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Exception\MethodNotAllowedException; @@ -209,6 +210,9 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { catch (MethodNotAllowedException $e) { return NULL; } + catch (AccessDeniedHttpException $e) { + return NULL; + } } } diff --git a/core/modules/system/tests/modules/router_test_directory/src/TestControllers.php b/core/modules/system/tests/modules/router_test_directory/src/TestControllers.php index 0db6826f18b..1eec2632d68 100644 --- a/core/modules/system/tests/modules/router_test_directory/src/TestControllers.php +++ b/core/modules/system/tests/modules/router_test_directory/src/TestControllers.php @@ -56,7 +56,7 @@ class TestControllers { public function test9($uid) { $text = 'Route not matched.'; try { - $match = \Drupal::service('router')->match('/user/' . $uid); + $match = \Drupal::service('router.no_access_checks')->match('/user/' . $uid); if (isset($match['user']) && $match['user'] instanceof UserInterface) { $text = sprintf('User route "%s" was matched.', $match[RouteObjectInterface::ROUTE_NAME]); } diff --git a/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php index 4697dfaee0f..7e178320774 100644 --- a/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php +++ b/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php @@ -14,6 +14,7 @@ use Drupal\language\LanguageNegotiationMethodBase; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; @@ -110,7 +111,7 @@ class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase impleme * @return bool * TRUE if the path is administrative, FALSE otherwise. */ - public function isAdminPath(Request $request) { + protected function isAdminPath(Request $request) { $result = FALSE; if ($request && $this->adminContext) { // If called from an event subscriber, the request may not have the route @@ -127,6 +128,9 @@ class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase impleme catch (ResourceNotFoundException $e) { return FALSE; } + catch (AccessDeniedHttpException $e) { + return FALSE; + } $route_object = $attributes[RouteObjectInterface::ROUTE_OBJECT]; } $result = $this->adminContext->isAdminRoute($route_object); diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/AccessSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/AccessSubscriberTest.php deleted file mode 100644 index aa902bb501b..00000000000 --- a/core/tests/Drupal/Tests/Core/EventSubscriber/AccessSubscriberTest.php +++ /dev/null @@ -1,156 +0,0 @@ -event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') - ->disableOriginalConstructor() - ->getMock(); - - $this->request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request') - ->disableOriginalConstructor() - ->getMock(); - - $this->parameterBag = $this->getMockBuilder('Symfony\Component\HttpFoundation\ParameterBag') - ->disableOriginalConstructor() - ->getMock(); - - $this->route = $this->getMockBuilder('Symfony\Component\Routing\Route') - ->disableOriginalConstructor() - ->getMock(); - - $this->request->attributes = $this->parameterBag; - - $this->event->expects($this->any()) - ->method('getRequest') - ->will($this->returnValue($this->request)); - - $this->accessManager = $this->getMock('Drupal\Core\Access\AccessManagerInterface'); - - $this->currentUser = $this->getMockBuilder('Drupal\Core\Session\AccountInterface') - ->disableOriginalConstructor() - ->getMock(); - } - - /** - * Tests access denied throws an exception. - * - * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException - */ - public function testAccessSubscriberThrowsAccessDeniedException() { - - $this->parameterBag->expects($this->any()) - ->method('has') - ->with(RouteObjectInterface::ROUTE_OBJECT) - ->will($this->returnValue(TRUE)); - - $this->parameterBag->expects($this->any()) - ->method('get') - ->with(RouteObjectInterface::ROUTE_OBJECT) - ->will($this->returnValue($this->route)); - - $this->accessManager->expects($this->any()) - ->method('check') - ->with($this->anything()) - ->will($this->returnValue(FALSE)); - - $subscriber = new AccessSubscriber($this->accessManager, $this->currentUser); - $subscriber->onKernelRequestAccessCheck($this->event); - } - - /** - * Tests that the AccessSubscriber only acts on requests with route object. - */ - public function testAccessSubscriberOnlyChecksForRequestsWithRouteObject() { - $this->parameterBag->expects($this->any()) - ->method('has') - ->with(RouteObjectInterface::ROUTE_OBJECT) - ->will($this->returnValue(FALSE)); - - $this->accessManager->expects($this->never())->method('check'); - - $subscriber = new AccessSubscriber($this->accessManager, $this->currentUser); - $subscriber->onKernelRequestAccessCheck($this->event); - } - - /** - * Tests that if access is granted, AccessSubscriber will not throw an exception. - */ - public function testAccessSubscriberDoesNotAlterRequestIfAccessManagerGrantsAccess() { - $this->parameterBag->expects($this->once()) - ->method('has') - ->with(RouteObjectInterface::ROUTE_OBJECT) - ->will($this->returnValue(TRUE)); - - $this->parameterBag->expects($this->once()) - ->method('get') - ->with(RouteObjectInterface::ROUTE_OBJECT) - ->will($this->returnValue($this->route)); - - $this->accessManager->expects($this->once()) - ->method('check') - ->with($this->equalTo($this->route)) - ->will($this->returnValue(TRUE)); - - $subscriber = new AccessSubscriber($this->accessManager, $this->currentUser); - // We're testing that no exception is thrown in this case. There is no - // return. - $subscriber->onKernelRequestAccessCheck($this->event); - } - -} diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php index ce0fb84c9c7..3285a9d821f 100644 --- a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php +++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php @@ -53,7 +53,7 @@ class ExternalUrlTest extends UnitTestCase { $this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface'); $container = new ContainerBuilder(); - $container->set('router', $this->router); + $container->set('router.no_access_checks', $this->router); $container->set('url_generator', $this->urlGenerator); \Drupal::setContainer($container); } diff --git a/core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php b/core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php new file mode 100644 index 00000000000..846e1edd2a0 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php @@ -0,0 +1,118 @@ +route = new Route('test'); + $this->accessManager = $this->getMock('Drupal\Core\Access\AccessManagerInterface'); + $this->currentUser = $this->getMock('Drupal\Core\Session\AccountInterface'); + } + + /** + * Sets up a chain router with matchRequest. + */ + protected function setupRouter() { + $this->chainRouter = $this->getMockBuilder('Symfony\Cmf\Component\Routing\ChainRouter') + ->disableOriginalConstructor() + ->getMock(); + $this->chainRouter->expects($this->once()) + ->method('matchRequest') + ->will($this->returnValue(array(RouteObjectInterface::ROUTE_OBJECT => $this->route))); + $this->router = new AccessAwareRouter($this->chainRouter, $this->accessManager, $this->currentUser); + } + + /** + * Tests the matchRequest() function for access allowed. + */ + public function testMatchRequestAllowed() { + $this->setupRouter(); + $this->accessManager->expects($this->once()) + ->method('check') + ->will($this->returnValue(TRUE)); + $request = new Request(); + $parameters = $this->router->matchRequest($request); + $this->assertSame($request->attributes->all(), array(RouteObjectInterface::ROUTE_OBJECT => $this->route)); + $this->assertSame($parameters, array(RouteObjectInterface::ROUTE_OBJECT => $this->route)); + } + + /** + * Tests the matchRequest() function for access denied. + * + * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + public function testMatchRequestDenied() { + $this->setupRouter(); + $this->accessManager->expects($this->once()) + ->method('check') + ->will($this->returnValue(FALSE)); + $this->router->matchRequest(new Request()); + } + + /** + * Ensure that methods are passed to the wrapped router. + * + * @covers ::__call + */ + public function testCall() { + $mock_router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + + $this->chainRouter = $this->getMockBuilder('Symfony\Cmf\Component\Routing\ChainRouter') + ->disableOriginalConstructor() + ->setMethods(['add']) + ->getMock(); + $this->chainRouter->expects($this->once()) + ->method('add') + ->with($mock_router) + ->willReturnSelf(); + $this->router = new AccessAwareRouter($this->chainRouter, $this->accessManager, $this->currentUser); + + $this->router->add($mock_router); + } + +} diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php index 324f9e9492f..735af218ede 100644 --- a/core/tests/Drupal/Tests/Core/UrlTest.php +++ b/core/tests/Drupal/Tests/Core/UrlTest.php @@ -61,7 +61,7 @@ class UrlTest extends UnitTestCase { $this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface'); $container = new ContainerBuilder(); - $container->set('router', $this->router); + $container->set('router.no_access_checks', $this->router); $container->set('url_generator', $this->urlGenerator); \Drupal::setContainer($container); }