group-8.x-1.x-dev/src/Entity/Access/GroupAccessControlHandler.php
src/Entity/Access/GroupAccessControlHandler.php
<?php
namespace Drupal\group\Entity\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\group\Access\GroupAccessResult;
use Drupal\group\Entity\GroupInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Access controller for the Group entity.
*
* @see \Drupal\group\Entity\Group.
*/
class GroupAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
/**
* Map of revision operations.
*
* Keys contain revision operations, where values are an array containing the
* group permission and entity operation. The group permission is checked to
* see if you have access at all.
*
* Entity operation is used to determine additional access, e.g for the
* 'delete revision' operation, an account must also have access to the
* 'delete' operation on the same group.
*/
protected const REVISION_OPERATION_MAP = [
'view all revisions' => ['view all group revisions', 'view'],
'view revision' => ['view group revisions', 'view'],
'revert revision' => ['revert group revisions', 'update'],
'delete revision' => ['delete group revisions', 'delete'],
];
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
$instance = new static($entity_type);
$instance->entityTypeManager = $container->get('entity_type.manager');
return $instance;
}
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
assert($entity instanceof GroupInterface);
switch ($operation) {
case 'view':
if (!$entity->isPublished()) {
$access_result = GroupAccessResult::allowedIfHasGroupPermission($entity, $account, 'view any unpublished group');
if (!$access_result->isAllowed() && $account->isAuthenticated() && $account->id() === $entity->getOwnerId()) {
$access_result = GroupAccessResult::allowedIfHasGroupPermission($entity, $account, 'view own unpublished group')->cachePerUser();
}
}
else {
$access_result = GroupAccessResult::allowedIfHasGroupPermission($entity, $account, 'view group');
}
// The access result might change if group status changes.
return $access_result->addCacheableDependency($entity);
case 'update':
return GroupAccessResult::allowedIfHasGroupPermission($entity, $account, 'edit group');
case 'delete':
return GroupAccessResult::allowedIfHasGroupPermission($entity, $account, 'delete group');
}
[$revision_permission, $entity_operation] = static::REVISION_OPERATION_MAP[$operation] ?? [
NULL,
NULL,
];
// Revision operations.
if ($revision_permission) {
$cacheability = (new CacheableMetadata())->addCacheContexts(['user.group_permissions']);
// If user doesn't have the required permission, quit.
if (!$entity->hasPermission($revision_permission, $account)) {
return AccessResult::forbidden()->addCacheableDependency($cacheability);
}
// If the user has the view all revisions permission and this is the view
// all revisions operation then we can allow access.
if ($operation === 'view all revisions') {
return AccessResult::allowed()->addCacheableDependency($cacheability);
}
// If this is the default revision, return access denied for revert or
// delete operations. At this point, we need to add the entity as a cache
// dependency because if it changes, the result might change.
$cacheability->addCacheableDependency($entity);
if ($entity->isDefaultRevision() && ($operation === 'revert revision' || $operation === 'delete revision')) {
return AccessResult::forbidden()->addCacheableDependency($cacheability);
}
// First check the access to the default revision and, if the passed in
// group is not the default revision, check access to that too.
$storage = $this->entityTypeManager->getStorage('group');
$access = $this->access($storage->load($entity->id()), $entity_operation, $account, TRUE);
if (!$entity->isDefaultRevision()) {
$access = $access->andIf($this->access($entity, $entity_operation, $account, TRUE));
}
assert($access instanceof RefinableCacheableDependencyInterface);
return $access->addCacheableDependency($cacheability);
}
// The Group module's ideology is that if you want to do something to a
// group, you need Group to explicitly allow access or else the result will
// be forbidden. Having said that, if we do not support an operation yet,
// it's probably nicer to return neutral here. This way, any module that
// exposes new operations will work as intended AND NOT HAVE GROUP ACCESS
// CHECKS until Group specifically implements said operations.
return AccessResult::neutral();
}
/**
* {@inheritdoc}
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
return AccessResult::allowedIfHasPermission($account, 'create ' . $entity_bundle . ' group');
}
}
