Verified Commit 26f41796 authored by godotislate's avatar godotislate
Browse files

refactor: #2473041 Restructure node access grant behavior into the node access handler

By: xjm
By: acbramley
By: joey-santiago
By: alexpott
By: steven jones
By: nicxvan
By: godotislate
parent 8799b9f1
Loading
Loading
Loading
Loading
Loading
+9 −6
Changes for core/modules/node/node.module: 9 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\node\Hook\NodeThemeHooks;
use Drupal\node\NodeGrantsHelper;
use Drupal\node\NodeInterface;
use Drupal\node\NodeTypeInterface;

@@ -303,14 +304,16 @@ function template_preprocess_node(&$variables): void {
 * @return array
 *   An associative array in which the keys are realms, and the values are
 *   arrays of grants for those realms.
 *
 * @deprecated in drupal:11.4.0 and is removed from drupal:13.0.0. Use
 *    \Drupal::service('Drupal\node\NodeGrantsHelper')->nodeAccessGrants()
 *    instead.
 *
 * @see https://www.drupal.org/node/3578055
 */
function node_access_grants($operation, AccountInterface $account) {
  // Fetch node access grants from other modules.
  $grants = \Drupal::moduleHandler()->invokeAll('node_grants', [$account, $operation]);
  // Allow modules to alter the assigned grants.
  \Drupal::moduleHandler()->alter('node_grants', $grants, $account, $operation);

  return array_merge(['all' => [0]], $grants);
  @trigger_error('node_access_grants() is deprecated in drupal:11.4.0 and is removed from drupal:13.0.0. Use  \Drupal::service(\'Drupal\node\NodeGrantsHelper\')->nodeAccessGrants(). See https://www.drupal.org/node/3578055', E_USER_DEPRECATED);
  return \Drupal::service(NodeGrantsHelper::class)->nodeAccessGrants($operation, $account);
}

/**
+4 −1
Changes for core/modules/node/node.services.yml: 4 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@ services:
    autoconfigure: true
  node.route_subscriber:
    class: Drupal\node\Routing\RouteSubscriber
  Drupal\node\NodeGrantsHelper:
    class: Drupal\node\NodeGrantsHelper
    autowire: true
  node.grant_storage:
    class: Drupal\node\NodeGrantDatabaseStorage
    autowire: true
@@ -38,7 +41,7 @@ services:
      - { name: paramconverter }
  cache_context.user.node_grants:
    class: Drupal\node\Cache\NodeAccessGrantsCacheContext
    arguments: ['@current_user', '@entity_type.manager']
    arguments: ['@current_user', '@entity_type.manager', '@Drupal\node\NodeGrantsHelper']
    tags:
      - { name: cache.context }
  node.node_route_context:
+13 −1
Changes for core/modules/node/src/Cache/NodeAccessGrantsCacheContext.php: 13 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
use Drupal\Core\Cache\Context\UserCacheContextBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeGrantsHelper;

/**
 * Defines the node access view cache context service.
@@ -22,15 +23,26 @@
 */
class NodeAccessGrantsCacheContext extends UserCacheContextBase implements CalculatedCacheContextInterface {

  /**
   * The node grants helper service.
   */
  protected NodeGrantsHelper $nodeGrantsHelper;

  public function __construct(
    AccountInterface $user,
    protected ?EntityTypeManagerInterface $entityTypeManager = NULL,
    ?NodeGrantsHelper $nodeGrantsHelper = NULL,
  ) {
    parent::__construct($user);
    if (!$entityTypeManager) {
      @trigger_error('Calling NodeAccessGrantsCacheContext::__construct() without the $entityTypeManager argument is deprecated in drupal:11.3.0 and the $entityTypeManager argument will be required in drupal:12.0.0. See https://www.drupal.org/node/3038909', E_USER_DEPRECATED);
      $this->entityTypeManager = \Drupal::entityTypeManager();
    }
    if (!$nodeGrantsHelper) {
      @trigger_error('Calling NodeAccessGrantsCacheContext::__construct() without the $nodeGrantsHelper argument is deprecated in drupal:11.4.0 and the $nodeGrantsHelper argument will be required in drupal:12.0.0. See https://www.drupal.org/node/3578055', E_USER_DEPRECATED);
      $nodeGrantsHelper = \Drupal::service(NodeGrantsHelper::class);
    }
    $this->nodeGrantsHelper = $nodeGrantsHelper;
  }

  /**
@@ -87,7 +99,7 @@ protected function checkNodeGrants($operation) {
      return 'view.all';
    }

    $grants = node_access_grants($operation, $this->user);
    $grants = $this->nodeGrantsHelper->nodeAccessGrants($operation, $this->user);
    $grants_context_parts = [];
    foreach ($grants as $realm => $gids) {
      $grants_context_parts[] = $realm . ':' . implode(',', $gids);
+14 −3
Changes for core/modules/node/src/NodeGrantDatabaseStorage.php: 14 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -25,17 +25,28 @@ class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
   */
  protected readonly bool $hasNodeGrantsImplementations;

  /**
   * The node grants helper service.
   */
  protected NodeGrantsHelper $nodeGrantsHelper;

  public function __construct(
    protected readonly Connection $database,
    protected readonly ModuleHandlerInterface $moduleHandler,
    protected readonly LanguageManagerInterface $languageManager,
    #[Autowire(service: 'node.view_all_nodes_memory_cache')]
    protected MemoryCacheInterface $memoryCache,
    ?NodeGrantsHelper $nodeGrantsHelper,
  ) {
    if (!$memoryCache) {
      @trigger_error('Calling NodeGrantDatabaseStorage::__construct() without the $memoryCache argument is deprecated in drupal:11.3.0 and the $memoryCache argument will be required in drupal:12.0.0. See https://www.drupal.org/node/3038909', E_USER_DEPRECATED);
      $this->memoryCache = \Drupal::service('node.view_all_nodes_memory_cache');
    }
    if (!$nodeGrantsHelper) {
      @trigger_error('Calling NodeGrantDatabaseStorage::__construct() without the $nodeGrantsHelper argument is deprecated in drupal:11.4.0 and the $nodeGrantsHelper argument will be required in drupal:12.0.0. See https://www.drupal.org/node/3578055', E_USER_DEPRECATED);
      $nodeGrantsHelper = \Drupal::service(NodeGrantsHelper::class);
    }
    $this->nodeGrantsHelper = $nodeGrantsHelper;
    $this->hasNodeGrantsImplementations = $this->moduleHandler->hasImplementations('node_grants');
  }

@@ -91,7 +102,7 @@ public function access(NodeInterface $node, $operation, AccountInterface $accoun
    $query->condition($nids);
    $query->range(0, 1);

    $grants = $this->buildGrantsQueryCondition(node_access_grants($operation, $account));
    $grants = $this->buildGrantsQueryCondition($this->nodeGrantsHelper->nodeAccessGrants($operation, $account));

    if (count($grants) > 0) {
      $query->condition($grants);
@@ -136,7 +147,7 @@ public function checkAll(AccountInterface $account) {
      ->condition('nid', 0)
      ->condition('grant_view', 1, '>=');

    $grants = $this->buildGrantsQueryCondition(node_access_grants('view', $account));
    $grants = $this->buildGrantsQueryCondition($this->nodeGrantsHelper->nodeAccessGrants('view', $account));

    if (count($grants) > 0) {
      $query->condition($grants);
@@ -157,7 +168,7 @@ public function alterQuery($query, array $tables, $operation, AccountInterface $
    // Find all instances of the base table being joined which could appear
    // more than once in the query, and could be aliased. Join each one to
    // the node_access table.
    $grants = node_access_grants($operation, $account);
    $grants = $this->nodeGrantsHelper->nodeAccessGrants($operation, $account);
    // If any grant exists for the specified user, then user has access to the
    // node for the specified operation.
    $grant_conditions = $this->buildGrantsQueryCondition($grants);
+50 −0
Changes for core/modules/node/src/NodeGrantsHelper.php: 50 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\node;

use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Defines some helpers for the node access control system relating to grants.
 *
 * @ingroup node_access
 */
class NodeGrantsHelper {

  public function __construct(
    protected readonly ModuleHandlerInterface $moduleHandler,
  ) {}

  /**
   * Fetches an array of permission IDs granted to the given user ID.
   *
   * The implementation here provides only the universal "all" grant. A node
   * access module should implement hook_node_grants() to provide a grant list
   * for the user.
   *
   * After the default grants have been loaded, we allow modules to alter the
   * grants array by reference. This hook allows for complex business logic to
   * be applied when integrating multiple node access modules.
   *
   * @param string $operation
   *   The operation that the user is trying to perform.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The account object for the user performing the operation.
   *
   * @return array
   *   An associative array in which the keys are realms, and the values are
   *   arrays of grants for those realms.
   */
  public function nodeAccessGrants(string $operation, AccountInterface $account): array {
    // Fetch node access grants from other modules.
    $grants = $this->moduleHandler->invokeAll('node_grants', [$account, $operation]);
    // Allow modules to alter the assigned grants.
    $this->moduleHandler->alter('node_grants', $grants, $account, $operation);

    return array_merge(['all' => [0]], $grants);
  }

}
Loading