access_policy-1.0.x-dev/src/AccessPolicyEntityAccessControlHandler.php
src/AccessPolicyEntityAccessControlHandler.php
<?php namespace Drupal\access_policy; use Drupal\Core\Access\AccessResult; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Access controller for content entities assigned an AccessPolicy. */ class AccessPolicyEntityAccessControlHandler implements ContainerInjectionInterface { /** * The access policy validator. * * @var \Drupal\access_policy\AccessPolicyValidator */ protected $accessPolicyValidator; /** * The access policy selection service. * * @var \Drupal\access_policy\AccessPolicySelectionInterface */ protected $accessPolicySelection; /** * Access policy information. * * @var \Drupal\access_policy\AccessPolicyInformation */ protected $accessPolicyInformation; /** * The access rule manager. * * @var \Drupal\access_policy\AccessPolicyHandlerManager */ protected $accessRuleManager; /** * The operation plugin manager. * * @var \Drupal\access_policy\AccessPolicyOperationPluginManager */ protected $operationPluginManager; /** * Constructs a AccessPolicyEntityAccessControlHandler object. * * @param \Drupal\access_policy\AccessPolicyValidator $access_policy_validator * The access policy validator. * @param \Drupal\access_policy\AccessPolicySelectionInterface $access_policy_selection * The access policy selection handler. * @param \Drupal\access_policy\AccessPolicyInformation $policy_information * The access policy information service. * @param \Drupal\access_policy\AccessPolicyHandlerManager $access_rule_manager * The access rule manager. * @param \Drupal\access_policy\AccessPolicyOperationPluginManager $operation_plugin_manager * The operation plugin manager. */ public function __construct(AccessPolicyValidator $access_policy_validator, AccessPolicySelectionInterface $access_policy_selection, AccessPolicyInformation $policy_information, AccessPolicyHandlerManager $access_rule_manager, AccessPolicyOperationPluginManager $operation_plugin_manager) { $this->accessPolicyValidator = $access_policy_validator; $this->accessPolicySelection = $access_policy_selection; $this->accessPolicyInformation = $policy_information; $this->accessRuleManager = $access_rule_manager; $this->operationPluginManager = $operation_plugin_manager; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('access_policy.validator'), $container->get('access_policy.selection'), $container->get('access_policy.information'), $container->get('plugin.manager.access_policy.access_rule'), $container->get('plugin.manager.access_policy_operation'), ); } /** * Check access qualifications. * * @param \Drupal\Core\Entity\EntityInterface $entity * The content entity. * @param \Drupal\Core\Session\AccountInterface $account * The current user. * @param string $operation * The current operation. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function access(EntityInterface $entity, AccountInterface $account, string $operation) { if (!$this->accessPolicyInformation->hasEnabledForEntitiesOfEntityType($entity->getEntityType())) { return AccessResult::neutral(); } $operation = $this->getCurrentOperation($entity, $operation); // If assigning an access policy confirm that the user has policies they // can assign first. if ($operation == 'manage_access') { $access = $this->accessPolicySelection->validateAssignAccessPolicy($entity, $account); if (!$access) { return $this->accessResult($entity, $operation, $access); } } $access = $this->accessPolicyValidator->validate($entity, $account, $operation); $violations = $this->accessPolicyValidator->getViolations($entity); return $this->accessResult($entity, $operation, $access, $violations); } /** * Final access result based on operation. * * @param \Drupal\Core\Entity\EntityInterface $entity * The current entity. * @param string $operation * The operation. * @param bool $allowed * TRUE if allowed; FALSE otherwise. * @param array $violations * Array of violation messages. * * @return \Drupal\Core\Access\AccessResultInterface * Access result. */ private function accessResult(EntityInterface $entity, $operation, $allowed, array $violations = []) { $contexts = $this->getCacheContexts($entity); $this->addCacheableDependencies($entity); if (!$allowed) { $reason = $this->formatViolationReason($violations); return AccessResult::forbidden($reason)->addCacheableDependency($entity)->addCacheContexts($contexts); } // The view unpublished operation requires an allowed result otherwise it // will return forbidden. All other operations work with neutral. switch ($operation) { case 'manage_access': return AccessResult::allowed()->addCacheableDependency($entity)->addCacheContexts($contexts); default; return AccessResult::neutral()->addCacheableDependency($entity)->addCacheContexts($contexts); } } /** * Add any cacheable dependencies from the access policies. * * This ensures that the cache gets invalidated when the access policy is * updated. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. */ protected function addCacheableDependencies(EntityInterface $entity) { $policies = $entity->get('access_policy')->referencedEntities(); foreach ($policies as $policy) { $entity->addCacheableDependency($policy); } } /** * Get the cache context based on the entity's access policy. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * * @return array * The cache context. */ protected function getCacheContexts(EntityInterface $entity) { return $this->accessPolicyValidator->getCacheContexts($entity); } /** * Format the violation reason string. * * @param array $violations * Array of violations. * * @return string * The violation reason. */ private function formatViolationReason(array $violations) { $reason = []; foreach ($violations as $messages) { foreach ($messages as $message) { $reason[] = $message; } } return implode(', ', $reason); } /** * Get the current operation. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * @param string $operation * The operation. * * @return string * The current operation. */ private function getCurrentOperation(EntityInterface $entity, $operation) { $plugin = $this->operationPluginManager->getCurrentOperation($entity, $operation); if ($plugin) { return $plugin->getPluginId(); } return $operation; } }