Commit cd15b235 authored by catch's avatar catch

Issue #2046737 by dawehner, pwolanin, tim.plunkett: Add a method to the...

Issue #2046737 by dawehner, pwolanin, tim.plunkett: Add a method to the AccessManager that only needs a route name and parameters.
parent d789fcbf
...@@ -379,8 +379,10 @@ services: ...@@ -379,8 +379,10 @@ services:
- [setRequest, ['@?request']] - [setRequest, ['@?request']]
access_manager: access_manager:
class: Drupal\Core\Access\AccessManager class: Drupal\Core\Access\AccessManager
arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager']
calls: calls:
- [setContainer, ['@service_container']] - [setContainer, ['@service_container']]
- [setRequest, ['@?request']]
access_subscriber: access_subscriber:
class: Drupal\Core\EventSubscriber\AccessSubscriber class: Drupal\Core\EventSubscriber\AccessSubscriber
tags: tags:
......
...@@ -7,11 +7,17 @@ ...@@ -7,11 +7,17 @@
namespace Drupal\Core\Access; namespace Drupal\Core\Access;
use Drupal\Core\ParamConverter\ParamConverterManager;
use Drupal\Core\Routing\RouteProviderInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\DependencyInjection\ContainerAware; use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
/** /**
* Attaches access check services to routes and runs them on request. * Attaches access check services to routes and runs them on request.
...@@ -48,6 +54,63 @@ class AccessManager extends ContainerAware { ...@@ -48,6 +54,63 @@ class AccessManager extends ContainerAware {
*/ */
protected $dynamicRequirementMap; protected $dynamicRequirementMap;
/**
* The route provider.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected $routeProvider;
/**
* The url generator.
*
* @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface
*/
protected $urlGenerator;
/**
* The paramconverter manager.
*
* @var \Drupal\Core\ParamConverter\ParamConverterManager
*/
protected $paramConverterManager;
/**
* A request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* 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\ParamConverterManager $paramconverter_manager
* The param converter manager.
*/
public function __construct(RouteProviderInterface $route_provider, UrlGeneratorInterface $url_generator, ParamConverterManager $paramconverter_manager) {
$this->routeProvider = $route_provider;
$this->urlGenerator = $url_generator;
$this->paramConverterManager = $paramconverter_manager;
}
/**
* Sets the request object to use.
*
* This is used by the RouterListener to make additional request attributes
* available.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*/
public function setRequest(Request $request) {
$this->request = $request;
}
/** /**
* Registers a new AccessCheck by service ID. * Registers a new AccessCheck by service ID.
* *
...@@ -107,6 +170,43 @@ protected function applies(Route $route) { ...@@ -107,6 +170,43 @@ protected function applies(Route $route) {
return $checks; return $checks;
} }
/**
* Checks a named route with parameters against applicable access check services.
*
* Determines whether the route is accessible or not.
*
* @param string $route_name
* The route to check access to.
* @param array $parameters
* Optional array of values to substitute into the route path patern.
* @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(), Request $route_request = NULL) {
try {
$route = $this->routeProvider->getRouteByName($route_name, $parameters);
if (empty($route_request)) {
// Create a request and copy the account from the current request.
$route_request = Request::create($this->urlGenerator->generate($route_name, $parameters));
$defaults = $parameters;
$defaults['_account'] = $this->request->attributes->get('_account');
$defaults[RouteObjectInterface::ROUTE_OBJECT] = $route;
$route_request->attributes->add($this->paramConverterManager->enhance($defaults, $route_request));
}
return $this->check($route, $route_request);
}
catch (RouteNotFoundException $e) {
return FALSE;
}
catch (NotFoundHttpException $e) {
return FALSE;
}
}
/** /**
* Checks a route against applicable access check services. * Checks a route against applicable access check services.
* *
...@@ -118,7 +218,7 @@ protected function applies(Route $route) { ...@@ -118,7 +218,7 @@ protected function applies(Route $route) {
* The incoming request object. * The incoming request object.
* *
* @return bool * @return bool
* Returns TRUE if the user has access to the route, otherwise FALSE. * Returns TRUE if the user has access to the route, otherwise FALSE.
* *
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* If any access check denies access or none explicitly approve. * If any access check denies access or none explicitly approve.
......
...@@ -8,12 +8,16 @@ ...@@ -8,12 +8,16 @@
namespace Drupal\Tests\Core\Access; namespace Drupal\Tests\Core\Access;
use Drupal\Core\Access\AccessCheckInterface; use Drupal\Core\Access\AccessCheckInterface;
use Drupal\Core\Access\AccessInterface;
use Drupal\Core\Access\AccessManager; use Drupal\Core\Access\AccessManager;
use Drupal\Core\Access\DefaultAccessCheck; use Drupal\Core\Access\DefaultAccessCheck;
use Drupal\system\Tests\Routing\MockRouteProvider;
use Drupal\Tests\UnitTestCase; use Drupal\Tests\UnitTestCase;
use Drupal\router_test\Access\DefinedTestAccessCheck; use Drupal\router_test\Access\DefinedTestAccessCheck;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
...@@ -45,6 +49,27 @@ class AccessManagerTest extends UnitTestCase { ...@@ -45,6 +49,27 @@ class AccessManagerTest extends UnitTestCase {
*/ */
protected $accessManager; protected $accessManager;
/**
* The route provider.
*
* @var \PHPUnit_Framework_MockObject_MockObject
*/
protected $routeProvider;
/**
* The url generator
*
* @var \PHPUnit_Framework_MockObject_MockObject
*/
protected $urlGenerator;
/**
* The parameter converter.
*
* @var \PHPUnit_Framework_MockObject_MockObject
*/
protected $paramConverter;
public static function getInfo() { public static function getInfo() {
return array( return array(
'name' => 'Access manager tests', 'name' => 'Access manager tests',
...@@ -60,13 +85,38 @@ protected function setUp() { ...@@ -60,13 +85,38 @@ protected function setUp() {
parent::setUp(); parent::setUp();
$this->container = new ContainerBuilder(); $this->container = new ContainerBuilder();
$this->accessManager = new AccessManager();
$this->accessManager->setContainer($this->container);
$this->routeCollection = new RouteCollection(); $this->routeCollection = new RouteCollection();
$this->routeCollection->add('test_route_1', new Route('/test-route-1')); $this->routeCollection->add('test_route_1', new Route('/test-route-1'));
$this->routeCollection->add('test_route_2', new Route('/test-route-2', array(), array('_access' => 'TRUE'))); $this->routeCollection->add('test_route_2', new Route('/test-route-2', array(), array('_access' => 'TRUE')));
$this->routeCollection->add('test_route_3', new Route('/test-route-3', array(), array('_access' => 'FALSE'))); $this->routeCollection->add('test_route_3', new Route('/test-route-3', array(), array('_access' => 'FALSE')));
$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');
$map[] = array('test_route_2', array(), '/test-route-2');
$map[] = array('test_route_3', array(), '/test-route-3');
$map[] = array('test_route_4', array('value' => 'example'), '/test-route-4/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\ParamConverterManager');
$this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter);
$this->accessManager->setContainer($this->container);
} }
/** /**
...@@ -255,6 +305,119 @@ public function testStaticAccessCheckInterface() { ...@@ -255,6 +305,119 @@ public function testStaticAccessCheckInterface() {
$this->accessManager->setChecks($this->routeCollection); $this->accessManager->setChecks($this->routeCollection);
} }
/**
* Tests the checkNamedRoute method.
*
* @see \Drupal\Core\Access\AccessManager::checkNamedRoute()
*/
public function testCheckNamedRoute() {
$this->setupAccessChecker();
$this->accessManager->setChecks($this->routeCollection);
// Tests the access with routes without parameters.
$request = new Request();
$this->assertTrue($this->accessManager->checkNamedRoute('test_route_2', array(), $request));
$this->assertFalse($this->accessManager->checkNamedRoute('test_route_3', array(), $request));
// Tests the access with routes with parameters with given request.
$request = new Request();
$request->attributes->set('value', 'example');
$request->attributes->set('value2', 'example2');
$this->assertTrue($this->accessManager->checkNamedRoute('test_route_4', array(), $request));
// Tests the access with routes without given request.
$account = $this->getMock('Drupal\Core\Session\AccountInterface');
$this->accessManager->setRequest(new Request(array(), array(), array('_account' => $account)));
$this->paramConverter->expects($this->at(0))
->method('enhance')
->will($this->returnValue(array()));
$this->paramConverter->expects($this->at(1))
->method('enhance')
->will($this->returnValue(array()));
// Tests the access with routes with parameters without given request.
$this->assertTrue($this->accessManager->checkNamedRoute('test_route_2', array()));
$this->assertTrue($this->accessManager->checkNamedRoute('test_route_4', array('value' => 'example')));
}
/**
* Tests the checkNamedRoute with upcasted values.
*
* @see \Drupal\Core\Access\AccessManager::checkNamedRoute()
*/
public function testCheckNamedRouteWithUpcastedValues() {
$account = $this->getMock('Drupal\Core\Session\AccountInterface');
$this->routeCollection = new RouteCollection();
$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'))
->will($this->returnValue($route));
$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\ParamConverterManager');
$this->paramConverter->expects($this->at(0))
->method('enhance')
->will($this->returnValue(array('value' => 'upcasted_value')));
$subrequest = Request::create('/test-route-1/example');
$class = $this->getMockClass('Symfony\Component\HttpFoundation\Request', array('create'));
$class::staticExpects($this->any())
->method('create')
->with('/test-route-1/example')
->will($this->returnValue($subrequest));
$this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter);
$this->accessManager->setContainer($this->container);
$this->accessManager->setRequest(new Request(array(), array(), array('_account' => $account)));
$access_check = $this->getMock('Drupal\Core\Access\AccessCheckInterface');
$access_check->expects($this->any())
->method('applies')
->will($this->returnValue(TRUE));
$access_check->expects($this->any())
->method('access')
->with($route, $subrequest)
->will($this->returnValue(AccessInterface::KILL));
$subrequest->attributes->set('value', 'upcasted_value');
$this->container->register('test_access', $access_check);
$this->accessManager->addCheckService('test_access');
$this->accessManager->setChecks($this->routeCollection);
$this->assertFalse($this->accessManager->checkNamedRoute('test_route_1', array('value' => 'example')));
}
/**
* 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()));
$this->setupAccessChecker();
$this->assertFalse($this->accessManager->checkNamedRoute('test_route_1'), 'A non existing route lead to access.');
}
/** /**
* Converts AccessCheckInterface constants to a string. * Converts AccessCheckInterface constants to a string.
* *
...@@ -283,7 +446,7 @@ protected static function convertAccessCheckInterfaceToString($constant) { ...@@ -283,7 +446,7 @@ protected static function convertAccessCheckInterfaceToString($constant) {
* Adds a default access check service to the container and the access manager. * Adds a default access check service to the container and the access manager.
*/ */
protected function setupAccessChecker() { protected function setupAccessChecker() {
$this->accessManager = new AccessManager(); $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter);
$this->accessManager->setContainer($this->container); $this->accessManager->setContainer($this->container);
$access_check = new DefaultAccessCheck(); $access_check = new DefaultAccessCheck();
$this->container->register('test_access_default', $access_check); $this->container->register('test_access_default', $access_check);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment