From 2ea32d6cc935c8bcbdd54fb150e77852c7ba4a40 Mon Sep 17 00:00:00 2001 From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org> Date: Wed, 11 Nov 2015 11:43:46 +0000 Subject: [PATCH] Issue #2557815 by Wim Leers, effulgentsia, Fabianx, dawehner: Automatically bubble the "user.node_grants:$op" cache context in node_query_node_access_alter() --- core/modules/node/node.module | 8 +++ .../src/Tests/NodeAccessAutoBubblingTest.php | 72 +++++++++++++++++++ .../node_access_test_auto_bubbling.info.yml | 6 ++ ...node_access_test_auto_bubbling.routing.yml | 6 ++ .../NodeAccessTestAutoBubblingController.php | 61 ++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 core/modules/node/src/Tests/NodeAccessAutoBubblingTest.php create mode 100644 core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.info.yml create mode 100644 core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.routing.yml create mode 100644 core/modules/node/tests/node_access_test_auto_bubbling/src/Controller/NodeAccessTestAutoBubblingController.php diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 896c4f888a1d..a54fc0a2c936 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1064,6 +1064,14 @@ function node_query_node_access_alter(AlterableInterface $query) { // Update the query for the given storage method. \Drupal::service('node.grant_storage')->alterQuery($query, $tables, $op, $account, $base_table); + + // Bubble the 'user.node_grants:$op' cache context to the current render + // context. + $renderer = \Drupal::service('renderer'); + if ($renderer->hasRenderContext()) { + $build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]]; + $renderer->render($build); + } } /** diff --git a/core/modules/node/src/Tests/NodeAccessAutoBubblingTest.php b/core/modules/node/src/Tests/NodeAccessAutoBubblingTest.php new file mode 100644 index 000000000000..9085672398ef --- /dev/null +++ b/core/modules/node/src/Tests/NodeAccessAutoBubblingTest.php @@ -0,0 +1,72 @@ +<?php + +/** + * @file + * Contains \Drupal\node\Tests\NodeAccessAutoBubblingTest. + */ + +namespace Drupal\node\Tests; + +use Drupal\Core\Url; + +/** + * Tests the node access automatic cacheability bubbling logic. + * + * @group node + * @group Cache + * @group cacheability_safeguards + */ +class NodeAccessAutoBubblingTest extends NodeTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['node_access_test', 'node_access_test_auto_bubbling']; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + node_access_rebuild(); + + // Create some content. + $this->drupalCreateNode(); + $this->drupalCreateNode(); + $this->drupalCreateNode(); + $this->drupalCreateNode(); + } + + /** + * Tests that the node grants cache context is auto-added, only when needed. + * + * @see node_query_node_access_alter() + */ + public function testNodeAccessCacheabilitySafeguard() { + $this->dumpHeaders = TRUE; + + // The node grants cache context should be added automatically. + $this->drupalGet(new Url('node_access_test_auto_bubbling')); + $this->assertCacheContext('user.node_grants:view'); + + // The root user has the 'bypass node access' permission, which means the + // node grants cache context is not necessary. + $this->drupalLogin($this->rootUser); + $this->drupalGet(new Url('node_access_test_auto_bubbling')); + $this->assertNoCacheContext('user.node_grants:view'); + $this->drupalLogout(); + + // Uninstall the module with the only hook_node_grants() implementation. + $this->container->get('module_installer')->uninstall(['node_access_test']); + $this->rebuildContainer(); + + // Because there are no node grants defined, there also is no need for the + // node grants cache context to be bubbled. + $this->drupalGet(new Url('node_access_test_auto_bubbling')); + $this->assertNoCacheContext('user.node_grants:view'); + } + +} diff --git a/core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.info.yml b/core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.info.yml new file mode 100644 index 000000000000..49a990d75147 --- /dev/null +++ b/core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.info.yml @@ -0,0 +1,6 @@ +name: 'Node module access automatic cacheability bubbling tests' +type: module +description: 'Support module for node permission testing. Provides a route which does a node access query without explicitly specifying the corresponding cache context.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.routing.yml b/core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.routing.yml new file mode 100644 index 000000000000..34fd420b3f6c --- /dev/null +++ b/core/modules/node/tests/node_access_test_auto_bubbling/node_access_test_auto_bubbling.routing.yml @@ -0,0 +1,6 @@ +node_access_test_auto_bubbling: + path: '/node_access_test_auto_bubbling' + defaults: + _controller: '\Drupal\node_access_test_auto_bubbling\Controller\NodeAccessTestAutoBubblingController::latest' + requirements: + _access: 'TRUE' diff --git a/core/modules/node/tests/node_access_test_auto_bubbling/src/Controller/NodeAccessTestAutoBubblingController.php b/core/modules/node/tests/node_access_test_auto_bubbling/src/Controller/NodeAccessTestAutoBubblingController.php new file mode 100644 index 000000000000..1fcaa4b0324c --- /dev/null +++ b/core/modules/node/tests/node_access_test_auto_bubbling/src/Controller/NodeAccessTestAutoBubblingController.php @@ -0,0 +1,61 @@ +<?php + +/** + * @file + * Contains \Drupal\node_access_test_auto_bubbling\Controller\NodeAccessTestAutoBubblingController. + */ + +namespace Drupal\node_access_test_auto_bubbling\Controller; + +use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\Query\QueryFactory; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Returns a node ID listing. + */ +class NodeAccessTestAutoBubblingController extends ControllerBase implements ContainerInjectionInterface { + + /** + * The entity query factory service. + * + * @var \Drupal\Core\Entity\Query\QueryFactory + */ + protected $entityQuery; + + /** + * Constructs a new NodeAccessTestAutoBubblingController. + * + * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query + * The entity query factory. + */ + public function __construct(QueryFactory $entity_query) { + $this->entityQuery = $entity_query; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.query') + ); + } + + /** + * Lists the three latest published node IDs. + * + * @return array + * A render array. + */ + public function latest() { + $nids = $this->entityQuery->get('node') + ->condition('status', NODE_PUBLISHED) + ->sort('created', 'DESC') + ->range(0, 3) + ->execute(); + return ['#markup' => $this->t('The three latest nodes are: @nids.', ['@nids' => implode(', ', $nids)])]; + } + +} -- GitLab