You are here

protected static function TemporaryQueryGuard::getCommentAccessCondition in JSON:API 8

Same name and namespace in other branches
  1. 8.2 src/Access/TemporaryQueryGuard.php \Drupal\jsonapi\Access\TemporaryQueryGuard::getCommentAccessCondition()

Gets an access condition for a comment entity.

Unlike all other core entity types, Comment entities' access control depends on access to a referenced entity. More challenging yet, that entity reference field may target different entity types depending on the comment bundle. This makes the query access conditions sufficiently complex to merit a dedicated method.

Parameters

\Drupal\Core\Entity\EntityTypeInterface $comment_entity_type: The comment entity type object.

\Drupal\Core\Session\AccountInterface $current_user: The current user.

\Drupal\Core\Cache\CacheableMetadata $cacheability: Collects cacheability for the query.

int $depth: Internal use only. The recursion depth. It is possible to have comments on comments, but since comment access is dependent on access to the entity on which they live, this method can recurse endlessly.

Return value

\Drupal\jsonapi\Query\EntityConditionGroup|null An EntityConditionGroup or NULL if no conditions need to be applied to secure an entity query.

1 call to TemporaryQueryGuard::getCommentAccessCondition()
TemporaryQueryGuard::getAccessCondition in src/Access/TemporaryQueryGuard.php
Gets an EntityConditionGroup that filters out inaccessible entities.

File

src/Access/TemporaryQueryGuard.php, line 500

Class

TemporaryQueryGuard
Adds sufficient access control to collection queries.

Namespace

Drupal\jsonapi\Access

Code

protected static function getCommentAccessCondition(EntityTypeInterface $comment_entity_type, AccountInterface $current_user, CacheableMetadata $cacheability, $depth = 1) {

  // If a comment is assigned to another entity or author the cache needs to
  // be invalidated.
  $cacheability
    ->addCacheTags($comment_entity_type
    ->getListCacheTags());

  // Constructs a big EntityConditionGroup which will filter comments based on
  // the current user's access to the entities on which each comment lives.
  // This is especially complex because comments of different bundles can
  // live on entities of different entity types.
  $comment_entity_type_id = $comment_entity_type
    ->id();
  $field_map = static::$fieldManager
    ->getFieldMapByFieldType('entity_reference');
  assert(isset($field_map[$comment_entity_type_id]['entity_id']['bundles']), 'Every comment has an `entity_id` field.');
  $bundle_ids_by_target_entity_type_id = [];
  foreach ($field_map[$comment_entity_type_id]['entity_id']['bundles'] as $bundle_id) {
    $field_definitions = static::$fieldManager
      ->getFieldDefinitions($comment_entity_type_id, $bundle_id);
    $commented_entity_field_definition = $field_definitions['entity_id'];

    // Each commented entity field definition has a setting which indicates
    // the entity type of the commented entity reference field. This differs
    // per bundle.
    $target_entity_type_id = $commented_entity_field_definition
      ->getSetting('target_type');
    $bundle_ids_by_target_entity_type_id[$target_entity_type_id][] = $bundle_id;
  }
  $bundle_specific_access_conditions = [];
  foreach ($bundle_ids_by_target_entity_type_id as $target_entity_type_id => $bundle_ids) {

    // Construct a field specifier prefix which targets the commented entity.
    $condition_field_prefix = "entity_id.entity:{$target_entity_type_id}";

    // Ensure that for each possible commented entity type (which varies per
    // bundle), a condition is created that restricts access based on access
    // to the commented entity.
    $bundle_condition = new EntityCondition($comment_entity_type
      ->getKey('bundle'), $bundle_ids, 'IN');

    // Comments on comments can create an infinite recursion! If the target
    // entity type ID is comment, we need special behavior.
    if ($target_entity_type_id === $comment_entity_type_id) {
      $nested_comment_condition = $depth <= 3 ? static::getCommentAccessCondition($comment_entity_type, $current_user, $cacheability, $depth + 1) : static::alwaysFalse($comment_entity_type);
      $prefixed_comment_condition = static::addConditionFieldPrefix($nested_comment_condition, $condition_field_prefix);
      $bundle_specific_access_conditions[$target_entity_type_id] = new EntityConditionGroup('AND', [
        $bundle_condition,
        $prefixed_comment_condition,
      ]);
    }
    else {
      $target_condition = static::getAccessCondition($target_entity_type_id, $cacheability);
      $bundle_specific_access_conditions[$target_entity_type_id] = !is_null($target_condition) ? new EntityConditionGroup('AND', [
        $bundle_condition,
        static::addConditionFieldPrefix($target_condition, $condition_field_prefix),
      ]) : $bundle_condition;
    }
  }

  // This condition ensures that the user is only permitted to see the
  // comments for which the user is also able to view the entity on which each
  // comment lives.
  $commented_entity_condition = new EntityConditionGroup('OR', array_values($bundle_specific_access_conditions));
  return $commented_entity_condition;
}