Commit f83208f2 authored by webchick's avatar webchick

Issue #1956896 by dawehner, alexpott, msonnabaum, klausi: Added role based access checker.

parent b2f931d3
......@@ -17,22 +17,22 @@ Undefined index: foo
</testsuite>
<testsuite name="Drupal\Tests\Core\NestedArrayUnitTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Core/NestedArrayUnitTest.php" namespace="Drupal\Tests\Core" fullPackage="Drupal.Tests.Core" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/>
<testsuite name="Drupal\breakpoint\Tests\BreakpointMediaQueryTest" file="/home/chx/www/system/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php" namespace="Drupal\breakpoint\Tests" fullPackage="Drupal.breakpoint.Tests" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/>
<testsuite name="Drupal\Tests\Core\Route\RouterRoleTest" file="/var/www/d8/core/tests/Drupal/Tests/Core/Route/RouterRoleTest.php" namespace="Drupal\Tests\Core\Route" fullPackage="Drupal.Tests.Core.Route" tests="3" assertions="3" failures="3" errors="0" time="0.009176">
<testsuite name="Drupal\Tests\Core\Route\RouterRoleTest::testRoleAccess" tests="3" assertions="3" failures="3" errors="0" time="0.009176">
<testsuite name="Drupal\Tests\Core\Route\RoleAccessCheckTest" file="/var/www/d8/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTestkTest.php" namespace="Drupal\Tests\Core\Route" fullPackage="Drupal.Tests.Core.Route" tests="3" assertions="3" failures="3" errors="0" time="0.009176">
<testsuite name="Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess" tests="3" assertions="3" failures="3" errors="0" time="0.009176">
<testcase name="testRoleAccess with data set #0" assertions="1" time="0.004519">
<failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RouterRoleTest::testRoleAccess with data set #0 ('role_test_1', array(Drupal\user\Plugin\Core\Entity\User, Drupal\user\Plugin\Core\Entity\User))
<failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess with data set #0 ('role_test_1', array(Drupal\user\Plugin\Core\Entity\User, Drupal\user\Plugin\Core\Entity\User))
Access granted for user with the roles role_test_1 on path: role_test_1
Failed asserting that false is true.
</failure>
</testcase>
<testcase name="testRoleAccess with data set #1" assertions="1" time="0.002354">
<failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RouterRoleTest::testRoleAccess with data set #1 ('role_test_2', array(Drupal\user\Plugin\Core\Entity\User, Drupal\user\Plugin\Core\Entity\User))
<failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess with data set #1 ('role_test_2', array(Drupal\user\Plugin\Core\Entity\User, Drupal\user\Plugin\Core\Entity\User))
Access granted for user with the roles role_test_2 on path: role_test_2
Failed asserting that false is true.
</failure>
</testcase>
<testcase name="testRoleAccess with data set #2" assertions="1" time="0.002303">
<failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RouterRoleTest::testRoleAccess with data set #2 ('role_test_3', array(Drupal\user\Plugin\Core\Entity\User))
<failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess with data set #2 ('role_test_3', array(Drupal\user\Plugin\Core\Entity\User))
Access granted for user with the roles role_test_1, role_test_2 on path: role_test_3
Failed asserting that false is true.
</failure>
......
<?php
/**
* @file
* Contains \Drupal\user\Access\RoleAccessCheck.
*/
namespace Drupal\user\Access;
use Drupal\Core\Access\AccessCheckInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Determines access to routes based on roles.
*
* You can specify the '_role' key on route requirements. If you specify a
* single role, users with that role with have access. If you specify multiple
* ones you can conjunct them with AND by using a "+" and with OR by using ",".
*/
class RoleAccessCheck implements AccessCheckInterface {
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
return array_key_exists('_role', $route->getRequirements());
}
/**
* {@inheritdoc}
*/
public function access(Route $route, Request $request) {
// Requirements just allow strings, so this might be a comma separated list.
$rid_string = $route->getRequirement('_role');
// @todo Replace the role check with a correctly injected and session-using
// alternative.
$account = $GLOBALS['user'];
$explode_and = array_filter(array_map('trim', explode('+', $rid_string)));
if (count($explode_and) > 1) {
$diff = array_diff($explode_and, array_keys($account->roles));
if (empty($diff)) {
return TRUE;
}
}
else {
$explode_or = array_filter(array_map('trim', explode(',', $rid_string)));
$intersection = array_intersect($explode_or, array_keys($account->roles));
if (!empty($intersection)) {
return TRUE;
}
}
// If there is no allowed role, return NULL to give other checks a chance.
return NULL;
}
}
......@@ -7,6 +7,10 @@ services:
class: Drupal\user\Access\RegisterAccessCheck
tags:
- { name: access_check }
access_check.user.role:
class: Drupal\user\Access\RoleAccessCheck
tags:
- { name: access_check }
user.data:
class: Drupal\user\UserData
arguments: ['@database']
......
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Route\RoleAccessCheckTest.
*/
namespace Drupal\Tests\Core\Route;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
use Drupal\user\Access\RoleAccessCheck;
use Drupal\user\Plugin\Core\Entity\User;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
// Needed because the Entity class uses this constant.
// @todo Remove once http://drupal.org/node/1620010 is in.
if (!defined('LANGUAGE_NOT_SPECIFIED')) {
define('LANGUAGE_NOT_SPECIFIED', 'und');
}
/**
* Defines tests for role based access in routes.
*
* @see \Drupal\user\Access\RoleAccessCheck
*/
class RoleAccessCheckTest extends UnitTestCase {
public static function getInfo() {
return array(
'name' => 'Router Role tests',
'description' => 'Test for the role based access checker in the routing system.',
'group' => 'Routing',
);
}
/**
* Generates the test route collection.
*
* @return \Symfony\Component\Routing\RouteCollection
* Returns the test route collection.
*/
protected function getTestRouteCollection() {
$route_collection = new RouteCollection();
$route_collection->add('role_test_1', new Route('/role_test_1',
array(
'_controller' => '\Drupal\router_test\TestControllers::test1',
),
array(
'_role' => 'role_test_1',
)
));
$route_collection->add('role_test_2', new Route('/role_test_2',
array(
'_controller' => '\Drupal\router_test\TestControllers::test1',
),
array(
'_role' => 'role_test_2',
)
));
$route_collection->add('role_test_3', new Route('/role_test_3',
array(
'_controller' => '\Drupal\router_test\TestControllers::test1',
),
array(
'_role' => 'role_test_1+role_test_2',
)
));
// Ensure that trimming the values works on "OR" conjunctions.
$route_collection->add('role_test_4', new Route('/role_test_4',
array(
'_controller' => '\Drupal\router_test\TestControllers::test1',
),
array(
'_role' => 'role_test_1 + role_test_2',
)
));
$route_collection->add('role_test_5', new Route('/role_test_5',
array(
'_controller' => '\Drupal\router_test\TestControllers::test1',
),
array(
'_role' => 'role_test_1,role_test_2',
)
));
// Ensure that trimming the values works on "AND" conjunctions.
$route_collection->add('role_test_6', new Route('/role_test_6',
array(
'_controller' => '\Drupal\router_test\TestControllers::test1',
),
array(
'_role' => 'role_test_1 , role_test_2',
)
));
return $route_collection;
}
/**
* Provides data for the role access test.
*
* @see \Drupal\Tests\Core\Route\RouterRoleTest::testRoleAccess
*/
public function roleAccessProvider() {
// Setup two different roles used in the test.
$rid_1 = 'role_test_1';
$rid_2 = 'role_test_2';
// Setup one user with the first role, one with the second, one with both
// and one final without any of these two roles.
$account_1 = new User(array('uid' => 1), 'user');
$account_1->roles[$rid_1] = $rid_1;
$account_2 = new User(array('uid' => 2), 'user');
$account_2->roles[$rid_2] = $rid_2;
$account_12 = new User(array('uid' => 3), 'user');
$account_12->roles[$rid_1] = $rid_1;
$account_12->roles[$rid_2] = $rid_2;
$account_none = new User(array('uid' => 4), 'user');
// Setup expected values; specify which paths can be accessed by which user.
return array(
array('role_test_1', array($account_1, $account_12), array($account_2, $account_none)),
array('role_test_2', array($account_2, $account_12), array($account_1, $account_none)),
array('role_test_3', array($account_12), array($account_1, $account_2, $account_none)),
array('role_test_4', array($account_12), array($account_1, $account_2, $account_none)),
array('role_test_5', array($account_1, $account_2, $account_12), array()),
array('role_test_6', array($account_1, $account_2, $account_12), array()),
);
}
/**
* Tests role requirements on routes.
*
* @param string $path
* The path to check access for.
* @param array $grant_accounts
* A list of accounts which should have access to the given path.
* @param array $deny_accounts
* A list of accounts which should not have access to the given path.
*
* @see \Drupal\Tests\Core\Route\RouterRoleTest::getTestRouteCollection
* @see \Drupal\Tests\Core\Route\RouterRoleTest::roleAccessProvider
*
* @dataProvider roleAccessProvider
*/
public function testRoleAccess($path, $grant_accounts, $deny_accounts) {
$role_access_check = new RoleAccessCheck();
$collection = $this->getTestRouteCollection();
foreach ($grant_accounts as $account) {
// @todo Replace the global user with a properly injection session.
$GLOBALS['user'] = $account;
$subrequest = Request::create($path, 'GET');
$message = sprintf('Access granted for user with the roles %s on path: %s', implode(', ', $account->roles), $path);
$this->assertTrue($role_access_check->access($collection->get($path), $subrequest), $message);
}
// Check all users which don't have access.
foreach ($deny_accounts as $account) {
$GLOBALS['user'] = $account;
$subrequest = Request::create($path, 'GET');
$message = sprintf('Access denied for user %s with the roles %s on path: %s', $account->id(), implode(', ', $account->roles), $path);
$has_access = $role_access_check->access($collection->get($path), $subrequest);
$this->assertEmpty($has_access , $message);
}
}
}
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