3 namespace Drupal\Core\Session;
5 use Drupal\Core\PrivateKey;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\Cache\CacheBackendInterface;
8 use Drupal\Core\Site\Settings;
11 * Generates and caches the permissions hash for a user.
13 class PermissionsHashGenerator implements PermissionsHashGeneratorInterface {
16 * The private key service.
18 * @var \Drupal\Core\PrivateKey
20 protected $privateKey;
23 * The cache backend interface to use for the persistent cache.
25 * @var \Drupal\Core\Cache\CacheBackendInterface
30 * The cache backend interface to use for the static cache.
32 * @var \Drupal\Core\Cache\CacheBackendInterface
37 * Constructs a PermissionsHashGenerator object.
39 * @param \Drupal\Core\PrivateKey $private_key
40 * The private key service.
41 * @param \Drupal\Core\Cache\CacheBackendInterface $cache
42 * The cache backend interface to use for the persistent cache.
43 * @param \Drupal\Core\Cache\CacheBackendInterface $static
44 * The cache backend interface to use for the static cache.
46 public function __construct(PrivateKey $private_key, CacheBackendInterface $cache, CacheBackendInterface $static) {
47 $this->privateKey = $private_key;
48 $this->cache = $cache;
49 $this->static = $static;
55 * Cached by role, invalidated whenever permissions change.
57 public function generate(AccountInterface $account) {
58 // User 1 is the super user, and can always access all permissions. Use a
59 // different, unique identifier for the hash.
60 if ($account->id() == 1) {
61 return $this->hash('is-super-user');
64 $sorted_roles = $account->getRoles();
66 $role_list = implode(',', $sorted_roles);
67 $cid = "user_permissions_hash:$role_list";
68 if ($static_cache = $this->static->get($cid)) {
69 return $static_cache->data;
72 $tags = Cache::buildTags('config:user.role', $sorted_roles, '.');
73 if ($cache = $this->cache->get($cid)) {
74 $permissions_hash = $cache->data;
77 $permissions_hash = $this->doGenerate($sorted_roles);
78 $this->cache->set($cid, $permissions_hash, Cache::PERMANENT, $tags);
80 $this->static->set($cid, $permissions_hash, Cache::PERMANENT, $tags);
83 return $permissions_hash;
87 * Generates a hash that uniquely identifies the user's permissions.
89 * @param string[] $roles
93 * The permissions hash.
95 protected function doGenerate(array $roles) {
96 // @todo Once Drupal gets rid of user_role_permissions(), we should be able
97 // to inject the user role controller and call a method on that instead.
98 $permissions_by_role = user_role_permissions($roles);
99 foreach ($permissions_by_role as $role => $permissions) {
101 // Note that for admin roles (\Drupal\user\RoleInterface::isAdmin()), the
102 // permissions returned will be empty ($permissions = []). Therefore the
103 // presence of the role ID as a key in $permissions_by_role is essential
104 // to ensure that the hash correctly recognizes admin roles. (If the hash
105 // was based solely on the union of $permissions, the admin roles would
106 // effectively be no-ops, allowing for hash collisions.)
107 $permissions_by_role[$role] = $permissions;
109 return $this->hash(serialize($permissions_by_role));
113 * Hashes the given string.
115 * @param string $identifier
116 * The string to be hashed.
121 protected function hash($identifier) {
122 return hash('sha256', $this->privateKey->get() . Settings::getHashSalt() . $identifier);