og-8.x-1.x-dev/src/Cache/Context/OgPermissionsCacheContext.php
src/Cache/Context/OgPermissionsCacheContext.php
<?php
declare(strict_types=1);
namespace Drupal\og\Cache\Context;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\user\EntityOwnerInterface;
use Drupal\Core\PrivateKey;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\og\MembershipManagerInterface;
use Drupal\og\OgContextInterface;
use Drupal\og\OgMembershipInterface;
use Drupal\og\OgRoleInterface;
/**
* Defines a cache context service for the OG permissions of the current user.
*
* Cache context ID: 'og_permissions'.
*/
class OgPermissionsCacheContext implements CacheContextInterface {
/**
* The string to return when no context is found.
*/
const NO_CONTEXT = 'none';
/**
* Key used when the user has administrative permissions in a group.
*/
const ADMIN_PERMISSION = 'is-admin';
/**
* An array of cached cache context key hashes.
*
* @var string[]
*/
protected array $hashes = [];
public function __construct(
protected readonly AccountInterface $user,
protected readonly OgContextInterface $ogContext,
protected readonly MembershipManagerInterface $membershipManager,
protected readonly EntityTypeManagerInterface $entityTypeManager,
protected readonly PrivateKey $privateKey,
protected readonly ConfigFactoryInterface $configFactory,
) {}
/**
* {@inheritdoc}
*/
public static function getLabel() {
return new TranslatableMarkup('OG permissions');
}
/**
* {@inheritdoc}
*/
public function getContext() {
// Do not provide a cache context if there is no group in the current
// context.
$group = $this->ogContext->getGroup();
if (!$group instanceof ContentEntityInterface) {
return self::NO_CONTEXT;
}
$cache_id = implode(':', [$this->user->id(), $group->getEntityTypeId(), $group->bundle(), $group->id()]);
if (!isset($this->hashes[$cache_id])) {
$permissions = $this->getPermissions($group);
if (empty($permissions)) {
$this->hashes[$cache_id] = self::NO_CONTEXT;
}
else {
sort($permissions);
$this->hashes[$cache_id] = $this->hash(serialize($permissions));
}
}
return $this->hashes[$cache_id];
}
/**
* {@inheritdoc}
*/
public function getCacheableMetadata() {
return CacheableMetadata::createFromObject($this->configFactory->get('og.settings'));
}
/**
* Returns the OG permissions for the current user in the given group.
*/
protected function getPermissions(ContentEntityInterface $group): array {
// Anonymous users have no OG permissions.
if (!$this->user->isAuthenticated()) {
return [];
}
if ($this->user->hasPermission('administer organic groups')) {
// Global OG admin.
return [self::ADMIN_PERMISSION];
}
$config = $this->configFactory->get('og.settings');
// Group owners can receive full permissions if configured.
if ($config->get('group_manager_full_access') && $group instanceof EntityOwnerInterface && $group->getOwnerId() == $this->user->id()) {
return [self::ADMIN_PERMISSION];
}
// If the user is blocked they have no permissions.
if ($this->membershipManager->isMember($group, $this->user->id(), [OgMembershipInterface::STATE_BLOCKED])) {
return [];
}
// Gather permissions from the user's membership.
$membership = $this->membershipManager->getMembership($group, $this->user->id());
if (!empty($membership)) {
$permissions = [];
foreach ($membership->getRoles() as $role) {
if ($role->isAdmin()) {
// Role is admin, so we can return early.
return [self::ADMIN_PERMISSION];
}
$permissions = array_merge($permissions, $role->getPermissions());
}
return array_unique($permissions);
}
// Authenticated non-members inherit permissions from the group's
// non-member role.
$role_id = "{$group->getEntityTypeId()}-{$group->bundle()}-" . OgRoleInterface::ANONYMOUS;
$role = $this->entityTypeManager->getStorage('og_role')->load($role_id);
return $role?->getPermissions() ?? [];
}
/**
* Hashes the given string.
*
* @param string $identifier
* The string to be hashed.
*
* @return string
* The hash.
*/
protected function hash(string $identifier): string {
return hash('sha256', $this->privateKey->get() . Settings::getHashSalt() . $identifier);
}
}
