Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. EntityAccessChecker.php

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

jsonapi.api.php

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\Access
View 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.

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal