diff --git a/core/includes/update.inc b/core/includes/update.inc index a5feb50..134e853 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -1597,3 +1597,35 @@ function update_add_cache_columns($table) { )); } } + +/** + * Replace permissions during update. + * + * This function can replace one permission to several or even delete an old + * one. + * + * @param array $replace + * An associative array. The keys are the old permissions the values are a + * list of new permissions. If the list is an empty array, the old permission + * is removed. + */ +function update_replace_permissions($replace) { + $prefix = 'user.role.'; + $cut = strlen($prefix); + $role_names = Drupal::service('config.storage')->listAll($prefix); + foreach ($role_names as $role_name) { + $rid = substr($role_name, $cut); + $config = Drupal::config("user.role.$rid"); + $permissions = $config->get('permissions'); + foreach ($replace as $old_permission => $new_permissions) { + $key = array_search($old_permission, $permissions); + if ($key !== FALSE) { + unset($permissions[$key]); + $permissions = array_merge($permissions, $new_permissions); + } + } + $config + ->set('permissions', $permissions) + ->save(); + } +} diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 7d848d6..54f744d 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1221,18 +1221,22 @@ function comment_node_update_index(EntityInterface $node, $langcode) { $index_comments = &drupal_static(__FUNCTION__); if ($index_comments === NULL) { - // Find and save roles that can 'access comments' or 'search content'. - $perms = array('access comments' => array(), 'search content' => array()); - $result = db_query("SELECT rid, permission FROM {role_permission} WHERE permission IN ('access comments', 'search content')"); - foreach ($result as $record) { - $perms[$record->permission][$record->rid] = $record->rid; - } - - // Prevent indexing of comments if there are any roles that can search but - // not view comments. + // Do not index in the following three cases: + // 1. 'Authenticated user' can search content but can't access comments. + // 2. 'Anonymous user' can search content but can't access comments. + // 3. Any role can search content but can't access comments and access + // comments is not granted by the 'authenticated user' role. In this case + // all users might have both permissions from various roles but it is also + // possible to set up a user to have only search content and so a user + // edit could change the security situation so it is not safe to index the + // comments. $index_comments = TRUE; - foreach ($perms['search content'] as $rid) { - if (!isset($perms['access comments'][$rid]) && (($rid == DRUPAL_AUTHENTICATED_RID || $rid == DRUPAL_ANONYMOUS_RID) || !isset($perms['access comments'][DRUPAL_AUTHENTICATED_RID]))) { + $roles = user_roles(); + $authenticated_can_access = in_array('access comments', $roles[DRUPAL_AUTHENTICATED_RID]->permissions); + foreach (user_roles() as $rid => $role_object) { + $permissions = $roles[$rid]->permissions; + if (in_array('search content', $permissions) && !in_array('access comments', $permissions) && + ($rid == DRUPAL_AUTHENTICATED_RID || $rid == DRUPAL_ANONYMOUS_RID || !$authenticated_can_access)) { $index_comments = FALSE; break; } diff --git a/core/modules/field_ui/field_ui.install b/core/modules/field_ui/field_ui.install index 8ce3e2c..25fec01 100644 --- a/core/modules/field_ui/field_ui.install +++ b/core/modules/field_ui/field_ui.install @@ -10,36 +10,27 @@ */ function field_ui_update_8001() { - $permissions = array( + $replace = array( 'administer comments' => array( + 'administer comments', 'administer comment fields', 'administer comment display', ), 'administer content types' => array( + 'administer content types', 'administer node fields', 'administer node display', ), 'administer users' => array( + 'administer users', 'administer user fields', 'administer user display', ), 'administer taxonomy' => array( + 'administer taxonomy', 'administer taxonomy_term fields', 'administer taxonomy_term display', ), ); - - // We can not call user_permission_get_modules() as that will start - // invoking hooks which we can't during update hooks. Directly query - // for the permissions and insert them into the database. - foreach ($permissions as $old_permission => $new_permissions) { - $results = db_query("SELECT rid FROM {role_permission} WHERE permission = :permission", array(':permission' => $old_permission)); - foreach ($results as $record) { - $query = db_insert('role_permission')->fields(array('rid', 'permission', 'module')); - foreach ($new_permissions as $new_permission) { - $query->values(array($record->rid, $new_permission, 'field_ui')); - } - $query->execute(); - } - } + update_replace_permissions($replace); } diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php index 85b5fef..f7f91d1 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php @@ -30,7 +30,7 @@ function setUp() { // filter_permission() calls into url() to output a link in the description. $this->installSchema('system', 'url_alias'); - $this->installSchema('user', array('users_roles', 'role_permission')); + $this->installSchema('user', array('users_roles')); // Install filter_test module, which ships with custom default format. $this->installConfig(array('user', 'filter_test')); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php index 73b3a44..c073b4b 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php @@ -26,9 +26,7 @@ public static function getInfo() { function setUp() { parent::setUp(); // Clear permissions for authenticated users. - db_delete('role_permission') - ->condition('rid', DRUPAL_AUTHENTICATED_RID) - ->execute(); + $this->container->get('config.factory')->get('user.role.' . DRUPAL_AUTHENTICATED_RID)->clear('permissions'); } /** diff --git a/core/modules/node/node.install b/core/modules/node/node.install index b0735d3..4366384 100644 --- a/core/modules/node/node.install +++ b/core/modules/node/node.install @@ -479,6 +479,15 @@ function _update_7000_node_get_types() { */ /** + * Implements hook_update_dependency(). + */ +function node_update_dependency() { + $dependencies['node'][8013] = array( + 'user' => 8002, + ); +} + +/** * Rename node type language variable names. * * @see http://drupal.org/node/540294 @@ -704,15 +713,11 @@ function node_update_8012() { * Renames global revision permissions to use the word 'all'. */ function node_update_8013() { - $actions = array('view', 'delete', 'revert'); - - foreach ($actions as $action) { - db_update('role_permission') - ->fields(array('permission' => $action . ' all revisions')) - ->condition('permission', $action . ' revisions') - ->condition('module', 'node') - ->execute(); - } + update_replace_permissions(array( + 'view revisions' => array('view all revisions'), + 'revert revisions' => array('revert all revisions'), + 'delete revisions' => array('delete all revisions'), + )); } /** diff --git a/core/modules/node/node.views_execution.inc b/core/modules/node/node.views_execution.inc index ead8702..f264bf2 100644 --- a/core/modules/node/node.views_execution.inc +++ b/core/modules/node/node.views_execution.inc @@ -30,17 +30,9 @@ function node_views_analyze(ViewExecutable $view) { // check for no access control $access = $display->getOption('access'); if (empty($access['type']) || $access['type'] == 'none') { - $result = db_select('role_permission', 'p') - ->fields('p', array('rid', 'permission')) - ->condition('p.rid', array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID), 'IN') - ->condition('p.permission', 'access content') - ->execute(); - - foreach ($result as $role) { - $role->safe = TRUE; - $roles[$role->rid] = $role; - } - if (!($roles[DRUPAL_ANONYMOUS_RID]->safe && $roles[DRUPAL_AUTHENTICATED_RID]->safe)) { + $anonymous_has_access = in_array('access content', entity_load('user_role', DRUPAL_ANONYMOUS_RID)->permissions); + $authenticated_has_access = in_array('access content', entity_load('user_role.', DRUPAL_AUTHENTICATED_RID)->permissions); + if (!$anonymous_has_access || !$authenticated_has_access) { $ret[] = Analyzer::formatMessage(t('Some roles lack permission to access content, but display %display has no access control.', array('%display' => $display->display['display_title'])), 'warning'); } $filters = $display->getOption('filters'); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 76988fa..753a616 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -551,8 +551,7 @@ protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NUL // Grant the specified permissions to the role, if any. if (!empty($permissions)) { user_role_grant_permissions($role->id(), $permissions); - - $assigned_permissions = db_query('SELECT permission FROM {role_permission} WHERE rid = :rid', array(':rid' => $role->id()))->fetchCol(); + $assigned_permissions = entity_load('user_role', $role->id())->permissions; $missing_permissions = array_diff($permissions, $assigned_permissions); if (!$missing_permissions) { $this->pass(t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role')); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php index c29e4f3..300c4f6 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php @@ -29,7 +29,7 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installSchema('user', array('role_permission', 'users_roles')); + $this->installSchema('user', array('users_roles')); $this->installSchema('system', array('variable', 'url_alias')); $this->installSchema('language', 'language'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php index 892fd18..27a45fb 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php @@ -31,7 +31,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_roles', 'users_data', 'role_permission')); + $this->installSchema('user', array('users_data')); $this->installSchema('node', array('node', 'node_revision', 'node_type', 'node_access')); $this->installSchema('comment', array('comment', 'node_comment_statistics')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php index 41918d3..1f65ff9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -36,8 +36,7 @@ public function setUp() { * (optional) The values used to create the entity. * @param array $permissions * (optional) Array of permission names to assign to user. The - * role_permission and users_roles tables must be installed before this can - * be used. + * users_roles tables must be installed before this can be used. * * @return \Drupal\user\Plugin\Core\Entity\User * The created user entity. diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php index 3345d2f..78cecbf 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php @@ -38,8 +38,7 @@ function testUserPermsUninstalled() { module_disable(array('module_test')); module_uninstall(array('module_test')); - // Are the perms defined by module_test removed from {role_permission}. - $count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField(); - $this->assertEqual(0, $count, 'Permissions were all removed.'); + // Are the perms defined by module_test removed? + $this->assertFalse(user_roles(FALSE, 'module_test perm'), 'Permissions were all removed.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php index 6b669a5..c800e6f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php @@ -184,6 +184,8 @@ public function testLanguageNoPluralsUpgrade() { * Tests upgrading translations permissions. */ public function testLanguagePermissionsUpgrade() { + // Insert a permission into the Drupal 7 database before running the + // upgrade. db_insert('role_permission')->fields(array( 'rid' => 2, 'permission' => 'translate content', @@ -191,12 +193,8 @@ public function testLanguagePermissionsUpgrade() { ))->execute(); $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); - - // Check that translate content role doesn't exist on database. - $old_permission_exists = db_query('SELECT * FROM {role_permission} WHERE permission LIKE ?', array('translate content'))->fetchObject(); - $this->assertFalse($old_permission_exists, 'No translate content role left on database.'); + $this->assertFalse(user_roles(FALSE, 'translate content'), 'No translate content role left in config.'); // Check that translate content has been renamed to translate all content. - $new_permission_exists = db_query('SELECT * FROM {role_permission} WHERE permission LIKE ?', array('translate all content'))->fetchObject(); - $this->assertTrue($new_permission_exists, 'Rename role translate content to translate all content was completed successfully.'); + $this->assertTrue(user_roles(FALSE, 'translate all content'), 'Rename role translate content to translate all content was completed successfully.'); } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 2a6f76c..b95f63c 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1521,13 +1521,12 @@ function system_update_8020() { $blocked_ips_exists = db_query_range('SELECT 1 FROM {blocked_ips}', 0, 1)->fetchField(); if ($blocked_ips_exists) { // Rename the permission name. - db_update('role_permission') - ->fields(array( - 'permission' => 'ban IP addresses', - 'module' => 'ban', - )) - ->condition('permission', 'block IP addresses') - ->execute(); + update_replace_permissions(array( + 'block IP addresses' => array('ban IP addresses'), + )); + config('user.module.permission') + ->set('ban.ban IP addresses', TRUE) + ->save(); // Rename {blocked_ips} table into {ban_ip}. db_rename_table('blocked_ips', 'ban_ip'); // Remove all references to the removed action callback. diff --git a/core/modules/translation/translation.install b/core/modules/translation/translation.install index 61d014d..ad15ba0 100644 --- a/core/modules/translation/translation.install +++ b/core/modules/translation/translation.install @@ -10,10 +10,9 @@ * Rename the translate content permission. */ function translation_update_8000() { - db_update('role_permission') - ->fields(array('permission' => 'translate all content')) - ->condition('permission', 'translate content') - ->execute(); + update_replace_permissions(array( + 'translate content' => array('translate all content'), + )); } /** diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php b/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php index 7aa22fe..cbd5cc2 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php +++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php @@ -40,32 +40,31 @@ function pre_render(&$values) { $this->items = array(); foreach ($values as $result) { - $uids[] = $this->get_value($result); + $uids[] = $this->get_value($result, 'uid'); } if ($uids) { - // Get a list of all the modules implementing a hook_permission() and sort by - // display name. - $module_info = system_get_info('module'); - $modules = array(); - foreach (module_implements('permission') as $module) { - $modules[$module] = $module_info[$module]['name']; - } - asort($modules); - - $permissions = module_invoke_all('permission'); - - $query = db_select('role_permission', 'rp'); - $query->join('users_roles', 'u', 'u.rid = rp.rid'); + $permission_names = \Drupal::moduleHandler()->invokeAll('permission'); + $query = db_select('users_roles', 'u'); $query->fields('u', array('uid', 'rid')); - $query->addField('rp', 'permission'); $query->condition('u.uid', $uids); - $query->condition('rp.module', array_keys($modules)); - $query->orderBy('rp.permission'); $result = $query->execute(); - - foreach ($result as $perm) { - $this->items[$perm->uid][$perm->permission]['permission'] = $permissions[$perm->permission]['title']; + $rids = array(); + foreach ($result as $row) { + $rids[] = $row->rid; + } + if ($rids) { + $roles = entity_load_multiple('user_role', $rids); + foreach ($result as $row) { + foreach ($roles[$row->rid]->permissions as $permission) { + $this->items[$row->uid][$permission]['permission'] = $permission_names[$permission]['title']; + } + } + } + foreach ($uids as $uid) { + if (isset($this->items[$uid])) { + ksort($this->items[$uid]); + } } } } diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php b/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php index 54f24f8..62db56b 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php +++ b/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php @@ -41,4 +41,25 @@ function get_value_options() { } } + /** + * {@inheritdoc} + * + * Replace the configured permission with a filter by all roles that have this + * permission. + */ + public function query() { + // @todo user_role_names() should maybe support multiple permissions. + $rids = array(); + // Get all roles, that have the configured permissions. + foreach ($this->value as $permission) { + $roles = user_role_names(FALSE, $permission); + $rids += array_keys($roles); + } + $rids = array_unique($rids); + $this->value = $rids; + + // $this->value contains the role IDs that have the configured permission. + parent::query(); + } + } diff --git a/core/modules/user/lib/Drupal/user/RoleStorageController.php b/core/modules/user/lib/Drupal/user/RoleStorageController.php index 680b412..5a42ca8 100644 --- a/core/modules/user/lib/Drupal/user/RoleStorageController.php +++ b/core/modules/user/lib/Drupal/user/RoleStorageController.php @@ -34,25 +34,30 @@ public function preSave(EntityInterface $entity) { */ public function resetCache(array $ids = NULL) { parent::resetCache($ids); - // Clear the user access cache. drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); + $prefix = 'user.permission.'; + if ($ids) { + $names = array(); + foreach ($ids as $id) { + $names[] = $prefix . $id; + } + } + else { + $names = \Drupal::service('config.storage')->listAll($prefix); + } + foreach ($names as $name) { + config($name)->init(); + } } /** * {@inheritdoc} */ protected function postDelete($entities) { - $rids = array_keys($entities); - - // Delete permission assignments. - db_delete('role_permission') - ->condition('rid', $rids) - ->execute(); // Remove the role from all users. db_delete('users_roles') - ->condition('rid', $rids) + ->condition('rid', array_keys($entities)) ->execute(); } diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php index 2455b10..348bb87 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php @@ -8,6 +8,7 @@ namespace Drupal\user\Tests; use Drupal\simpletest\WebTestBase; +use Drupal\user\RoleStorageController; class UserPermissionsTest extends WebTestBase { protected $admin_user; @@ -46,8 +47,8 @@ function testUserPermissionChanges() { $edit[$rid . '[administer nodes]'] = TRUE; $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.'); - drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); + $storage_controller = $this->container->get('plugin.manager.entity')->getStorageController('user_role'); + $storage_controller->resetCache(); $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.'); // Remove a permission. @@ -56,8 +57,7 @@ function testUserPermissionChanges() { $edit[$rid . '[access user profiles]'] = FALSE; $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.'); - drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); + $storage_controller->resetCache(); $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.'); } diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFieldPermissionTest.php b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFieldPermissionTest.php new file mode 100644 index 0000000..ae84e4b --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFieldPermissionTest.php @@ -0,0 +1,66 @@ + 'User: Permissions Field', + 'description' => 'Tests the permission field handler.', + 'group' => 'Views module integration', + ); + } + + /** + * Tests the permission field handler output. + */ + public function testFieldPermission() { + $this->setupPermissionTestData(); + + $view = views_get_view('test_field_permission'); + $this->executeView($view); + $view->initStyle(); + $view->render(); + $style_plugin = $view->style_plugin; + + $expected_permissions = array(); + $expected_permissions[$this->users[0]->id()] = array(); + $expected_permissions[$this->users[1]->id()] = array(); + $expected_permissions[$this->users[2]->id()][] = t('Administer permissions'); + // View user profiles comes first, because we sort by the permission + // machine name. + $expected_permissions[$this->users[3]->id()][] = t('View user profiles'); + $expected_permissions[$this->users[3]->id()][] = t('Administer permissions'); + $expected_permissions[$this->users[3]->id()][] = t('Administer users'); + + foreach ($view->result as $index => $row) { + $uid = $view->field['uid']->get_value($row); + $rendered_permission = $style_plugin->get_field($index, 'permission'); + + $expected_output = implode(', ', $expected_permissions[$uid]); + $this->assertEqual($rendered_permission, $expected_output, 'The right permissions are rendered.'); + } + } + +} diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFilterPermissionTest.php b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFilterPermissionTest.php new file mode 100644 index 0000000..c8ca5d7 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFilterPermissionTest.php @@ -0,0 +1,131 @@ + 'User: Permissions Filter', + 'description' => 'Tests the permission filter handler.', + 'group' => 'Views module integration', + ); + } + + /** + * Tests the permission filter handler. + * + * @todo Fix the different commented out tests by fixing the many to one + * handler handling with the NOT operator. + */ + public function testFilterPermission() { + $this->setupPermissionTestData(); + + $column_map = array('uid' => 'uid'); + $view = views_get_view('test_filter_permission'); + + // Filter by a non existing permission. + $view->initHandlers(); + $view->filter['permission']->value = array('non_existent_permission'); + $this->executeView($view); + $this->assertEqual(count($view->result), 4, 'A non existent permission is not filtered so everything is the result.'); + $expected[] = array('uid' => 1); + $expected[] = array('uid' => 2); + $expected[] = array('uid' => 3); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by a non existing permission with operator NOT. + // $view->initHandlers(); + // $view->filter['permission']->value = array('Non existent permission'); + // $view->filter['permission']->operator = 'not'; + // $this->executeView($view); + // $this->assertEqual(count($view->result), count($this->users), 'A non existent permission with the NOt operator should result in every user'); + // $expected = array(); + // $expected[] = array('uid' => 1); + // $expected[] = array('uid' => 2); + // $expected[] = array('uid' => 3); + // $expected[] = array('uid' => 4); + // $this->assertIdenticalResultset($view, $expected, $column_map); + // $view->destroy(); + + // Filter by a permission. + $view->initHandlers(); + $view->filter['permission']->value = array('administer permissions'); + $this->executeView($view); + $this->assertEqual(count($view->result), 2); + $expected = array(); + $expected[] = array('uid' => 3); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by a permission with operator NOT. + // $view->initHandlers(); + // $view->filter['permission']->value = array('administer permissions'); + // $view->filter['permission']->operator = 'not'; + // $this->executeView($view); + // $this->assertEqual(count($view->result), 2); + // $expected = array(); + // $expected[] = array('uid' => 1); + // $expected[] = array('uid' => 2); + // $this->assertIdenticalResultset($view, $expected, $column_map); + // $view->destroy(); + + // Filter by another permission of a role with multiple permissions. + $view->initHandlers(); + $view->filter['permission']->value = array('administer users'); + $this->executeView($view); + $this->assertEqual(count($view->result), 1); + $expected = array(); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by another permission of a role with multiple permissions and + // operator NOT. + // $view->initHandlers(); + // $view->filter['permission']->value = array('administer users'); + // $view->filter['permission']->operator = 'not'; + // $this->executeView($view); + // $this->assertEqual(count($view->result), 3); + // $expected = array(); + // $expected[] = array('uid' => 1); + // $expected[] = array('uid' => 2); + // $expected[] = array('uid' => 3); + // $this->assertIdenticalResultset($view, $expected, $column_map); + // $view->destroy(); + + $view->initDisplay(); + $view->initHandlers(); + $view->filter['permission']->get_value_options(); + + $expected = array_map(function ($permission) { + return check_plain(strip_tags($permission['title'])); + }, $this->container->get('module_handler')->invokeAll('permission')); + $this->assertEqual($view->filter['permission']->value_options, $expected, 'Ensure the all permissions are available'); + } + +} diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php new file mode 100644 index 0000000..0071f12 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php @@ -0,0 +1,90 @@ +installSchema('user', array('users', 'users_roles')); + $this->installSchema('system', 'sequences'); + + $entity_manager = $this->container->get('plugin.manager.entity'); + $this->roleStorageController = $entity_manager->getStorageController('user_role'); + $this->userStorageController = $entity_manager->getStorageController('user'); + } + + /** + * Set some test data for permission related tests. + */ + protected function setupPermissionTestData() { + // Setup a role without any permission. + $this->roleStorageController->create(array('id' => 'no_permission')) + ->save(); + // Setup a role with just one permission. + $this->roleStorageController->create(array('id' => 'one_permission')) + ->save(); + user_role_grant_permissions('one_permission', array('administer permissions')); + // Setup a role with multiple permissions. + $this->roleStorageController->create(array('id' => 'multiple_permissions')) + ->save(); + user_role_grant_permissions('multiple_permissions', array('administer permissions', 'administer users', 'access user profiles')); + + // Setup a user without an extra role. + $this->users[] = $account = $this->userStorageController->create(array()); + $account->save(); + // Setup a user with just the first role (so no permission beside the + // ones from the authenticated role). + $this->users[] = $account = $this->userStorageController->create(array('name' => 'first_role', 'roles' => array('no_permission' => 'no_permission'))); + $account->save(); + // Setup a user with just the second role (so one additional permission). + $this->users[] = $account = $this->userStorageController->create(array('name' => 'second_role', 'roles' => array('one_permission' => 'one_permission'))); + $account->save(); + // Setup a user with both the second and the third role. + $this->users[] = $account = $this->userStorageController->create(array('name' => 'second_third_role', 'roles' => array('one_permission' => 'one_permission', 'multiple_permissions' => 'multiple_permissions'))); + $account->save(); + } + +} diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_field_permission.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_field_permission.yml new file mode 100644 index 0000000..94f587d --- /dev/null +++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_field_permission.yml @@ -0,0 +1,135 @@ +base_field: uid +base_table: users +core: 8.x +description: '' +status: '1' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' + display_options: + access: + type: none + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + fields: + uid: + id: uid + table: users + field: uid + relationship: none + group_type: group + admin_label: '' + label: Uid + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + link_to_user: '0' + plugin_id: user + permission: + id: permission + table: users_roles + field: permission + relationship: none + group_type: group + admin_label: '' + label: Permission + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + type: separator + separator: ', ' + plugin_id: user_permissions + filters: { } + sorts: { } +human_name: test_field_permission +module: views +id: test_field_permission +tag: '' +langcode: en diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml new file mode 100644 index 0000000..ef1fd1c --- /dev/null +++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml @@ -0,0 +1,123 @@ +base_field: uid +base_table: users +core: 8.x +description: '' +status: '1' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' + display_options: + access: + type: none + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + fields: + uid: + id: uid + table: users + field: uid + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '0' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + link_to_user: '0' + plugin_id: user + filters: + permission: + id: permission + table: users_roles + field: permission + relationship: none + group_type: group + admin_label: '' + operator: or + value: + 'access user profiles': 'access user profiles' + group: '1' + exposed: '0' + expose: + operator_id: '0' + label: '' + description: '' + use_operator: '0' + operator: '' + identifier: '' + required: '0' + remember: '0' + multiple: '0' + remember_roles: + authenticated: authenticated + reduce: '0' + is_grouped: '0' + group_info: + label: '' + description: '' + identifier: '' + optional: '1' + widget: select + multiple: '0' + remember: '0' + default_group: All + default_group_multiple: { } + group_items: { } + reduce_duplicates: '1' + plugin_id: user_permissions + sorts: { } +human_name: test_filter_permission +module: views +id: test_filter_permission +tag: '' +langcode: en diff --git a/core/modules/user/user.install b/core/modules/user/user.install index dca417c..b57d760 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -148,42 +148,6 @@ function user_schema() { ), ); - $schema['role_permission'] = array( - 'description' => 'Stores the permissions assigned to user roles.', - 'fields' => array( - 'rid' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'description' => 'Foreign Key: {role}.rid.', - ), - 'permission' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'A single permission granted to the role identified by rid.', - ), - 'module' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => "The module declaring the permission.", - ), - ), - 'primary key' => array('rid', 'permission'), - 'indexes' => array( - 'permission' => array('permission'), - ), - 'foreign keys' => array( - 'role' => array( - 'table' => 'role', - 'columns' => array('rid' => 'rid'), - ), - ), - ); - $schema['users_data'] = array( 'description' => 'Stores module data as key/value pairs per user.', 'fields' => array( @@ -437,9 +401,6 @@ function user_update_8002() { ); db_change_field('role', 'rid', 'rid', $column); - $column['description'] = 'Foreign Key: {role}.rid.'; - db_change_field('role_permission', 'rid', 'rid', $column); - $column['description'] = 'Primary Key: {role}.rid for role.'; db_change_field('users_roles', 'rid', 'rid', $column); @@ -456,32 +417,16 @@ function user_update_8002() { db_drop_unique_key('role', 'name'); // Rename the built-in serial role IDs into the hardcoded machine names. - db_update('role') - ->fields(array('rid' => DRUPAL_ANONYMOUS_RID)) - ->condition('rid', 1) - ->execute(); - db_update('role') - ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID)) - ->condition('rid', 2) - ->execute(); - - db_update('role_permission') - ->fields(array('rid' => DRUPAL_ANONYMOUS_RID)) - ->condition('rid', 1) - ->execute(); - db_update('role_permission') - ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID)) - ->condition('rid', 2) - ->execute(); - - db_update('users_roles') - ->fields(array('rid' => DRUPAL_ANONYMOUS_RID)) - ->condition('rid', 1) - ->execute(); - db_update('users_roles') - ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID)) - ->condition('rid', 2) - ->execute(); + foreach (array(1, 2) as $rid) { + db_update('role') + ->fields(array('rid' => _user_update_map_rid($rid))) + ->condition('rid', $rid) + ->execute(); + db_update('users_roles') + ->fields(array('rid' => _user_update_map_rid($rid))) + ->condition('rid', $rid) + ->execute(); + } } /** @@ -1051,5 +996,43 @@ function user_update_8017() { } /** + * Maps a role id to the new string. + * + * @param int $rid + * A D7 numeric role ID. + * + * @return string + * A D8 string role ID. + */ +function _user_update_map_rid($rid) { + $rid_map = array( + 1 => DRUPAL_ANONYMOUS_RID, + 2 => DRUPAL_AUTHENTICATED_RID, + ); + return isset($rid_map[$rid]) ? $rid_map[$rid] : $rid; +} + +/** + * Migrate roles permissions into configuration. + * + * @ingroup config_upgrade + */ +function user_update_8018() { + $db_permissions = db_select('role_permission', 'p') + ->fields('p') + ->execute() + ->fetchAll(); + $new_permissions = array(); + foreach ($db_permissions as $permission) { + $new_permissions[_user_update_map_rid($permission->rid)][] = $permission->permission; + } + foreach ($new_permissions as $rid => $permissions) { + config("user.role.$rid") + ->set('permissions', $permissions) + ->save(); + } +} + +/** * @} End of "addtogroup updates-7.x-to-8.x". */ diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 3ae6154..f0bfa6b 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -414,41 +414,19 @@ function user_password($length = 10) { /** * Determine the permissions for one or more roles. * - * @param $roles + * @param array $roles * An array whose keys are the role IDs of interest, such as $user->roles. * - * @return + * @return array * An array indexed by role ID. Each value is an array whose keys are the * permission strings for the given role ID. */ -function user_role_permissions($roles) { - $cache = &drupal_static(__FUNCTION__, array()); - - $role_permissions = $fetch = array(); - +function user_role_permissions(array $roles) { + $role_permissions = array(); foreach ($roles as $rid => $name) { - if (isset($cache[$rid])) { - $role_permissions[$rid] = $cache[$rid]; - } - else { - // Add this rid to the list of those needing to be fetched. - $fetch[] = $rid; - // Prepare in case no permissions are returned. - $cache[$rid] = array(); - } - } - - if ($fetch) { - // Get from the database permissions that were not in the static variable. - // Only role IDs with at least one permission assigned will return rows. - $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch)); - - foreach ($result as $row) { - $cache[$row->rid][$row->permission] = TRUE; - } - foreach ($fetch as $rid) { - // For every rid, we know we at least assigned an empty array. - $role_permissions[$rid] = $cache[$rid]; + $role_permissions[$rid] = array(); + foreach (Drupal::config("user.role.$rid")->get('permissions') as $permission) { + $role_permissions[$rid][$permission] = TRUE; } } @@ -1870,12 +1848,11 @@ function user_roles($membersonly = FALSE, $permission = NULL) { } if (!empty($permission)) { - $result = db_select('role_permission', 'p') - ->fields('p', array('rid')) - ->condition('p.rid', array_keys($roles)) - ->condition('p.permission', $permission) - ->execute()->fetchCol(); - $roles = array_intersect_key($roles, array_flip($result)); + foreach ($roles as $rid => $role_object) { + if (!in_array($permission, $role_object->permissions)) { + unset($roles[$rid]); + } + } } if (empty($permission)) { @@ -1982,23 +1959,20 @@ function user_role_change_permissions($rid, array $permissions = array()) { * @see user_role_revoke_permissions() */ function user_role_grant_permissions($rid, array $permissions = array()) { - $modules = user_permission_get_modules(); // Grant new permissions for the role. - foreach ($permissions as $name) { - db_merge('role_permission') - ->key(array( - 'rid' => $rid, - 'permission' => $name, - )) - ->fields(array( - 'module' => $modules[$name], - )) - ->execute(); + $role_entity = entity_load('user_role', $rid); + $role_entity->permissions = array_unique(array_merge($role_entity->permissions, $permissions)); + $role_entity->save(); + + $modules = user_permission_get_modules(); + $config = Drupal::config('user.module.permission'); + foreach ($modules as $permission => $module) { + $config->set("$module.$permission", TRUE); } + $config->save(); // Clear the user access cache. drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); } /** @@ -2014,14 +1988,11 @@ function user_role_grant_permissions($rid, array $permissions = array()) { */ function user_role_revoke_permissions($rid, array $permissions = array()) { // Revoke permissions for the role. - db_delete('role_permission') - ->condition('rid', $rid) - ->condition('permission', $permissions, 'IN') - ->execute(); - + $role_entity = entity_load('user_role', $rid); + $role_entity->permissions = array_diff($role_entity->permissions, $permissions); + $role_entity->save(); // Clear the user access cache. drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); } /** @@ -2317,17 +2288,13 @@ function user_build_filter_query(SelectInterface $query) { // the authenticated role. If so, then all users would be listed, and we can // skip adding it to the filter query. if ($key == 'permission') { - $account = entity_create('user', array()); - $account->uid = 'user_filter'; - $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1); - if (user_access($value, $account)) { + $roles = user_roles(FALSE, $value); + if (isset($roles[DRUPAL_AUTHENTICATED_RID])) { continue; } - $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); - $permission_alias = $query->join('role_permission', 'p', $users_roles_alias . '.rid = %alias.rid'); - $query->condition($permission_alias . '.permission', $value); + $value = array_keys($roles); } - elseif ($key == 'role') { + if ($key == 'permission' || $key == 'role') { $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); $query->condition($users_roles_alias . '.rid' , $value); } @@ -2635,11 +2602,25 @@ function user_modules_installed($modules) { /** * Implements hook_modules_uninstalled(). + * + * Remove all permissions from the permission configurations for the uninstalled + * module. */ function user_modules_uninstalled($modules) { - db_delete('role_permission') - ->condition('module', $modules, 'IN') - ->execute(); + $config = Drupal::config('user.module.permission'); + $permissions_to_remove = array(); + foreach ($modules as $module) { + $permissions_to_remove += (array) $config->get($module); + $config->clear($module); + } + $config->save(); + if ($permissions_to_remove) { + $permissions_to_remove = array_keys($permissions_to_remove); + foreach (user_roles() as $rid => $role_entity) { + $role_entity->permissions = array_diff($role_entity->permissions, $permissions_to_remove); + $role_entity->save(); + } + } // Remove any potentially orphan module data stored for users. drupal_container()->get('user.data')->delete($modules); } diff --git a/core/modules/user/user.views.inc b/core/modules/user/user.views.inc index 8a27f82..0a62d83 100644 --- a/core/modules/user/user.views.inc +++ b/core/modules/user/user.views.inc @@ -351,19 +351,7 @@ function user_views_data() { ), ); - // Define the base group of this table. Fields that don't have a group defined - // will go into this field by default. - $data['role_permission']['table']['group'] = t('User'); - // Explain how this table joins to others. - $data['role_permission']['table']['join'] = array( - 'users' => array( - 'left_table' => 'users_roles', - 'left_field' => 'rid', - 'field' => 'rid', - ), - ); - - $data['role_permission']['permission'] = array( + $data['users_roles']['permission'] = array( 'title' => t('Permission'), 'help' => t('The user permissions.'), 'field' => array( @@ -372,6 +360,7 @@ function user_views_data() { ), 'filter' => array( 'id' => 'user_permissions', + 'real field' => 'rid', ), ); diff --git a/core/modules/views/lib/Drupal/views/ManyToOneHelper.php b/core/modules/views/lib/Drupal/views/ManyToOneHelper.php index 1209901..1c38e3b 100644 --- a/core/modules/views/lib/Drupal/views/ManyToOneHelper.php +++ b/core/modules/views/lib/Drupal/views/ManyToOneHelper.php @@ -309,7 +309,7 @@ function add_filter() { else { $placeholder = $this->placeholder(); if (count($this->handler->value) > 1) { - $this->query->add_where_expression(0, "$field $operator($placeholder)", array($placeholder => $value)); + $this->handler->query->add_where_expression(0, "$field $operator($placeholder)", array($placeholder => $value)); } else { $this->handler->query->add_where_expression(0, "$field $operator $placeholder", array($placeholder => $value)); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php index 721b3c5..72af9bd 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php @@ -36,11 +36,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - $this->installSchema('user', 'role_permission'); - } - function testSimple() { $view = views_get_view('test_view'); $view->setDisplay(); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php index d5f2eac..643b683 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php @@ -38,11 +38,6 @@ public static function getInfo() { ); } - public function setUp() { - parent::setUp(); - $this->installSchema('user', 'role_permission'); - } - /** * Overrides Drupal\views\Tests\ViewTestBase::viewsData(). */ diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php index 172678d..95a543f 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php @@ -26,7 +26,7 @@ * Overrides \Drupal\views\Tests\ViewUnitTestBase::setUpFixtures(). */ protected function setUpFixtures() { - $this->installSchema('user', array('users', 'users_roles', 'role_permission')); + $this->installSchema('user', array('users', 'users_roles')); $this->installConfig(array('user')); parent::setUpFixtures(); diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php index 41e5c8b..8dd67ee 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php @@ -80,7 +80,7 @@ public static function getInfo() { } protected function setUpFixtures() { - $this->installSchema('user', array('users', 'role_permission')); + $this->installSchema('user', array('users')); $this->installSchema('node', array('node_type', 'node')); $this->installSchema('comment', array('comment', 'node_comment_statistics')); parent::setUpFixtures();