diff --git a/core/modules/simpletest/tests/Drupal/simpletest/Tests/phpunit_error.xml b/core/modules/simpletest/tests/Drupal/simpletest/Tests/phpunit_error.xml index a8cf4d9..95bde5d 100644 --- a/core/modules/simpletest/tests/Drupal/simpletest/Tests/phpunit_error.xml +++ b/core/modules/simpletest/tests/Drupal/simpletest/Tests/phpunit_error.xml @@ -17,22 +17,22 @@ Undefined index: foo - - + + - 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)) + 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. - 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)) + 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. - Drupal\Tests\Core\Route\RouterRoleTest::testRoleAccess with data set #2 ('role_test_3', array(Drupal\user\Plugin\Core\Entity\User)) + 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. diff --git a/core/modules/user/lib/Drupal/user/Access/RoleAccessCheck.php b/core/modules/user/lib/Drupal/user/Access/RoleAccessCheck.php new file mode 100644 index 0000000..104763d --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Access/RoleAccessCheck.php @@ -0,0 +1,60 @@ +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; + } + +} diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml index e8c5e52..77f93e2 100644 --- a/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -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'] diff --git a/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTest.php b/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTest.php new file mode 100644 index 0000000..2561c55 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTest.php @@ -0,0 +1,157 @@ + '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', + ) + )); + $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', + ) + )); + + 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_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); + } + } + +}