class EntityAccessChecker
Checks access to entities.
JSON:API needs to check access to every single entity type. Some entity types have non-standard access checking logic. This class centralizes entity access checking logic.
@internal JSON:API maintains no PHP API. The API is the HTTP API. This class may change at any time and could break any dependencies on it.
Hierarchy
- class \Drupal\jsonapi\Access\EntityAccessChecker
 
Expanded class hierarchy of EntityAccessChecker
See also
https://www.drupal.org/project/drupal/issues/3032787
2 files declare their use of EntityAccessChecker
- EntityResource.php in core/
modules/ jsonapi/ src/ Controller/ EntityResource.php  - IncludeResolver.php in core/
modules/ jsonapi/ src/ IncludeResolver.php  
1 string reference to 'EntityAccessChecker'
- jsonapi.services.yml in core/
modules/ jsonapi/ jsonapi.services.yml  - core/modules/jsonapi/jsonapi.services.yml
 
1 service uses EntityAccessChecker
- jsonapi.entity_access_checker in core/
modules/ jsonapi/ jsonapi.services.yml  - Drupal\jsonapi\Access\EntityAccessChecker
 
File
- 
              core/
modules/ jsonapi/ src/ Access/ EntityAccessChecker.php, line 33  
Namespace
Drupal\jsonapi\AccessView source
class EntityAccessChecker {
  
  /**
   * The JSON:API resource type repository.
   *
   * @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface
   */
  protected $resourceTypeRepository;
  
  /**
   * The router.
   *
   * @var \Symfony\Component\Routing\RouterInterface
   */
  protected $router;
  
  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;
  
  /**
   * The entity repository.
   *
   * @var \Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;
  
  /**
   * The latest revision check service.
   *
   * This will be NULL unless the content_moderation module is installed. This
   * is a temporary measure. JSON:API should not need to be aware of the
   * Content Moderation module.
   *
   * @var \Drupal\content_moderation\Access\LatestRevisionCheck
   */
  protected $latestRevisionCheck = NULL;
  
  /**
   * EntityAccessChecker constructor.
   *
   * @param \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface $resource_type_repository
   *   The JSON:API resource type repository.
   * @param \Symfony\Component\Routing\RouterInterface $router
   *   The router.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
   *   The entity repository.
   */
  public function __construct(ResourceTypeRepositoryInterface $resource_type_repository, RouterInterface $router, AccountInterface $account, EntityRepositoryInterface $entity_repository) {
    $this->resourceTypeRepository = $resource_type_repository;
    $this->router = $router;
    $this->currentUser = $account;
    $this->entityRepository = $entity_repository;
  }
  
  /**
   * Sets the media revision access check service.
   *
   * This is only called when content_moderation module is installed.
   *
   * @param \Drupal\content_moderation\Access\LatestRevisionCheck $latest_revision_check
   *   The latest revision access check service provided by the
   *   content_moderation module.
   *
   * @see self::$latestRevisionCheck
   */
  public function setLatestRevisionCheck(LatestRevisionCheck $latest_revision_check) {
    $this->latestRevisionCheck = $latest_revision_check;
  }
  
  /**
   * Get the object to normalize and the access based on the provided entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity to test access for.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   (optional) The account with which access should be checked. Defaults to
   *   the current user.
   *
   * @return \Drupal\jsonapi\JsonApiResource\ResourceObject|\Drupal\jsonapi\JsonApiResource\LabelOnlyResourceObject|\Drupal\jsonapi\Exception\EntityAccessDeniedHttpException
   *   The ResourceObject, a LabelOnlyResourceObject or an
   *   EntityAccessDeniedHttpException object if neither is accessible. All
   *   three possible return values carry the access result cacheability.
   */
  public function getAccessCheckedResourceObject(EntityInterface $entity, AccountInterface $account = NULL) {
    $account = $account ?: $this->currentUser;
    $resource_type = $this->resourceTypeRepository
      ->get($entity->getEntityTypeId(), $entity->bundle());
    $entity = $this->entityRepository
      ->getTranslationFromContext($entity, NULL, [
      'operation' => 'entity_upcast',
    ]);
    $access = $this->checkEntityAccess($entity, 'view', $account);
    $entity->addCacheableDependency($access);
    if (!$access->isAllowed()) {
      // If this is the default revision or the entity is not revisionable, then
      // check access to the entity label. Revision support is all or nothing.
      if (!$entity->getEntityType()
        ->isRevisionable() || $entity->isDefaultRevision()) {
        $label_access = $entity->access('view label', NULL, TRUE);
        $entity->addCacheableDependency($label_access);
        if ($label_access->isAllowed()) {
          return LabelOnlyResourceObject::createFromEntity($resource_type, $entity);
        }
        $access = $access->orIf($label_access);
      }
      return new EntityAccessDeniedHttpException($entity, $access, '/data', 'The current user is not allowed to GET the selected resource.');
    }
    return ResourceObject::createFromEntity($resource_type, $entity);
  }
  
  /**
   * Checks access to the given entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity for which access should be evaluated.
   * @param string $operation
   *   The entity operation for which access should be evaluated.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   (optional) The account with which access should be checked. Defaults to
   *   the current user.
   *
   * @return \Drupal\Core\Access\AccessResultInterface|\Drupal\Core\Access\AccessResultReasonInterface
   *   The access check result.
   */
  public function checkEntityAccess(EntityInterface $entity, $operation, AccountInterface $account) {
    $access = $entity->access($operation, $account, TRUE);
    if ($entity->getEntityType()
      ->isRevisionable()) {
      $access = AccessResult::neutral()->addCacheContexts([
        'url.query_args:' . JsonApiSpec::VERSION_QUERY_PARAMETER,
      ])
        ->orIf($access);
      if (!$entity->isDefaultRevision()) {
        assert($operation === 'view', 'JSON:API does not yet support mutable operations on revisions.');
        $revision_access = $this->checkRevisionViewAccess($entity, $account);
        $access = $access->andIf($revision_access);
        // The revision access reason should trump the primary access reason.
        if (!$access->isAllowed()) {
          $reason = $access instanceof AccessResultReasonInterface ? $access->getReason() : '';
          $access->setReason(trim('The user does not have access to the requested version. ' . $reason));
        }
      }
    }
    return $access;
  }
  
  /**
   * Checks access to the given revision entity.
   *
   * This should only be called for non-default revisions.
   *
   * There is no standardized API for revision access checking in Drupal core
   * and this method shims that missing API.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The revised entity for which to check access.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   (optional) The account with which access should be checked. Defaults to
   *   the current user.
   *
   * @return \Drupal\Core\Access\AccessResultInterface|\Drupal\Core\Access\AccessResultReasonInterface
   *   The access check result.
   */
  protected function checkRevisionViewAccess(EntityInterface $entity, AccountInterface $account) {
    assert($entity instanceof RevisionableInterface);
    assert(!$entity->isDefaultRevision(), 'It is not necessary to check revision access when the entity is the default revision.');
    $entity_type = $entity->getEntityType();
    $access = $entity->access('view all revisions', $account, TRUE);
    // Apply content_moderation's additional access logic.
    // @see \Drupal\content_moderation\Access\LatestRevisionCheck::access()
    if ($entity_type->getLinkTemplate('latest-version') && $entity->isLatestRevision() && isset($this->latestRevisionCheck)) {
      // The latest revision access checker only expects to be invoked by the
      // routing system, which makes it necessary to fake a route match.
      $routes = $this->router
        ->getRouteCollection();
      $resource_type = $this->resourceTypeRepository
        ->get($entity->getEntityTypeId(), $entity->bundle());
      $route_name = sprintf('jsonapi.%s.individual', $resource_type->getTypeName());
      $route = $routes->get($route_name);
      $route->setOption('_content_moderation_entity_type', 'entity');
      $route_match = new RouteMatch($route_name, $route, [
        'entity' => $entity,
      ], [
        'entity' => $entity->uuid(),
      ]);
      $moderation_access_result = $this->latestRevisionCheck
        ->access($route, $route_match, $account);
      $access = $access->andIf($moderation_access_result);
    }
    return $access;
  }
}
Members
| Title Sort descending | Modifiers | Object type | Summary | 
|---|---|---|---|
| EntityAccessChecker::$currentUser | protected | property | The current user. | 
| EntityAccessChecker::$entityRepository | protected | property | The entity repository. | 
| EntityAccessChecker::$latestRevisionCheck | protected | property | The latest revision check service. | 
| EntityAccessChecker::$resourceTypeRepository | protected | property | The JSON:API resource type repository. | 
| EntityAccessChecker::$router | protected | property | The router. | 
| EntityAccessChecker::checkEntityAccess | public | function | Checks access to the given entity. | 
| EntityAccessChecker::checkRevisionViewAccess | protected | function | Checks access to the given revision entity. | 
| EntityAccessChecker::getAccessCheckedResourceObject | public | function | Get the object to normalize and the access based on the provided entity. | 
| EntityAccessChecker::setLatestRevisionCheck | public | function | Sets the media revision access check service. | 
| EntityAccessChecker::__construct | public | function | EntityAccessChecker constructor. | 
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.