PermissionsHashGenerator.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. <?php
  2. namespace Drupal\Core\Session;
  3. use Drupal\Core\PrivateKey;
  4. use Drupal\Core\Cache\Cache;
  5. use Drupal\Core\Cache\CacheBackendInterface;
  6. use Drupal\Core\Site\Settings;
  7. /**
  8. * Generates and caches the permissions hash for a user.
  9. */
  10. class PermissionsHashGenerator implements PermissionsHashGeneratorInterface {
  11. /**
  12. * The private key service.
  13. *
  14. * @var \Drupal\Core\PrivateKey
  15. */
  16. protected $privateKey;
  17. /**
  18. * The cache backend interface to use for the persistent cache.
  19. *
  20. * @var \Drupal\Core\Cache\CacheBackendInterface
  21. */
  22. protected $cache;
  23. /**
  24. * The cache backend interface to use for the static cache.
  25. *
  26. * @var \Drupal\Core\Cache\CacheBackendInterface
  27. */
  28. protected $static;
  29. /**
  30. * Constructs a PermissionsHashGenerator object.
  31. *
  32. * @param \Drupal\Core\PrivateKey $private_key
  33. * The private key service.
  34. * @param \Drupal\Core\Cache\CacheBackendInterface $cache
  35. * The cache backend interface to use for the persistent cache.
  36. * @param \Drupal\Core\Cache\CacheBackendInterface $static
  37. * The cache backend interface to use for the static cache.
  38. */
  39. public function __construct(PrivateKey $private_key, CacheBackendInterface $cache, CacheBackendInterface $static) {
  40. $this->privateKey = $private_key;
  41. $this->cache = $cache;
  42. $this->static = $static;
  43. }
  44. /**
  45. * {@inheritdoc}
  46. *
  47. * Cached by role, invalidated whenever permissions change.
  48. */
  49. public function generate(AccountInterface $account) {
  50. // User 1 is the super user, and can always access all permissions. Use a
  51. // different, unique identifier for the hash.
  52. if ($account->id() == 1) {
  53. return $this->hash('is-super-user');
  54. }
  55. $sorted_roles = $account->getRoles();
  56. sort($sorted_roles);
  57. $role_list = implode(',', $sorted_roles);
  58. $cid = "user_permissions_hash:$role_list";
  59. if ($static_cache = $this->static->get($cid)) {
  60. return $static_cache->data;
  61. }
  62. else {
  63. $tags = Cache::buildTags('config:user.role', $sorted_roles, '.');
  64. if ($cache = $this->cache->get($cid)) {
  65. $permissions_hash = $cache->data;
  66. }
  67. else {
  68. $permissions_hash = $this->doGenerate($sorted_roles);
  69. $this->cache->set($cid, $permissions_hash, Cache::PERMANENT, $tags);
  70. }
  71. $this->static->set($cid, $permissions_hash, Cache::PERMANENT, $tags);
  72. }
  73. return $permissions_hash;
  74. }
  75. /**
  76. * Generates a hash that uniquely identifies the user's permissions.
  77. *
  78. * @param string[] $roles
  79. * The user's roles.
  80. *
  81. * @return string
  82. * The permissions hash.
  83. */
  84. protected function doGenerate(array $roles) {
  85. // @todo Once Drupal gets rid of user_role_permissions(), we should be able
  86. // to inject the user role controller and call a method on that instead.
  87. $permissions_by_role = user_role_permissions($roles);
  88. foreach ($permissions_by_role as $role => $permissions) {
  89. sort($permissions);
  90. // Note that for admin roles (\Drupal\user\RoleInterface::isAdmin()), the
  91. // permissions returned will be empty ($permissions = []). Therefore the
  92. // presence of the role ID as a key in $permissions_by_role is essential
  93. // to ensure that the hash correctly recognizes admin roles. (If the hash
  94. // was based solely on the union of $permissions, the admin roles would
  95. // effectively be no-ops, allowing for hash collisions.)
  96. $permissions_by_role[$role] = $permissions;
  97. }
  98. return $this->hash(serialize($permissions_by_role));
  99. }
  100. /**
  101. * Hashes the given string.
  102. *
  103. * @param string $identifier
  104. * The string to be hashed.
  105. *
  106. * @return string
  107. * The hash.
  108. */
  109. protected function hash($identifier) {
  110. return hash('sha256', $this->privateKey->get() . Settings::getHashSalt() . $identifier);
  111. }
  112. }