diff --git a/core/core.services.yml b/core/core.services.yml index 4f1b5ab35c..b253430d70 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1527,7 +1527,7 @@ services: arguments: ['@current_user', '@session_handler.write_safe'] user_permissions_hash_generator: class: Drupal\Core\Session\PermissionsHashGenerator - arguments: ['@private_key', '@cache.bootstrap', '@cache.static'] + arguments: ['@private_key', '@cache.bootstrap', '@cache.static', '@entity_type.manager'] current_user: class: Drupal\Core\Session\AccountProxy arguments: ['@event_dispatcher'] diff --git a/core/lib/Drupal/Core/Cache/Context/IsSuperUserCacheContext.php b/core/lib/Drupal/Core/Cache/Context/IsSuperUserCacheContext.php index c6c489427c..3f2bfbd798 100644 --- a/core/lib/Drupal/Core/Cache/Context/IsSuperUserCacheContext.php +++ b/core/lib/Drupal/Core/Cache/Context/IsSuperUserCacheContext.php @@ -3,18 +3,32 @@ namespace Drupal\Core\Cache\Context; use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Session\AccountInterface; /** * Defines the IsSuperUserCacheContext service, for "super user or not" caching. * * Cache context ID: 'user.is_super_user'. + * + * @deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. See + * https://www.drupal.org/node/2910500 for more information. + * @see https://www.drupal.org/node/2910500 */ class IsSuperUserCacheContext extends UserCacheContextBase implements CacheContextInterface { + /** + * {@inheritdoc} + */ + public function __construct(AccountInterface $user) { + @trigger_error('\Drupal\Core\Cache\Context\IsSuperUserCacheContext is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.', E_USER_DEPRECATED); + parent::__construct($user); + } + /** * {@inheritdoc} */ public static function getLabel() { + @trigger_error('Calling ' . __METHOD__ . '() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.', E_USER_DEPRECATED); return t('Is super user'); } @@ -22,13 +36,17 @@ public static function getLabel() { * {@inheritdoc} */ public function getContext() { - return ((int) $this->user->id()) === 1 ? '1' : '0'; + @trigger_error('Calling ' . __METHOD__ . '() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.', E_USER_DEPRECATED); + // Always return 0 as there is no longer a super user. See: + // https://www.drupal.org/node/540008 + return '0'; } /** * {@inheritdoc} */ public function getCacheableMetadata() { + @trigger_error('Calling ' . __METHOD__ . '() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.', E_USER_DEPRECATED); return new CacheableMetadata(); } diff --git a/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php b/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php index 0cd0fbe1a5..0a7f909991 100644 --- a/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php +++ b/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php @@ -27,13 +27,6 @@ public static function getLabel() { * {@inheritdoc} */ public function getContext($role = NULL) { - // User 1 does not actually have any special behavior for roles; this is - // added as additional security and backwards compatibility protection for - // SA-CORE-2015-002. - // @todo Remove in Drupal 9.0.0. - if ($this->user->id() == 1) { - return 'is-super-user'; - } if ($role === NULL) { return implode(',', $this->user->getRoles()); } diff --git a/core/lib/Drupal/Core/Session/AccountInterface.php b/core/lib/Drupal/Core/Session/AccountInterface.php index 6fdd60356a..7278b2f6e8 100644 --- a/core/lib/Drupal/Core/Session/AccountInterface.php +++ b/core/lib/Drupal/Core/Session/AccountInterface.php @@ -22,6 +22,11 @@ interface AccountInterface { */ const AUTHENTICATED_ROLE = 'authenticated'; + /** + * Role ID for the administrator users. + */ + const ADMINISTRATOR_ROLE = 'administrator'; + /** * Returns the user ID or 0 for anonymous. * diff --git a/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php b/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php index e17253782f..60e40ab430 100644 --- a/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php +++ b/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php @@ -5,6 +5,7 @@ use Drupal\Core\PrivateKey; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Site\Settings; /** @@ -33,6 +34,13 @@ class PermissionsHashGenerator implements PermissionsHashGeneratorInterface { */ protected $static; + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + /** * Constructs a PermissionsHashGenerator object. * @@ -42,11 +50,14 @@ class PermissionsHashGenerator implements PermissionsHashGeneratorInterface { * The cache backend interface to use for the persistent cache. * @param \Drupal\Core\Cache\CacheBackendInterface $static * The cache backend interface to use for the static cache. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager used to retrieve a storage handler from. */ - public function __construct(PrivateKey $private_key, CacheBackendInterface $cache, CacheBackendInterface $static) { + public function __construct(PrivateKey $private_key, CacheBackendInterface $cache, CacheBackendInterface $static, EntityTypeManagerInterface $entity_type_manager) { $this->privateKey = $private_key; $this->cache = $cache; $this->static = $static; + $this->entityTypeManager = $entity_type_manager; } /** @@ -55,10 +66,14 @@ public function __construct(PrivateKey $private_key, CacheBackendInterface $cach * Cached by role, invalidated whenever permissions change. */ public function generate(AccountInterface $account) { - // User 1 is the super user, and can always access all permissions. Use a - // different, unique identifier for the hash. - if ($account->id() == 1) { - return $this->hash('is-super-user'); + // Admin roles have all permissions implicitly assigned. Use a different, + // unique identifier for the hash. + $storage = $this->entityTypeManager->getStorage('user_role'); + foreach ($storage->loadMultiple($account->getRoles()) as $role) { + /** @var \Drupal\user\RoleInterface $role */ + if ($role->isAdmin()) { + return $this->hash('is-admin'); + } } $sorted_roles = $account->getRoles(); diff --git a/core/lib/Drupal/Core/Session/UserSession.php b/core/lib/Drupal/Core/Session/UserSession.php index 5ed8e4c724..7ccac6aaf5 100644 --- a/core/lib/Drupal/Core/Session/UserSession.php +++ b/core/lib/Drupal/Core/Session/UserSession.php @@ -103,11 +103,6 @@ public function getRoles($exclude_locked_roles = FALSE) { * {@inheritdoc} */ public function hasPermission($permission) { - // User #1 has all privileges. - if ((int) $this->id() === 1) { - return TRUE; - } - return $this->getRoleStorage()->isPermissionInRoles($permission, $this->getRoles()); } diff --git a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php index cd89d586a6..2d4ca5e62b 100644 --- a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php +++ b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php @@ -14,6 +14,7 @@ use Drupal\Core\Extension\MissingDependencyException; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Serialization\Yaml; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\UserSession; use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\StreamWrapperInterface; @@ -359,6 +360,7 @@ protected function initUserSession() { 'pass_raw' => $password, 'passRaw' => $password, 'timezone' => date_default_timezone_get(), + 'roles' => [AccountInterface::AUTHENTICATED_ROLE, AccountInterface::ADMINISTRATOR_ROLE], ]); // The child site derives its session name from the database prefix when diff --git a/core/modules/comment/tests/src/Kernel/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/tests/src/Kernel/CommentDefaultFormatterCacheTagsTest.php index fc9bcda2fe..81900d27ff 100644 --- a/core/modules/comment/tests/src/Kernel/CommentDefaultFormatterCacheTagsTest.php +++ b/core/modules/comment/tests/src/Kernel/CommentDefaultFormatterCacheTagsTest.php @@ -34,11 +34,6 @@ class CommentDefaultFormatterCacheTagsTest extends EntityKernelTestBase { protected function setUp(): void { parent::setUp(); - // Create user 1 so that the user created later in the test has a different - // user ID. - // @todo Remove in https://www.drupal.org/node/540008. - $this->createUser(['uid' => 1, 'name' => 'user1'])->save(); - $this->container->get('module_handler')->loadInclude('comment', 'install'); comment_install(); diff --git a/core/modules/comment/tests/src/Kernel/Views/CommentAdminViewTest.php b/core/modules/comment/tests/src/Kernel/Views/CommentAdminViewTest.php index cdc3cd9c60..cf1c2895fe 100644 --- a/core/modules/comment/tests/src/Kernel/Views/CommentAdminViewTest.php +++ b/core/modules/comment/tests/src/Kernel/Views/CommentAdminViewTest.php @@ -54,11 +54,6 @@ protected function setUp($import_test_views = TRUE): void { // Create the anonymous role. $this->installConfig(['user']); - // Create user 1 so that the user created later in the test has a different - // user ID. - // @todo Remove in https://www.drupal.org/node/540008. - User::create(['uid' => 1, 'name' => 'user1'])->save(); - // Enable another language. ConfigurableLanguage::createFromLangcode('ur')->save(); // Rebuild the container to update the default language container variable. diff --git a/core/modules/comment/tests/src/Kernel/Views/CommentViewsKernelTestBase.php b/core/modules/comment/tests/src/Kernel/Views/CommentViewsKernelTestBase.php index 727a6803c8..f52b9df2a2 100644 --- a/core/modules/comment/tests/src/Kernel/Views/CommentViewsKernelTestBase.php +++ b/core/modules/comment/tests/src/Kernel/Views/CommentViewsKernelTestBase.php @@ -61,11 +61,6 @@ protected function setUp($import_test_views = TRUE) { ]) ->save(); - // Create user 1 so that the user created later in the test has a different - // user ID. - // @todo Remove in https://www.drupal.org/node/540008. - $this->userStorage->create(['uid' => 1, 'name' => 'user1'])->save(); - $admin_role = Role::create(['id' => 'admin']); $admin_role->grantPermission('administer comments'); $admin_role->grantPermission('access comments'); diff --git a/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php b/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php index 0c4c504ecf..505e3532c5 100644 --- a/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php @@ -51,7 +51,7 @@ protected function setUp(): void { $this->installConfig('content_moderation'); $this->installSchema('system', ['sequences']); - $this->adminUser = $this->createUser(array_keys($this->container->get('user.permissions')->getPermissions())); + $this->adminUser = $this->createUser(array_keys($this->container->get('user.permissions')->getPermissions()), 'admin_user', TRUE); } /** diff --git a/core/modules/file/tests/src/Kernel/FileManagedUnitTestBase.php b/core/modules/file/tests/src/Kernel/FileManagedUnitTestBase.php index a487566aa6..b5f9c7c13d 100644 --- a/core/modules/file/tests/src/Kernel/FileManagedUnitTestBase.php +++ b/core/modules/file/tests/src/Kernel/FileManagedUnitTestBase.php @@ -33,7 +33,7 @@ protected function setUp() { // Make sure that a user with uid 1 exists, self::createFile() relies on // it. - $user = User::create(['uid' => 1, 'name' => $this->randomMachineName()]); + $user = User::create(['uid' => 1, 'status' => 1, 'name' => $this->randomMachineName()]); $user->enforceIsNew(); $user->save(); \Drupal::currentUser()->setAccount($user); diff --git a/core/modules/filter/tests/src/Kernel/FilterDefaultConfigTest.php b/core/modules/filter/tests/src/Kernel/FilterDefaultConfigTest.php index 2fa3511d9c..c01b0782eb 100644 --- a/core/modules/filter/tests/src/Kernel/FilterDefaultConfigTest.php +++ b/core/modules/filter/tests/src/Kernel/FilterDefaultConfigTest.php @@ -47,6 +47,7 @@ public function testInstallation() { $this->assertEqual(array_keys(filter_get_roles_by_format($format)), [ RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID, + RoleInterface::ADMINISTRATOR_ID, ]); // Verify enabled filters. @@ -76,6 +77,7 @@ public function testUpdateRoles() { $this->assertEqual(array_keys(filter_get_roles_by_format($format)), [ RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID, + RoleInterface::ADMINISTRATOR_ID, ]); // Attempt to change roles. @@ -89,6 +91,7 @@ public function testUpdateRoles() { $this->assertEqual(array_keys(filter_get_roles_by_format($format)), [ RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID, + RoleInterface::ADMINISTRATOR_ID, ]); } diff --git a/core/modules/filter/tests/src/Kernel/TextFormatElementFormTest.php b/core/modules/filter/tests/src/Kernel/TextFormatElementFormTest.php index bdf673c909..7e71b30262 100644 --- a/core/modules/filter/tests/src/Kernel/TextFormatElementFormTest.php +++ b/core/modules/filter/tests/src/Kernel/TextFormatElementFormTest.php @@ -45,11 +45,6 @@ protected function setUp(): void { $this->installSchema('system', ['sequences']); $this->installConfig(['filter', 'filter_test']); - // Create user 1 so that the user created later in the test has a different - // user ID. - // @todo Remove in https://www.drupal.org/node/540008. - User::create(['uid' => 1, 'name' => 'user1'])->save(); - /* @var \Drupal\Core\Render\ElementInfoManager $manager */ $manager = \Drupal::service('plugin.manager.element_info'); $manager->clearCachedDefinitions(); diff --git a/core/modules/jsonapi/tests/src/Functional/RoleTest.php b/core/modules/jsonapi/tests/src/Functional/RoleTest.php index a540d46835..eac03bdd42 100644 --- a/core/modules/jsonapi/tests/src/Functional/RoleTest.php +++ b/core/modules/jsonapi/tests/src/Functional/RoleTest.php @@ -84,7 +84,7 @@ protected function getExpectedDocument() { 'self' => ['href' => $self_url], ], 'attributes' => [ - 'weight' => 2, + 'weight' => 3, 'langcode' => 'en', 'status' => TRUE, 'dependencies' => [], diff --git a/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareCombinationTest.php b/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareCombinationTest.php index 2a71d6446f..5c0a96f119 100644 --- a/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareCombinationTest.php +++ b/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareCombinationTest.php @@ -7,7 +7,6 @@ use Drupal\field\Entity\FieldConfig; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\node\Entity\NodeType; -use Drupal\user\Entity\User; use Drupal\field\Entity\FieldStorageConfig; /** @@ -87,9 +86,8 @@ protected function setUp(): void { // Create a normal authenticated user. $this->webUser = $this->drupalCreateUser(['access content']); - // Load the user 1 user for later use as an admin user with permission to - // see everything. - $this->adminUser = User::load(1); + // Create an admin user with permission to see everything. + $this->adminUser = $this->drupalCreateUser([], 'admin_user', TRUE); // The node_access_test_language module allows individual translations of a // node to be marked private (not viewable by normal users), and the diff --git a/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareTest.php b/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareTest.php index deaf02d687..3e10a810e0 100644 --- a/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareTest.php +++ b/core/modules/node/tests/src/Kernel/NodeAccessLanguageAwareTest.php @@ -6,7 +6,6 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\field\Entity\FieldConfig; use Drupal\language\Entity\ConfigurableLanguage; -use Drupal\user\Entity\User; use Drupal\field\Entity\FieldStorageConfig; /** @@ -76,9 +75,8 @@ protected function setUp(): void { // Create a normal authenticated user. $this->webUser = $this->drupalCreateUser(['access content']); - // Load the user 1 user for later use as an admin user with permission to - // see everything. - $this->adminUser = User::load(1); + // Create an admin user with permission to see everything. + $this->adminUser = $this->drupalCreateUser([], 'admin_user', TRUE); // Add Hungarian and Catalan. ConfigurableLanguage::createFromLangcode('hu')->save(); diff --git a/core/modules/node/tests/src/Kernel/NodeAccessLanguageTest.php b/core/modules/node/tests/src/Kernel/NodeAccessLanguageTest.php index a7a66057af..af33c109bd 100644 --- a/core/modules/node/tests/src/Kernel/NodeAccessLanguageTest.php +++ b/core/modules/node/tests/src/Kernel/NodeAccessLanguageTest.php @@ -6,7 +6,6 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\node\Entity\NodeType; -use Drupal\user\Entity\User; /** * Tests node_access and select queries with node_access tag functionality with @@ -184,9 +183,8 @@ public function testNodeAccessQueryTag() { // Create a normal authenticated user. $web_user = $this->drupalCreateUser(['access content']); - // Load the user 1 user for later use as an admin user with permission to - // see everything. - $admin_user = User::load(1); + // Create an admin user with permission to see everything. + $admin_user = $this->drupalCreateUser([], 'admin_user', TRUE); // Creating a private node with langcode Hungarian, will be saved as // the fallback in node access table. diff --git a/core/modules/shortcut/tests/src/Functional/ShortcutCacheTagsTest.php b/core/modules/shortcut/tests/src/Functional/ShortcutCacheTagsTest.php index b6e924bd3e..c9df6faeea 100644 --- a/core/modules/shortcut/tests/src/Functional/ShortcutCacheTagsTest.php +++ b/core/modules/shortcut/tests/src/Functional/ShortcutCacheTagsTest.php @@ -102,6 +102,7 @@ public function testToolbar() { 'config:shortcut.set.default', 'config:system.menu.admin', 'config:system.theme', + 'config:user.role.administrator', 'config:user.role.authenticated', 'rendered', 'user:' . $this->rootUser->id(), diff --git a/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php b/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php index 89c7b7dbc3..f9f89eba60 100644 --- a/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php +++ b/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php @@ -74,12 +74,8 @@ protected function setUp(): void { 'name' => '', ]); $anonymous_user->save(); - $admin_user = User::create([ - 'uid' => 1, - 'name' => 'admin', - 'status' => 1, - ]); - $admin_user->save(); + // Create an admin user with permission to see everything. + $admin_user = $this->createUser([], 'admin_user', TRUE); } /** diff --git a/core/modules/system/tests/src/Functional/Module/UninstallTest.php b/core/modules/system/tests/src/Functional/Module/UninstallTest.php index bf1ae8d3c0..75ee0ae3e9 100644 --- a/core/modules/system/tests/src/Functional/Module/UninstallTest.php +++ b/core/modules/system/tests/src/Functional/Module/UninstallTest.php @@ -5,6 +5,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Entity\EntityMalformedException; +use Drupal\Core\Session\AccountInterface; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; use Drupal\Tests\BrowserTestBase; @@ -37,7 +38,8 @@ public function testUserPermsUninstalled() { $this->container->get('module_installer')->uninstall(['module_test']); // Are the perms defined by module_test removed? - $this->assertEmpty(user_roles(FALSE, 'module_test perm'), 'Permissions were all removed.'); + $roles = array_diff_key(user_roles(FALSE, 'module_test perm'), [AccountInterface::ADMINISTRATOR_ROLE => 1]); + $this->assertEmpty($roles, 'Permissions were all removed.'); } /** diff --git a/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php b/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php index 2ba491e40a..1515e424d8 100644 --- a/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php +++ b/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php @@ -53,18 +53,10 @@ protected function setUp(): void { * @dataProvider testAccessProvider */ public function testAccess($which_user, $which_entity, $view_label_access_result, $view_access_result, $update_access_result, $delete_access_result, $create_access_result) { - // We must always create user 1, so that a "normal" user has an ID >1. - $root_user = $this->drupalCreateUser(); - - if ($which_user === 'user1') { - $user = $root_user; - } - else { - $permissions = ($which_user === 'admin') - ? ['administer site configuration'] - : []; - $user = $this->drupalCreateUser($permissions); - } + $permissions = ($which_user === 'admin') + ? ['administer site configuration'] + : []; + $user = $this->drupalCreateUser($permissions); $entity_values = ($which_entity === 'unlocked') ? ['locked' => FALSE] @@ -125,24 +117,6 @@ public function testAccessProvider() { AccessResult::forbidden()->addCacheTags(['rendered'])->setReason("The DateFormat config entity is locked."), AccessResult::allowed()->addCacheContexts(['user.permissions']), ], - 'user1 + unlocked' => [ - 'user1', - 'unlocked', - AccessResult::allowed(), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - AccessResult::allowed()->addCacheContexts(['user.permissions'])->addCacheTags(['rendered']), - AccessResult::allowed()->addCacheContexts(['user.permissions'])->addCacheTags(['rendered']), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - ], - 'user1 + locked' => [ - 'user1', - 'locked', - AccessResult::allowed(), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - AccessResult::forbidden()->addCacheTags(['rendered'])->setReason("The DateFormat config entity is locked."), - AccessResult::forbidden()->addCacheTags(['rendered'])->setReason("The DateFormat config entity is locked."), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - ], ]; } diff --git a/core/modules/system/tests/src/Kernel/MenuAccessControlHandlerTest.php b/core/modules/system/tests/src/Kernel/MenuAccessControlHandlerTest.php index 548c200402..442fd85769 100644 --- a/core/modules/system/tests/src/Kernel/MenuAccessControlHandlerTest.php +++ b/core/modules/system/tests/src/Kernel/MenuAccessControlHandlerTest.php @@ -53,18 +53,10 @@ protected function setUp(): void { * @dataProvider testAccessProvider */ public function testAccess($which_user, $which_entity, $view_label_access_result, $view_access_result, $update_access_result, $delete_access_result, $create_access_result) { - // We must always create user 1, so that a "normal" user has an ID >1. - $root_user = $this->drupalCreateUser(); - - if ($which_user === 'user1') { - $user = $root_user; - } - else { - $permissions = ($which_user === 'admin') - ? ['administer menu'] - : []; - $user = $this->drupalCreateUser($permissions); - } + $permissions = ($which_user === 'admin') + ? ['administer menu'] + : []; + $user = $this->drupalCreateUser($permissions); $entity_values = ($which_entity === 'unlocked') ? ['locked' => FALSE] @@ -125,24 +117,6 @@ public function testAccessProvider() { AccessResult::forbidden()->addCacheTags(['config:system.menu.llama'])->setReason("The Menu config entity is locked."), AccessResult::allowed()->addCacheContexts(['user.permissions']), ], - 'user1 + unlocked' => [ - 'user1', - 'unlocked', - AccessResult::allowed(), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - AccessResult::allowed()->addCacheContexts(['user.permissions'])->addCacheTags(['config:system.menu.llama']), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - ], - 'user1 + locked' => [ - 'user1', - 'locked', - AccessResult::allowed(), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - AccessResult::forbidden()->addCacheTags(['config:system.menu.llama'])->setReason("The Menu config entity is locked."), - AccessResult::allowed()->addCacheContexts(['user.permissions']), - ], ]; } diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyFieldVidTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyFieldVidTest.php index 456dd49405..fbc9390029 100644 --- a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyFieldVidTest.php +++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyFieldVidTest.php @@ -4,8 +4,8 @@ use Drupal\Core\Render\RenderContext; use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait; +use Drupal\Tests\user\Traits\UserCreationTrait; use Drupal\Tests\views\Kernel\ViewsKernelTestBase; -use Drupal\user\Entity\User; use Drupal\views\Tests\ViewTestData; use Drupal\views\Views; use Drupal\taxonomy\Entity\Vocabulary; @@ -18,6 +18,7 @@ class TaxonomyFieldVidTest extends ViewsKernelTestBase { use TaxonomyTestTrait; + use UserCreationTrait; /** * Modules to enable. @@ -66,10 +67,9 @@ protected function setUp($import_test_views = TRUE): void { $vocabulary = $this->createVocabulary(); $this->term1 = $this->createTerm($vocabulary); - // Create user 1 and set is as the logged in user, so that the logged in - // user has the correct permissions to view the vocabulary name. - $this->adminUser = User::create(['name' => $this->randomString()]); - $this->adminUser->save(); + // Create an admin user and set it as the logged in user, so that the logged + // in user has the correct permissions to view the vocabulary name. + $this->adminUser = $this->setUpCurrentUser([], [], TRUE); $this->container->get('current_user')->setAccount($this->adminUser); ViewTestData::createTestViews(static::class, ['taxonomy_test_views']); diff --git a/core/profiles/standard/config/install/user.role.administrator.yml b/core/modules/user/config/install/user.role.administrator.yml similarity index 100% rename from core/profiles/standard/config/install/user.role.administrator.yml rename to core/modules/user/config/install/user.role.administrator.yml diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php index 0ef0920cbf..1334e0e2c0 100644 --- a/core/modules/user/src/AccountSettingsForm.php +++ b/core/modules/user/src/AccountSettingsForm.php @@ -101,34 +101,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#required' => TRUE, ]; - // Administrative role option. - $form['admin_role'] = [ - '#type' => 'details', - '#title' => $this->t('Administrator role'), - '#open' => TRUE, - ]; - // Do not allow users to set the anonymous or authenticated user roles as the - // administrator role. - $roles = user_role_names(TRUE); - unset($roles[RoleInterface::AUTHENTICATED_ID]); - - $admin_roles = $this->roleStorage->getQuery() - ->condition('is_admin', TRUE) - ->execute(); - $default_value = reset($admin_roles); - - $form['admin_role']['user_admin_role'] = [ - '#type' => 'select', - '#title' => $this->t('Administrator role'), - '#empty_value' => '', - '#default_value' => $default_value, - '#options' => $roles, - '#description' => $this->t('This role will be automatically assigned new permissions whenever a module is enabled. Changing this setting will not affect existing permissions.'), - // Don't allow to select a single admin role in case multiple roles got - // marked as admin role already. - '#access' => count($admin_roles) <= 1, - ]; - // @todo Remove this check once language settings are generalized. if ($this->moduleHandler->moduleExists('content_translation')) { $form['language'] = [ @@ -461,22 +433,6 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $this->config('system.site') ->set('mail_notification', $form_state->getValue('mail_notification_address')) ->save(); - - // Change the admin role. - if ($form_state->hasValue('user_admin_role')) { - $admin_roles = $this->roleStorage->getQuery() - ->condition('is_admin', TRUE) - ->execute(); - - foreach ($admin_roles as $rid) { - $this->roleStorage->load($rid)->setIsAdmin(FALSE)->save(); - } - - $new_admin_role = $form_state->getValue('user_admin_role'); - if ($new_admin_role) { - $this->roleStorage->load($new_admin_role)->setIsAdmin(TRUE)->save(); - } - } } } diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php index dfffbfbb00..cc328b4aaf 100644 --- a/core/modules/user/src/Entity/User.php +++ b/core/modules/user/src/Entity/User.php @@ -96,11 +96,14 @@ public function preSave(EntityStorageInterface $storage) { parent::preSave($storage); // Make sure that the authenticated/anonymous roles are not persisted. - foreach ($this->get('roles') as $index => $item) { - if (in_array($item->target_id, [RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID])) { - $this->get('roles')->offsetUnset($index); + $internal = [RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID]; + $role_refs = $this->get('roles')->getValue(); + foreach ($role_refs as $index => $role_ref) { + if (in_array($role_ref['target_id'], $internal)) { + unset($role_refs[$index]); } } + $this->get('roles')->setValue($role_refs); // Store account cancellation information. foreach (['user_cancel_method', 'user_cancel_notify'] as $key) { @@ -208,11 +211,6 @@ public function removeRole($rid) { * {@inheritdoc} */ public function hasPermission($permission) { - // User #1 has all privileges. - if ((int) $this->id() === 1) { - return TRUE; - } - return $this->getRoleStorage()->isPermissionInRoles($permission, $this->getRoles()); } diff --git a/core/modules/user/src/RoleAccessControlHandler.php b/core/modules/user/src/RoleAccessControlHandler.php index e979f2d2c8..f784a31ee4 100644 --- a/core/modules/user/src/RoleAccessControlHandler.php +++ b/core/modules/user/src/RoleAccessControlHandler.php @@ -20,7 +20,12 @@ class RoleAccessControlHandler extends EntityAccessControlHandler { protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { switch ($operation) { case 'delete': - if ($entity->id() == RoleInterface::ANONYMOUS_ID || $entity->id() == RoleInterface::AUTHENTICATED_ID) { + $internal_roles = [ + RoleInterface::ANONYMOUS_ID, + RoleInterface::AUTHENTICATED_ID, + RoleInterface::ADMINISTRATOR_ID, + ]; + if (in_array($entity->id(), $internal_roles)) { return AccessResult::forbidden(); } diff --git a/core/modules/user/src/RoleInterface.php b/core/modules/user/src/RoleInterface.php index 3c9425f644..45fb4947b4 100644 --- a/core/modules/user/src/RoleInterface.php +++ b/core/modules/user/src/RoleInterface.php @@ -22,6 +22,11 @@ interface RoleInterface extends ConfigEntityInterface { */ const AUTHENTICATED_ID = AccountInterface::AUTHENTICATED_ROLE; + /** + * Role ID for the admin role; should match the 'role' entity ID. + */ + const ADMINISTRATOR_ID = AccountInterface::ADMINISTRATOR_ROLE; + /** * Returns a list of permissions assigned to the role. * diff --git a/core/modules/user/tests/src/Functional/Rest/RoleResourceTestBase.php b/core/modules/user/tests/src/Functional/Rest/RoleResourceTestBase.php index 1bb24b676e..b2730160b1 100644 --- a/core/modules/user/tests/src/Functional/Rest/RoleResourceTestBase.php +++ b/core/modules/user/tests/src/Functional/Rest/RoleResourceTestBase.php @@ -48,7 +48,7 @@ protected function createEntity() { protected function getExpectedNormalizedEntity() { return [ 'uuid' => $this->entity->uuid(), - 'weight' => 2, + 'weight' => 3, 'langcode' => 'en', 'status' => TRUE, 'dependencies' => [], diff --git a/core/modules/user/tests/src/Functional/Update/UserUpdateUserOneAdminTest.php b/core/modules/user/tests/src/Functional/Update/UserUpdateUserOneAdminTest.php new file mode 100644 index 0000000000..8ab0a4eae4 --- /dev/null +++ b/core/modules/user/tests/src/Functional/Update/UserUpdateUserOneAdminTest.php @@ -0,0 +1,108 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.8.0.bare.standard.php.gz', + ]; + } + + /** + * Tests that user 1 has the administrator role after the update. + */ + public function testUserOneAdminRole() { + // Standard install profile used to assign the administrator role to user 1. + // Let's check for its presence, remove it and check whether the removal did + // its job. + $this->assertTrue($this->userOneHasAdminRole()); + $user1 = User::load(1); + $user1->removeRole('administrator'); + $user1->save(); + $this->assertFalse($this->userOneHasAdminRole()); + + // The user_post_update_change_user_1_and_admin_role() post update function + // should have added the administrator role to user 1. + $this->runUpdates(); + $this->assertTrue($this->userOneHasAdminRole()); + } + + /** + * Tests that an existing non-admin 'administrator' role is renamed. + */ + public function testResolvesAdminRoleCollision() { + // Standard install profile used to assign the administrator role to user 1. + // Let's turn it into a non-admin role by assigning it a few permissions and + // unchecking the isAdmin() flag. It should then be migrated to a new role + // called administrator_legacy and users who had it should also receive the + // new role. + $admin_role = Role::load('administrator'); + $admin_role->setIsAdmin(FALSE); + $admin_role->grantPermission('access content'); + $admin_role->save(); + + // The user_post_update_change_user_1_and_admin_role() post update function + // should have added the administrator_legacy role to user 1. + $this->assertFalse($this->userOneHasLegacyAdminRole(), 'User one does not have the legacy admin role.'); + $this->assertTrue($this->userOneHasAdminRole()); + $this->runUpdates(); + $this->assertTrue($this->userOneHasAdminRole(), 'User one have the admin role.'); + $this->assertTrue($this->userOneHasLegacyAdminRole(), 'User one received the legacy admin role.'); + + // The legacy admin role should have the permissions of the old admin role. + $legacy_admin_role = Role::load($this->getLegacyAdminRoleId()); + $this->assertEqual(['access content'], $legacy_admin_role->getPermissions(), 'Legacy admin role has the right permissions.'); + } + + /** + * Checks whether user 1 has the administrator role. + * + * @return bool + * Whether user 1 has the administrator role. + */ + protected function userOneHasAdminRole() { + $user1 = User::load(1); + return in_array('administrator', $user1->getRoles(), TRUE); + } + + /** + * Checks whether user 1 has the legacy administrator role. + * + * @return bool + * Whether user 1 has the legacy administrator role. + */ + protected function userOneHasLegacyAdminRole() { + $user1 = User::load(1); + return in_array($this->getLegacyAdminRoleId(), $user1->getRoles(), TRUE); + } + + /** + * Retrieves the legacy administrator role ID. + * + * By default this is administrator_legacy but in the rare event of a naming + * collision, people may choose an ID themselves by setting it to the + * user_update_9201_legacy_admin_role setting in settings.php. + * + * @return string + * The legacy administrator role ID. + */ + protected function getLegacyAdminRoleId() { + return Settings::get('user_update_9201_legacy_admin_role', 'administrator_legacy'); + } + +} diff --git a/core/modules/user/tests/src/Functional/UserPermissionsTest.php b/core/modules/user/tests/src/Functional/UserPermissionsTest.php index 0532c834a6..4678fffbfb 100644 --- a/core/modules/user/tests/src/Functional/UserPermissionsTest.php +++ b/core/modules/user/tests/src/Functional/UserPermissionsTest.php @@ -4,7 +4,6 @@ use Drupal\Tests\BrowserTestBase; use Drupal\user\RoleInterface; -use Drupal\user\Entity\Role; /** * Verify that role permissions can be added and removed via the permissions @@ -57,9 +56,6 @@ public function testUserPermissionChanges() { $permissions_hash_generator = $this->container->get('user_permissions_hash_generator'); $storage = $this->container->get('entity_type.manager')->getStorage('user_role'); - - // Create an additional role and mark it as admin role. - Role::create(['is_admin' => TRUE, 'id' => 'administrator', 'label' => 'Administrator'])->save(); $storage->resetCache(); $this->drupalLogin($this->adminUser); @@ -101,49 +97,6 @@ public function testUserPermissionChanges() { } } - /** - * Test assigning of permissions for the administrator role. - */ - public function testAdministratorRole() { - $this->drupalLogin($this->adminUser); - $this->drupalGet('admin/config/people/accounts'); - - // Verify that the administration role is none by default. - $this->assertTrue($this->assertSession()->optionExists('edit-user-admin-role', '')->isSelected()); - - $this->assertFalse(Role::load($this->rid)->isAdmin()); - - // Set the user's role to be the administrator role. - $edit = []; - $edit['user_admin_role'] = $this->rid; - $this->drupalPostForm('admin/config/people/accounts', $edit, 'Save configuration'); - - \Drupal::entityTypeManager()->getStorage('user_role')->resetCache(); - $this->assertTrue(Role::load($this->rid)->isAdmin()); - - // Enable aggregator module and ensure the 'administer news feeds' - // permission is assigned by default. - \Drupal::service('module_installer')->install(['aggregator']); - - $this->assertTrue($this->adminUser->hasPermission('administer news feeds'), 'The permission was automatically assigned to the administrator role'); - - // Ensure that selecting '- None -' removes the admin role. - $edit = []; - $edit['user_admin_role'] = ''; - $this->drupalPostForm('admin/config/people/accounts', $edit, 'Save configuration'); - - \Drupal::entityTypeManager()->getStorage('user_role')->resetCache(); - \Drupal::configFactory()->reset(); - $this->assertFalse(Role::load($this->rid)->isAdmin()); - - // Manually create two admin roles, in that case the single select should be - // hidden. - Role::create(['id' => 'admin_role_0', 'is_admin' => TRUE, 'label' => 'Admin role 0'])->save(); - Role::create(['id' => 'admin_role_1', 'is_admin' => TRUE, 'label' => 'Admin role 1'])->save(); - $this->drupalGet('admin/config/people/accounts'); - $this->assertSession()->fieldNotExists('user_admin_role'); - } - /** * Verify proper permission changes by user_role_change_permissions(). */ diff --git a/core/modules/user/tests/src/Kernel/UserEntityTest.php b/core/modules/user/tests/src/Kernel/UserEntityTest.php index 3a2eff317d..10d0f47fe3 100644 --- a/core/modules/user/tests/src/Kernel/UserEntityTest.php +++ b/core/modules/user/tests/src/Kernel/UserEntityTest.php @@ -42,10 +42,16 @@ public function testUserMethods() { $role_storage->create(['id' => 'test_role_two'])->save(); $role_storage->create(['id' => 'test_role_three'])->save(); - $values = [ - 'uid' => 1, - 'roles' => ['test_role_one'], - ]; + // Test that user 1 is no different than any regular authenticated user. + $user = User::create(['uid' => 1]); + $this->assertEqual([RoleInterface::AUTHENTICATED_ID], $user->getRoles()); + + // Test that a regular authenticated user gets the authenticated role. + $user = User::create(['uid' => 2]); + $this->assertEqual([RoleInterface::AUTHENTICATED_ID], $user->getRoles()); + + // Test role manipulation for an authenticated user. + $values = ['uid' => 3, 'roles' => ['test_role_one']]; $user = User::create($values); $this->assertTrue($user->hasRole('test_role_one')); diff --git a/core/modules/user/tests/src/Kernel/UserInstallTest.php b/core/modules/user/tests/src/Kernel/UserInstallTest.php index 1ab0df44ac..24fa8415d6 100644 --- a/core/modules/user/tests/src/Kernel/UserInstallTest.php +++ b/core/modules/user/tests/src/Kernel/UserInstallTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\user\Kernel; +use Drupal\Core\Session\AccountInterface; use Drupal\KernelTests\KernelTestBase; /** @@ -48,6 +49,14 @@ public function testUserInstall() { $this->assertTrue($admin->isActive()); // Test that the anonymous user is blocked. $this->assertTrue($anon->isBlocked()); + + // Verify that UID 1 gets the administrator role. + $rids = \Drupal::database()->select('user__roles', 'r') + ->fields('r', ['roles_target_id']) + ->condition('entity_id', 1) + ->execute() + ->fetchCol(); + $this->assertEquals([AccountInterface::ADMINISTRATOR_ROLE], $rids, 'Admin user has administrator role'); } } diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 8375992ce7..c831ec8e67 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -5,6 +5,12 @@ * Install, update and uninstall functions for the user module. */ +use Drupal\Core\Config\ExtensionInstallStorage; +use Drupal\Core\Config\InstallStorage; +use Drupal\Core\Config\StorageInterface; +use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Site\Settings; + /** * Implements hook_schema(). */ @@ -87,6 +93,7 @@ function user_install() { 'name' => 'placeholder-for-uid-1', 'mail' => 'placeholder-for-uid-1', 'status' => TRUE, + 'roles' => [AccountInterface::ADMINISTRATOR_ROLE], ]) ->save(); } @@ -97,3 +104,38 @@ function user_install() { function user_update_last_removed() { return 8100; } + +/** + * Ensure the presence of the administrator role. + */ +function user_update_9201() { + $config_factory = \Drupal::configFactory(); + $config_name = 'user.role.administrator'; + $role = $config_factory->getEditable($config_name); + + if (!$role->isNew() && !$role->get('is_admin')) { + // We need to be careful not to grant people more access all of the sudden. + // Migrate the administrator role to a new administrator_legacy role. We will + // assign old administrators that role in the post update hook. + $legacy_admin_role = Settings::get('user_update_9201_legacy_admin_role', 'administrator_legacy'); + $legacy_role = $config_factory->getEditable('user.role.' . $legacy_admin_role); + $legacy_role->setData($role->getRawData()); + $legacy_role->set('id', $legacy_admin_role); + $legacy_role->set('label', 'Administrator (legacy)'); + $legacy_role->save(TRUE); + } + + // Add the administrator role if it doesn't exist yet or if we migrated an + // existing administrator role to administrator_legacy. + if ($role->isNew() || isset($legacy_role)) { + $yaml_storage = new ExtensionInstallStorage( + \Drupal::service('config.storage'), + InstallStorage::CONFIG_INSTALL_DIRECTORY, + StorageInterface::DEFAULT_COLLECTION, + TRUE, + NULL + ); + $role->setData($yaml_storage->read($config_name)); + $role->save(TRUE); + } +} diff --git a/core/modules/user/user.post_update.php b/core/modules/user/user.post_update.php index 154cd03590..b5006bfa77 100644 --- a/core/modules/user/user.post_update.php +++ b/core/modules/user/user.post_update.php @@ -5,6 +5,10 @@ * Post update functions for User module. */ +use Drupal\Core\Site\Settings; +use Drupal\user\Entity\Role; +use Drupal\user\Entity\User; + /** * Implements hook_removed_post_updates(). */ @@ -13,3 +17,30 @@ function user_removed_post_updates() { 'user_post_update_enforce_order_of_permissions' => '9.0.0', ]; } + +/** + * Grant user 1 the administrator role and potentially update legacy admins. + */ +function user_post_update_change_user_1_and_admin_role() { + // If we have a legacy administrator role, we need to update all users who had + // the original administrator role to now have the legacy role. + $legacy_admin_role = Settings::get('user_update_9201_legacy_admin_role', 'administrator_legacy'); + if ($role = Role::load($legacy_admin_role)) { + $uids = Drupal::entityQuery('user') + ->condition('roles', 'administrator') + ->execute(); + + if (!empty($uids)) { + /** @var \Drupal\user\UserInterface $account */ + foreach (User::loadMultiple($uids) as $account) { + $account->removeRole('administrator'); + $account->addRole($legacy_admin_role); + $account->save(); + } + } + } + + $account = User::load(1); + $account->addRole('administrator'); + $account->save(); +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php b/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php index 0c41f387c9..76efaf8a68 100644 --- a/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php +++ b/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php @@ -42,7 +42,7 @@ protected function setUp($import_test_views = TRUE) { $role_with_access = Role::create([ 'id' => 'with_access', - 'permissions' => ['view test entity field'], + 'permissions' => ['access content', 'view test entity field'], ]); $role_with_access->save(); $role_without_access = Role::create([ diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php index 9b4fe1cff7..95df6ed438 100644 --- a/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php +++ b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php @@ -7,9 +7,10 @@ use Drupal\entity_test\Entity\EntityTestRev; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; +use Drupal\Tests\views\Kernel\ViewsKernelTestBase; use Drupal\user\Entity\User; +use Drupal\user\RoleInterface; use Drupal\views\Plugin\views\field\EntityField; -use Drupal\Tests\views\Kernel\ViewsKernelTestBase; use Drupal\views\Tests\ViewTestData; use Drupal\views\Views; @@ -72,6 +73,7 @@ protected function setUp($import_test_views = TRUE): void { // First setup the needed entity types before installing the views. parent::setUp(FALSE); + $this->installConfig('user'); $this->installEntitySchema('user'); $this->installEntitySchema('entity_test'); $this->installEntitySchema('entity_test_rev'); @@ -79,7 +81,7 @@ protected function setUp($import_test_views = TRUE): void { ViewTestData::createTestViews(static::class, ['views_test_config']); // Bypass any field access. - $this->adminUser = User::create(['name' => $this->randomString()]); + $this->adminUser = User::create(['name' => $this->randomString(), 'roles' => [RoleInterface::ADMINISTRATOR_ID]]); $this->adminUser->save(); $this->container->get('current_user')->setAccount($this->adminUser); diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldRenderedEntityTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldRenderedEntityTest.php index 0c69458214..1f167b6747 100644 --- a/core/modules/views/tests/src/Kernel/Handler/FieldRenderedEntityTest.php +++ b/core/modules/views/tests/src/Kernel/Handler/FieldRenderedEntityTest.php @@ -49,11 +49,6 @@ protected function setUpFixtures() { $this->installEntitySchema('entity_test'); $this->installConfig(['entity_test']); - // Create user 1 so that the user created later in the test has a different - // user ID. - // @todo Remove in https://www.drupal.org/node/540008. - User::create(['uid' => 1, 'name' => 'user1'])->save(); - EntityViewMode::create([ 'id' => 'entity_test.foobar', 'targetEntityType' => 'entity_test', diff --git a/core/modules/views/tests/src/Kernel/Plugin/RssFieldsTest.php b/core/modules/views/tests/src/Kernel/Plugin/RssFieldsTest.php index f2b6ee7580..fc0fa2fbbf 100644 --- a/core/modules/views/tests/src/Kernel/Plugin/RssFieldsTest.php +++ b/core/modules/views/tests/src/Kernel/Plugin/RssFieldsTest.php @@ -49,9 +49,9 @@ protected function setUp($import_test_views = TRUE): void { * subdirectory. */ public function testLink() { - // Set up the current user as uid 1 so the test doesn't need to deal with - // permission. - $this->setUpCurrentUser(['uid' => 1]); + // Set up the current user as an administrator so the test doesn't need to + // deal with permission. + $this->setUpCurrentUser([], [], TRUE); $node = $this->createNode([ 'type' => 'article', diff --git a/core/modules/workspaces/workspaces.install b/core/modules/workspaces/workspaces.install index a04e775414..db3108b14a 100644 --- a/core/modules/workspaces/workspaces.install +++ b/core/modules/workspaces/workspaces.install @@ -65,8 +65,8 @@ function workspaces_install() { $result = $query->execute(); } - // Default to user ID 1 if we could not find any other administrator users. - $owner_id = !empty($result) ? reset($result) : 1; + // Default to user ID 0 if we could not find any other administrator users. + $owner_id = !empty($result) ? reset($result) : 0; // Create a 'stage' workspace by default. Workspace::create([ diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install index 990ca38d2c..6fda4647f1 100644 --- a/core/profiles/standard/standard.install +++ b/core/profiles/standard/standard.install @@ -5,7 +5,6 @@ * Install, update and uninstall functions for the standard installation profile. */ -use Drupal\user\Entity\User; use Drupal\shortcut\Entity\Shortcut; /** @@ -16,11 +15,6 @@ * @see system_install() */ function standard_install() { - // Assign user 1 the "administrator" role. - $user = User::load(1); - $user->roles[] = 'administrator'; - $user->save(); - // Populate the default shortcut set. $shortcut = Shortcut::create([ 'shortcut_set' => 'default', diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigSyncDirectoryMultilingualTest.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigSyncDirectoryMultilingualTest.php index 42bf5b5fbb..5a5e3d11c0 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigSyncDirectoryMultilingualTest.php +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigSyncDirectoryMultilingualTest.php @@ -48,7 +48,7 @@ public function testConfigSync() { parent::testConfigSync(); // Ensure that menu blocks have been created correctly. $this->assertSession()->responseNotContains('This block is broken or missing.'); - $this->assertSession()->linkExists('Add content'); + $this->assertSession()->responseContains('Congratulations, you installed Drupal!'); } } diff --git a/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php b/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php index b8b4c61897..0815192c7b 100644 --- a/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php +++ b/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php @@ -55,6 +55,7 @@ public function testModuleConfig($module) { // other modules. Therefore they are put into static::$modules, which though // doesn't install config files, so import those config files explicitly. Do // this for all tests in case optional configuration depends on it. + $this->installEntitySchema('user'); $this->installConfig(['system', 'user']); $module_path = drupal_get_path('module', $module) . '/'; diff --git a/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php index 0989959a57..d1224f9e9d 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php @@ -49,11 +49,6 @@ protected function setUp(): void { $this->installEntitySchema('entity_test_string_id'); - // Create user 1 so that the user created later in the test has a different - // user ID. - // @todo Remove in https://www.drupal.org/node/540008. - User::create(['uid' => 1, 'name' => 'user1'])->save(); - Role::create([ 'id' => 'test_role', 'label' => 'Can view test entities', diff --git a/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php index f1665b999b..72176d7b3f 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php @@ -6,8 +6,6 @@ use Drupal\entity_test\Entity\EntityTestMul; use Drupal\KernelTests\KernelTestBase; use Drupal\Tests\user\Traits\UserCreationTrait; -use Drupal\user\Entity\Role; -use Drupal\user\RoleInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -31,19 +29,10 @@ class RouteProviderTest extends KernelTestBase { protected function setUp(): void { parent::setUp(); - $this->setUpCurrentUser(['uid' => 1]); - $this->installEntitySchema('entity_test_mul'); $this->installEntitySchema('entity_test_admin_routes'); - /** @var \Drupal\user\RoleInterface $role */ - $role = Role::create([ - 'id' => RoleInterface::ANONYMOUS_ID, - ]); - $role - ->grantPermission('administer entity_test content') - ->grantPermission('view test entity'); - $role->save(); + $this->setUpCurrentUser([], ['administer entity_test content', 'view test entity']); } protected function httpKernelHandle($url) { diff --git a/core/tests/Drupal/KernelTests/Core/Render/RenderCacheTest.php b/core/tests/Drupal/KernelTests/Core/Render/RenderCacheTest.php index a258de0073..038d6baa4f 100644 --- a/core/tests/Drupal/KernelTests/Core/Render/RenderCacheTest.php +++ b/core/tests/Drupal/KernelTests/Core/Render/RenderCacheTest.php @@ -32,63 +32,52 @@ protected function setUp(): void { } /** - * Tests that user 1 has a different permission context with the same roles. + * Check the render cache for the user.permissions context. */ - public function testUser1PermissionContext() { - $this->doTestUser1WithContexts(['user.permissions']); + public function testPermissionContext() { + $this->doTestWithContexts(['user.permissions']); } /** - * Tests that user 1 has a different roles context with the same roles. + * Check the render cache for the user.roles context. */ - public function testUser1RolesContext() { - $this->doTestUser1WithContexts(['user.roles']); + public function testRolesContext() { + $this->doTestWithContexts(['user.roles']); } /** - * Ensures that user 1 has a unique render cache for the given context. + * Checks the functionality of the render cache for the given context. * * @param string[] $contexts * List of cache contexts to use. */ - protected function doTestUser1WithContexts($contexts) { - // Test that user 1 does not share the cache with other users who have the - // same roles, even when using a role-based cache context. - $user1 = $this->createUser(); - $this->assertEqual($user1->id(), 1); + protected function doTestWithContexts($contexts) { + // Set up two authenticated users and an admin user so we can test the + // output of the render cache for them. $first_authenticated_user = $this->createUser(); $second_authenticated_user = $this->createUser(); $admin_user = $this->createUser([], NULL, TRUE); - $this->assertEqual($user1->getRoles(), $first_authenticated_user->getRoles(), 'User 1 has the same roles as an authenticated user.'); - // Impersonate user 1 and render content that only user 1 should have - // permission to see. - \Drupal::service('account_switcher')->switchTo($user1); + // Set up a test element we will reuse below. $test_element = [ '#cache' => [ 'keys' => ['test'], 'contexts' => $contexts, ], ]; + + // Render content that only authenticated users should see. + \Drupal::service('account_switcher')->switchTo($first_authenticated_user); $element = $test_element; - $element['#markup'] = 'content for user 1'; + $element['#markup'] = 'content for authenticated users'; $output = \Drupal::service('renderer')->renderRoot($element); - $this->assertEqual($output, 'content for user 1'); + $this->assertEqual($output, 'content for authenticated users'); // Verify the cache is working by rendering the same element but with // different markup passed in; the result should be the same. $element = $test_element; $element['#markup'] = 'should not be used'; $output = \Drupal::service('renderer')->renderRoot($element); - $this->assertEqual($output, 'content for user 1'); - \Drupal::service('account_switcher')->switchBack(); - - // Verify that the first authenticated user does not see the same content - // as user 1. - \Drupal::service('account_switcher')->switchTo($first_authenticated_user); - $element = $test_element; - $element['#markup'] = 'content for authenticated users'; - $output = \Drupal::service('renderer')->renderRoot($element); $this->assertEqual($output, 'content for authenticated users'); \Drupal::service('account_switcher')->switchBack(); @@ -108,7 +97,6 @@ protected function doTestUser1WithContexts($contexts) { $element['#markup'] = 'content for admin user'; $output = \Drupal::service('renderer')->renderRoot($element); $this->assertEqual($output, 'content for admin user'); - \Drupal::service('account_switcher')->switchBack(); } } diff --git a/core/tests/Drupal/Tests/Core/Cache/Context/IsSuperUserCacheContextTest.php b/core/tests/Drupal/Tests/Core/Cache/Context/IsSuperUserCacheContextTest.php new file mode 100644 index 0000000000..2760195f4a --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Cache/Context/IsSuperUserCacheContextTest.php @@ -0,0 +1,66 @@ +expectDeprecation('\Drupal\Core\Cache\Context\IsSuperUserCacheContext is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.'); + $cache_context = new IsSuperUserCacheContext($this->getMockBuilder(AccountInterface::class) + ->getMock()); + } + + /** + * Tests the deprecation of \Drupal\Core\Cache\Context\IsSuperUserCacheContext::getLabel(). + * + * @group legacy + */ + public function testGetLabel() { + $this->expectDeprecation('\Drupal\Core\Cache\Context\IsSuperUserCacheContext is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.'); + $cache_context = new IsSuperUserCacheContext($this->getMockBuilder(AccountInterface::class) + ->getMock()); + $this->expectDeprecation('Calling Drupal\Core\Cache\Context\IsSuperUserCacheContext::getLabel() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.'); + $cache_context::getLabel(); + } + + /** + * Tests the deprecation of \Drupal\Core\Cache\Context\IsSuperUserCacheContext::getContext(). + * + * @group legacy + */ + public function testGetContext() { + $this->expectDeprecation('\Drupal\Core\Cache\Context\IsSuperUserCacheContext is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.'); + $cache_context = new IsSuperUserCacheContext($this->getMockBuilder(AccountInterface::class) + ->getMock()); + $this->expectDeprecation('Calling Drupal\Core\Cache\Context\IsSuperUserCacheContext::getContext() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.'); + $cache_context->getContext(); + } + + /** + * Tests the deprecation of \Drupal\Core\Cache\Context\IsSuperUserCacheContext::getCacheableMetadata(). + * + * @group legacy + */ + public function testGetCacheableMetadata() { + $this->expectDeprecation('\Drupal\Core\Cache\Context\IsSuperUserCacheContext is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.'); + $cache_context = new IsSuperUserCacheContext($this->getMockBuilder(AccountInterface::class) + ->getMock()); + $this->expectDeprecation('Calling Drupal\Core\Cache\Context\IsSuperUserCacheContext::getCacheableMetadata() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. See https://www.drupal.org/node/2910500 for more information.'); + $cache_context->getCacheableMetadata(); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php b/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php index 128f325fb9..409b08e238 100644 --- a/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php @@ -14,12 +14,19 @@ class PermissionsHashGeneratorTest extends UnitTestCase { /** - * The mocked super user account. + * The mocked admin account. * * @var \Drupal\user\UserInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $account1; + /** + * An "updated" admin account. + * + * @var \Drupal\user\UserInterface|\PHPUnit\Framework\MockObject\MockObject + */ + protected $account1Updated; + /** * A mocked account. * @@ -62,6 +69,13 @@ class PermissionsHashGeneratorTest extends UnitTestCase { */ protected $staticCache; + /** + * The mocked entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + /** * The permission hash class being tested. * @@ -77,57 +91,107 @@ protected function setUp(): void { new Settings(['hash_salt' => 'test']); - // The mocked super user account, with the same roles as Account 2. + // Mock an admin role. + $admin_role = $this->getMockBuilder('Drupal\user\Entity\Role') + ->disableOriginalConstructor() + ->setMethods(['isAdmin']) + ->getMock(); + $admin_role->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(TRUE)); + + // Mock a regular role. + $regular_role = $this->getMockBuilder('Drupal\user\Entity\Role') + ->disableOriginalConstructor() + ->setMethods(['isAdmin']) + ->getMock(); + $regular_role->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(FALSE)); + + // Account 1: 'authenticated' and 'administrator' role. + $roles_1 = [ + 'authenticated' => $regular_role, + 'administrator' => $admin_role, + ]; $this->account1 = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() ->setMethods(['getRoles', 'id']) ->getMock(); + $this->account1->expects($this->any()) + ->method('getRoles') + ->will($this->returnValue(array_keys($roles_1))); $this->account1->expects($this->any()) ->method('id') ->willReturn(1); - $this->account1->expects($this->never()) - ->method('getRoles'); - // Account 2: 'administrator' and 'authenticated' roles. - $roles_1 = ['administrator', 'authenticated']; + // Account 2: 'editor' and 'authenticated' roles. + $roles_2 = [ + 'editor' => $regular_role, + 'authenticated' => $regular_role, + ]; $this->account2 = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() ->setMethods(['getRoles', 'id']) ->getMock(); $this->account2->expects($this->any()) ->method('getRoles') - ->will($this->returnValue($roles_1)); + ->will($this->returnValue(array_keys($roles_2))); $this->account2->expects($this->any()) ->method('id') ->willReturn(2); - // Account 3: 'authenticated' and 'administrator' roles (different order). - $roles_3 = ['authenticated', 'administrator']; + // Account 3: 'authenticated' and 'editor' roles (different order). + $roles_3 = [ + 'authenticated' => $regular_role, + 'editor' => $regular_role, + ]; $this->account3 = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() ->setMethods(['getRoles', 'id']) ->getMock(); $this->account3->expects($this->any()) ->method('getRoles') - ->will($this->returnValue($roles_3)); + ->will($this->returnValue(array_keys($roles_3))); $this->account3->expects($this->any()) ->method('id') ->willReturn(3); - // Updated account 2: now also 'editor' role. - $roles_2_updated = ['editor', 'administrator', 'authenticated']; + // Updated account 1: now also 'publisher' role. + $roles_1_updated = [ + 'authenticated' => $regular_role, + 'administrator' => $admin_role, + 'publisher' => $regular_role, + ]; + $this->account1Updated = $this->getMockBuilder('Drupal\user\Entity\User') + ->disableOriginalConstructor() + ->setMethods(['getRoles', 'id']) + ->getMock(); + $this->account1Updated->expects($this->any()) + ->method('getRoles') + ->will($this->returnValue(array_keys($roles_1_updated))); + $this->account1Updated->expects($this->any()) + ->method('id') + ->willReturn(1); + + // Updated account 2: now also 'publisher' role. + $roles_2_updated = [ + 'editor' => $regular_role, + 'authenticated' => $regular_role, + 'publisher' => $regular_role, + ]; $this->account2Updated = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() ->setMethods(['getRoles', 'id']) ->getMock(); $this->account2Updated->expects($this->any()) ->method('getRoles') - ->will($this->returnValue($roles_2_updated)); + ->will($this->returnValue(array_keys($roles_2_updated))); $this->account2Updated->expects($this->any()) ->method('id') ->willReturn(2); - // Mocked private key + cache services. + // Mocked private key, cache, static cache and entity type manager services. $random = Crypt::randomBytesBase64(55); $this->privateKey = $this->getMockBuilder('Drupal\Core\PrivateKey') ->disableOriginalConstructor() @@ -142,25 +206,48 @@ protected function setUp(): void { $this->staticCache = $this->getMockBuilder('Drupal\Core\Cache\CacheBackendInterface') ->disableOriginalConstructor() ->getMock(); + $this->entityTypeManager = $this->getMockBuilder('Drupal\Core\Entity\EntityTypeManagerInterface') + ->disableOriginalConstructor() + ->getMock(); - $this->permissionsHash = new PermissionsHashGenerator($this->privateKey, $this->cache, $this->staticCache); + // Mock the role storage and the return values we care about. + $role_storage = $this->getMockBuilder('Drupal\Core\Entity\EntityStorageInterface') + ->disableOriginalConstructor() + ->getMock(); + $role_storage->expects($this->any()) + ->method('loadMultiple') + ->willReturnMap([ + [$this->account1->getRoles(), $roles_1], + [$this->account2->getRoles(), $roles_2], + [$this->account3->getRoles(), $roles_3], + [$this->account1Updated->getRoles(), $roles_1_updated], + [$this->account2Updated->getRoles(), $roles_2_updated], + ]); + + // Set the role storage on the entity type manager. + $this->entityTypeManager->expects($this->any()) + ->method('getStorage') + ->with('user_role') + ->will($this->returnValue($role_storage)); + + $this->permissionsHash = new PermissionsHashGenerator($this->privateKey, $this->cache, $this->staticCache, $this->entityTypeManager); } /** * @covers ::generate */ public function testGenerate() { - // Ensure that the super user (user 1) always gets the same hash. - $super_user_hash = $this->permissionsHash->generate($this->account1); + // Ensure that admin accounts always gets the same hash. + $admin_hash = $this->permissionsHash->generate($this->account1); + $updated_admin_hash = $this->permissionsHash->generate($this->account1Updated); + $this->assertSame($admin_hash, $updated_admin_hash, 'Admin user with updated roles generates same permissions hash.'); // Ensure that two user accounts with the same roles generate the same hash. $hash_2 = $this->permissionsHash->generate($this->account2); $hash_3 = $this->permissionsHash->generate($this->account3); $this->assertSame($hash_2, $hash_3, 'Different users with the same roles generate the same permissions hash.'); - $this->assertNotSame($hash_2, $super_user_hash, 'User 1 has a different hash despite having the same roles'); - - // Compare with hash for user account 1 with an additional role. + // Compare with hash for user account 2 with an additional role. $updated_hash_2 = $this->permissionsHash->generate($this->account2Updated); $this->assertNotSame($hash_2, $updated_hash_2, 'Same user with updated roles generates different permissions hash.'); } @@ -170,7 +257,7 @@ public function testGenerate() { */ public function testGeneratePersistentCache() { // Set expectations for the mocked cache backend. - $expected_cid = 'user_permissions_hash:administrator,authenticated'; + $expected_cid = 'user_permissions_hash:authenticated,editor'; $mock_cache = new \stdClass(); $mock_cache->data = 'test_hash_here'; @@ -198,7 +285,7 @@ public function testGeneratePersistentCache() { */ public function testGenerateStaticCache() { // Set expectations for the mocked cache backend. - $expected_cid = 'user_permissions_hash:administrator,authenticated'; + $expected_cid = 'user_permissions_hash:authenticated,editor'; $mock_cache = new \stdClass(); $mock_cache->data = 'test_hash_here'; @@ -223,7 +310,7 @@ public function testGenerateStaticCache() { */ public function testGenerateNoCache() { // Set expectations for the mocked cache backend. - $expected_cid = 'user_permissions_hash:administrator,authenticated'; + $expected_cid = 'user_permissions_hash:authenticated,editor'; $this->staticCache->expects($this->once()) ->method('get')