Commit 50979d21 authored by catch's avatar catch

Issue #2107137 by dawehner, damiankloip, tim.plunkett: Fix the DX for...

Issue #2107137 by dawehner, damiankloip, tim.plunkett: Fix the DX for declaring custom access checkers.
parent bcd866b6
......@@ -425,6 +425,11 @@ services:
class: Drupal\Core\Theme\ThemeAccessCheck
tags:
- { name: access_check }
access_check.custom:
class: Drupal\Core\Access\CustomAccessCheck
arguments: ['@controller_resolver']
tags:
- { name: access_check }
maintenance_mode_subscriber:
class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber
tags:
......
<?php
/**
* @file
* Contains \Drupal\Core\Access\CustomAccessCheck.
*/
namespace Drupal\Core\Access;
use Drupal\Core\Controller\ControllerResolverInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Defines an access checker that allows specifying a custom method for access.
*
* You should only use it when you are sure that the access callback will not be
* reused. Good examples in core are Edit or Toolbar module.
*
* The method is called on another instance of the controller class, so you
* cannot reuse any stored property of your actual controller instance used
* to generate the output.
*/
class CustomAccessCheck implements StaticAccessCheckInterface {
/**
* The controller resolver.
*
* @var \Drupal\Core\Controller\ControllerResolverInterface
*/
protected $controllerResolver;
/**
* Constructs a CustomAccessCheck instance.
*
* @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
* The controller resolver.
*/
public function __construct(ControllerResolverInterface $controller_resolver) {
$this->controllerResolver = $controller_resolver;
}
/**
* {@inheritdoc}
*/
public function appliesTo() {
return array('_custom_access');
}
/**
* {@inheritdoc}
*/
public function access(Route $route, Request $request) {
$access_controller = $route->getRequirement('_custom_access');
$controller = $this->controllerResolver->getControllerFromDefinition($access_controller);
$arguments = $this->controllerResolver->getArguments($request, $controller);
return call_user_func_array($controller, $arguments);
}
}
<?php
/**
* @file
* Contains \Drupal\toolbar\Access\SubtreeAccess.
*/
namespace Drupal\toolbar\Access;
use Drupal\Core\Access\StaticAccessCheckInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Defines a special access checker for the toolbar subtree route.
*/
class SubtreeAccess implements StaticAccessCheckInterface {
/**
* {@inheritdoc}
*/
public function appliesTo() {
return array('_access_toolbar_subtree');
}
/**
* {@inheritdoc}
*/
public function access(Route $route, Request $request) {
$hash = $request->get('hash');
return (user_access('access toolbar') && ($hash == _toolbar_get_subtrees_hash())) ? static::ALLOW : static::DENY;
}
}
......@@ -7,12 +7,15 @@
namespace Drupal\toolbar\Routing;
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.
*/
class ToolbarController {
class ToolbarController extends ControllerBase {
/**
* Returns the rendered subtree of each top-level toolbar link.
......@@ -27,4 +30,12 @@ public function subtreesJsonp() {
return $response;
}
/**
* Checks access for the subtree controller.
*/
public function checkSubTreeAccess(Request $request) {
$hash = $request->get('hash');
return ($this->currentUser()->hasPermission('access toolbar') && ($hash == _toolbar_get_subtrees_hash())) ? AccessInterface::ALLOW : AccessInterface::DENY;
}
}
......@@ -3,4 +3,4 @@ toolbar.subtrees:
defaults:
_controller: '\Drupal\toolbar\Routing\ToolbarController::subtreesJsonp'
requirements:
_access_toolbar_subtree: 'TRUE'
_custom_access: '\Drupal\toolbar\Routing\ToolbarController::checkSubTreeAccess'
services:
access_check.toolbar_subtree:
class: Drupal\toolbar\Access\SubtreeAccess
tags:
- { name: access_check }
cache.toolbar:
class: Drupal\Core\Cache\CacheBackendInterface
tags:
......
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\CustomAccessCheckTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Access\AccessInterface;
use Drupal\Core\Access\CustomAccessCheck;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Tests the custom access checker.
*
* @see \Drupal\Core\Access\CustomAccessCheck
*/
class CustomAccessCheckTest extends UnitTestCase {
/**
* The access checker to test.
*
* @var \Drupal\Core\Access\CustomAccessCheck
*/
protected $accessChecker;
/**
* The mocked controller resolver.
*
* @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $controllerResolver;
public static function getInfo() {
return array(
'name' => 'Custom access check',
'description' => 'Tests the custom access checker.',
'group' => 'Access'
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
$this->accessChecker = new CustomAccessCheck($this->controllerResolver);
}
/**
* Tests the appliesTo method.
*/
public function testAppliesTo() {
$this->assertEquals($this->accessChecker->appliesTo(), array('_custom_access'));
}
/**
* Test the access method.
*/
public function testAccess() {
$request = new Request(array());
$this->controllerResolver->expects($this->at(0))
->method('getControllerFromDefinition')
->with('\Drupal\Tests\Core\Access\TestController::accessDeny')
->will($this->returnValue(array(new TestController(), 'accessDeny')));
$this->controllerResolver->expects($this->at(1))
->method('getArguments')
->will($this->returnValue(array()));
$this->controllerResolver->expects($this->at(2))
->method('getControllerFromDefinition')
->with('\Drupal\Tests\Core\Access\TestController::accessAllow')
->will($this->returnValue(array(new TestController(), 'accessAllow')));
$this->controllerResolver->expects($this->at(3))
->method('getArguments')
->will($this->returnValue(array()));
$this->controllerResolver->expects($this->at(4))
->method('getControllerFromDefinition')
->with('\Drupal\Tests\Core\Access\TestController::accessParameter')
->will($this->returnValue(array(new TestController(), 'accessParameter')));
$this->controllerResolver->expects($this->at(5))
->method('getArguments')
->will($this->returnValue(array('parameter' => 'TRUE')));
$route = new Route('/test-route', array(), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessDeny'));
$this->assertNull($this->accessChecker->access($route, $request));
$route = new Route('/test-route', array(), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessAllow'));
$this->assertTrue($this->accessChecker->access($route, $request));
$route = new Route('/test-route', array('parameter' => 'TRUE'), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessParameter'));
$this->assertTrue($this->accessChecker->access($route, $request));
}
}
class TestController {
public function accessAllow() {
return AccessInterface::ALLOW;
}
public function accessDeny() {
return AccessInterface::DENY;
}
public function accessParameter($parameter) {
if ($parameter == 'TRUE') {
return AccessInterface::ALLOW;
}
else {
return AccessInterface::DENY;
}
}
}
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