Issue #3016781 by arpad.rozsa, Berdir, catch, Wim Leers, kristiaanvandeneynde: node_node_access() is too eager in adding the user cache context — fixing this makes some pages faster

merge-requests/1119/head
Alex Pott 2019-03-26 13:22:06 +00:00
parent 777d7fc516
commit 039eae1719
No known key found for this signature in database
GPG Key ID: 31905460D4A69276
6 changed files with 63 additions and 28 deletions

View File

@ -103,11 +103,11 @@ class QuickEditIntegrationLoadingTest extends BrowserTestBase {
$message = "The 'access in-place editing' permission is required.";
}
else {
$message = '';
$message = "The 'edit any article content' permission is required.";
}
$body = Json::decode($response->getBody());
$this->assertIdentical($message, $body['message']);
$this->assertSame($message, $body['message']);
}
}

View File

@ -974,33 +974,30 @@ function node_form_system_themes_admin_form_submit($form, FormStateInterface $fo
/**
* Implements hook_node_access().
*/
function node_node_access(NodeInterface $node, $op, $account) {
function node_node_access(NodeInterface $node, $op, AccountInterface $account) {
$type = $node->bundle();
$access = AccessResult::neutral();
switch ($op) {
case 'create':
return AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');
$access = AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');
case 'update':
if ($account->hasPermission('edit any ' . $type . ' content')) {
return AccessResult::allowed()->cachePerPermissions();
}
else {
return AccessResult::allowedIf($account->hasPermission('edit own ' . $type . ' content') && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->addCacheableDependency($node);
$access = AccessResult::allowedIfHasPermission($account, 'edit any ' . $type . ' content');
if (!$access->isAllowed() && $account->hasPermission('edit own ' . $type . ' content')) {
$access = $access->orIf(AccessResult::allowedIf($account->id() == $node->getOwnerId())->cachePerUser()->addCacheableDependency($node));
}
break;
case 'delete':
if ($account->hasPermission('delete any ' . $type . ' content')) {
return AccessResult::allowed()->cachePerPermissions();
$access = AccessResult::allowedIfHasPermission($account, 'delete any ' . $type . ' content');
if (!$access->isAllowed() && $account->hasPermission('delete own ' . $type . ' content')) {
$access = $access->orIf(AccessResult::allowedIf($account->id() == $node->getOwnerId()))->cachePerUser()->addCacheableDependency($node);
}
else {
return AccessResult::allowedIf($account->hasPermission('delete own ' . $type . ' content') && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->addCacheableDependency($node);
}
default:
// No opinion.
return AccessResult::neutral();
break;
}
return $access;
}
/**

View File

@ -12,7 +12,7 @@ use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
* @group Cache
* @group cacheability_safeguards
*/
class NodeAccessAutoBubblingTest extends NodeTestBase {
class NodeAccessCacheabilityTest extends NodeTestBase {
use AssertPageCacheContextsAndTagsTrait;
@ -67,4 +67,45 @@ class NodeAccessAutoBubblingTest extends NodeTestBase {
$this->assertNoCacheContext('user.node_grants:view');
}
/**
* Tests that the user cache contexts are correctly set.
*/
public function testNodeAccessCacheContext() {
// Create a user, with edit/delete own content permission.
$test_user1 = $this->drupalCreateUser([
'access content',
'edit own page content',
'delete own page content',
]);
$this->drupalLogin($test_user1);
$node1 = $this->createNode(['type' => 'page']);
// User should be able to edit/delete their own content.
// Therefore after the access check in node_node_access the user cache
// context should be added.
$this->drupalGet('node/' . $node1->id() . '/edit');
$this->assertCacheContext('user');
$this->drupalGet('node/' . $node1->id() . '/delete');
$this->assertCacheContext('user');
// Create a user without edit/delete permission.
$test_user2 = $this->drupalCreateUser([
'access content',
]);
$this->drupalLogin($test_user2);
$node2 = $this->createNode(['type' => 'page']);
// The user shouldn't have access to the node edit/delete pages.
// Therefore after the access check in node_node_access the user permissions
// cache context should be added.
$this->drupalGet('node/' . $node2->id() . '/edit');
$this->assertCacheContext('user.permissions');
$this->drupalGet('node/' . $node2->id() . '/delete');
$this->assertCacheContext('user.permissions');
}
}

View File

@ -71,7 +71,6 @@ class PageCacheTagsIntegrationTest extends BrowserTestBase {
'route',
'theme',
'timezone',
'user',
// The placed block is only visible on certain URLs through a visibility
// condition.
'url.path',
@ -81,6 +80,8 @@ class PageCacheTagsIntegrationTest extends BrowserTestBase {
// These two cache contexts are added by BigPipe.
'cookies:big_pipe_nojs',
'session.exists',
'user.roles:anonymous',
'user.roles:authenticated',
];
// Full node page 1.
@ -107,7 +108,6 @@ class PageCacheTagsIntegrationTest extends BrowserTestBase {
'config:block.block.bartik_page_title',
'node_view',
'node:' . $node_1->id(),
'user:0',
'user:' . $author_1->id(),
'config:filter.format.basic_html',
'config:color.theme.bartik',
@ -164,7 +164,6 @@ class PageCacheTagsIntegrationTest extends BrowserTestBase {
// FinishResponseSubscriber adds this cache tag to responses that have the
// 'user.permissions' cache context for anonymous users.
'config:user.role.anonymous',
'user:0',
]);
}

View File

@ -188,11 +188,10 @@ class RowRenderCacheTest extends ViewsKernelTestBase {
if ($check_cache) {
$keys = $cache_plugin->getRowCacheKeys($view->result[$index]);
$user_context = !$account->hasPermission('edit any test content') ? 'user' : 'user.permissions';
$cache = [
'#cache' => [
'keys' => $keys,
'contexts' => ['languages:language_interface', 'theme', $user_context],
'contexts' => ['languages:language_interface', 'theme', 'user.permissions'],
],
];
$element = $render_cache->get($cache);

View File

@ -37,12 +37,11 @@ class StandardJavascriptTest extends WebDriverTestBase {
$this->drupalGet('');
$this->assertBigPipePlaceholderReplacementCount(1);
// Node page: 3 placeholders:
// Node page: 2 placeholders:
// 1. messages
// 2. local tasks block
// 3. comment form
// 2. comment form
$this->drupalGet($node->toUrl());
$this->assertBigPipePlaceholderReplacementCount(3);
$this->assertBigPipePlaceholderReplacementCount(2);
}
/**