diff --git a/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php b/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php index 375d80b..2db501f 100644 --- a/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php +++ b/core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php @@ -23,6 +23,9 @@ public static function getLabel() { * {@inheritdoc} */ public function getContext($role = NULL) { + if ($this->user->id() == 1) { + return 'is-super-user'; + } if ($role === NULL) { return 'r.' . implode(',', $this->user->getRoles()); } diff --git a/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php b/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php index f3b67da..c4fe7b9 100644 --- a/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php +++ b/core/lib/Drupal/Core/Session/PermissionsHashGenerator.php @@ -51,10 +51,10 @@ 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. No hash - // necessary in this case. + // 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 'is-super-user'; + return $this->hash('is-super-user'); } $sorted_roles = $account->getRoles(); @@ -95,7 +95,20 @@ protected function doGenerate(array $roles) { // effectively be no-ops, allowing for hash collisions.) $permissions_by_role[$role] = $permissions; } - return hash('sha256', $this->privateKey->get() . Settings::getHashSalt() . serialize($permissions_by_role)); + return $this->hash(serialize($permissions_by_role)); + } + + /** + * Hashes the given string. + * + * @param string $identifier + * The string to be hashed. + * + * @return string + * The hash. + */ + protected function hash($identifier) { + return hash('sha256', $this->privateKey->get() . Settings::getHashSalt() . $identifier); } } diff --git a/core/modules/system/src/Tests/Render/RenderCacheTest.php b/core/modules/system/src/Tests/Render/RenderCacheTest.php index 55c3639..b4317d1 100644 --- a/core/modules/system/src/Tests/Render/RenderCacheTest.php +++ b/core/modules/system/src/Tests/Render/RenderCacheTest.php @@ -40,6 +40,23 @@ protected function setUp() { * Tests that user 1 has a different permission context with the same roles. */ public function testUser1PermissionContext() { + $this->doTestUser1WithContexts(['user.permissions']); + } + + /** + * Tests that user 1 has a different roles context with the same roles. + */ + public function testUser1RolesContext() { + $this->doTestUser1WithContexts(['user.roles']); + } + + /** + * Ensures that user 1 has a unique 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(); @@ -52,12 +69,12 @@ public function testUser1PermissionContext() { // Impersonate user 1 and render content that only user 1 should have // permission to see. \Drupal::service('account_switcher')->switchTo($user1); - $test_element = array( - '#cache' => array( - 'keys' => array('test'), - 'contexts' => array('user.permissions'), - ), - ); + $test_element = [ + '#cache' => [ + 'keys' => ['test'], + 'contexts' => $contexts, + ], + ]; $element = $test_element; $element['#markup'] = 'content for user 1'; $output = \Drupal::service('renderer')->render($element); diff --git a/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php b/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php index 42ea009..616788d 100644 --- a/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Session/PermissionsHashGeneratorTest.php @@ -76,14 +76,16 @@ protected function setUp() { new Settings(array('hash_salt' => 'test')); - // The mocked super user account. + // The mocked super user account, with the same roles as Account 2. $this->account1 = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() - ->setMethods(array('id')) + ->setMethods(array('getRoles', 'id')) ->getMock(); $this->account1->expects($this->any()) ->method('id') ->willReturn(1); + $this->account1->expects($this->never()) + ->method('getRoles'); // Account 2: 'administrator' and 'authenticated' roles. $roles_1 = array('administrator', 'authenticated'); @@ -145,13 +147,15 @@ protected function setUp() { */ public function testGenerate() { // Ensure that the super user (user 1) always gets the same hash. - $this->assertSame('is-super-user', $this->permissionsHash->generate($this->account1)); + $super_user_hash = $this->permissionsHash->generate($this->account1); // 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. $updated_hash_2 = $this->permissionsHash->generate($this->account2Updated); $this->assertNotSame($hash_2, $updated_hash_2, 'Same user with updated roles generates different permissions hash.');