Issue #3201714 by jonathanshaw, ravi.shankar, longwave, catch, alexpott: EntityQuery accessCheck: data cleanup should never care about the current user

merge-requests/469/head
catch 2021-03-20 19:29:51 +00:00
parent c97c52b7ff
commit 37a5b1a70f
8 changed files with 80 additions and 14 deletions

View File

@ -171,7 +171,7 @@ function comment_field_storage_config_insert(FieldStorageConfigInterface $field_
function comment_field_config_delete(FieldConfigInterface $field) {
if ($field->getType() == 'comment') {
// Delete all comments that used by the entity bundle.
$entity_query = \Drupal::entityQuery('comment');
$entity_query = \Drupal::entityQuery('comment')->accessCheck(FALSE);
$entity_query->condition('entity_type', $field->getEntityTypeId());
$entity_query->condition('field_name', $field->getName());
$cids = $entity_query->execute();
@ -318,7 +318,7 @@ function comment_entity_predelete(EntityInterface $entity) {
// entity type that has an integer ID, $entity->id() might be a string
// containing a number), and then cast it to an integer when querying.
if ($entity instanceof FieldableEntityInterface && is_numeric($entity->id())) {
$entity_query = \Drupal::entityQuery('comment');
$entity_query = \Drupal::entityQuery('comment')->accessCheck(FALSE);
$entity_query->condition('entity_id', (int) $entity->id());
$entity_query->condition('entity_type', $entity->getEntityTypeId());
$cids = $entity_query->execute();

View File

@ -615,6 +615,7 @@ function node_user_cancel($edit, UserInterface $account, $method) {
case 'user_cancel_block_unpublish':
// Unpublish nodes (current revisions).
$nids = \Drupal::entityQuery('node')
->accessCheck(FALSE)
->condition('uid', $account->id())
->execute();
module_load_include('inc', 'node', 'node.admin');

View File

@ -79,7 +79,7 @@ function node_access_test_node_grants($account, $op) {
function node_access_test_node_access_records(NodeInterface $node) {
$grants = [];
// For NodeAccessBaseTableTestCase, only set records for private nodes.
if (!\Drupal::state()->get('node_access_test.private') || $node->private->value) {
if (!\Drupal::state()->get('node_access_test.private') || (isset($node->private) && $node->private->value)) {
// Groups 8888 and 8889 for the node_access_test realm both receive a view
// grant for all controlled nodes. See node_access_test_node_grants().
$grants[] = [

View File

@ -97,6 +97,7 @@ class ShortcutSet extends ConfigEntityBundleBase implements ShortcutSetInterface
// Next, delete the shortcuts for this set.
$shortcut_ids = \Drupal::entityQuery('shortcut')
->accessCheck(FALSE)
->condition('shortcut_set', $entity->id(), '=')
->execute();

View File

@ -101,7 +101,8 @@ class PrepareModulesEntityUninstallForm extends ConfirmFormBase {
$form = parent::buildForm($form, $form_state);
$storage = $this->entityTypeManager->getStorage($entity_type_id);
$count = $storage->getQuery()->count()->execute();
$count = $storage->getQuery()->accessCheck(FALSE)->count()->execute();
$accessible_count = $storage->getQuery()->accessCheck(TRUE)->count()->execute();
$form['entity_type_id'] = [
'#type' => 'value',
@ -118,8 +119,9 @@ class PrepareModulesEntityUninstallForm extends ConfirmFormBase {
),
];
}
elseif ($entity_type->hasKey('label')) {
elseif ($accessible_count > 0 && $entity_type->hasKey('label')) {
$recent_entity_ids = $storage->getQuery()
->accessCheck(TRUE)
->sort($entity_type->getKey('id'), 'DESC')
->pager(10)
->execute();
@ -216,11 +218,12 @@ class PrepareModulesEntityUninstallForm extends ConfirmFormBase {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = $storage->getQuery()->count()->execute();
$context['sandbox']['max'] = $storage->getQuery()->accessCheck(FALSE)->count()->execute();
}
$entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
$entity_ids = $storage->getQuery()
->accessCheck(FALSE)
->sort($entity_type->getKey('id'), 'ASC')
->range(0, 10)
->execute();
@ -229,7 +232,7 @@ class PrepareModulesEntityUninstallForm extends ConfirmFormBase {
}
// Sometimes deletes cause secondary deletes. For example, deleting a
// taxonomy term can cause its children to be deleted too.
$context['sandbox']['progress'] = $context['sandbox']['max'] - $storage->getQuery()->count()->execute();
$context['sandbox']['progress'] = $context['sandbox']['max'] - $storage->getQuery()->accessCheck(FALSE)->count()->execute();
// Inform the batch engine that we are not finished and provide an
// estimation of the completion level we reached.

View File

@ -2,6 +2,7 @@
namespace Drupal\Tests\system\Functional\Module;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
@ -38,7 +39,7 @@ class PrepareUninstallTest extends BrowserTestBase {
*
* @var array
*/
protected static $modules = ['node', 'taxonomy', 'entity_test'];
protected static $modules = ['node', 'taxonomy', 'entity_test', 'node_access_test'];
/**
* {@inheritdoc}
@ -49,10 +50,16 @@ class PrepareUninstallTest extends BrowserTestBase {
$admin_user = $this->drupalCreateUser(['administer modules']);
$this->drupalLogin($admin_user);
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
node_access_rebuild();
node_access_test_add_field(NodeType::load('article'));
\Drupal::state()->set('node_access_test.private', TRUE);
// Create 10 nodes.
for ($i = 1; $i <= 5; $i++) {
$this->nodes[] = $this->drupalCreateNode(['type' => 'page']);
$this->nodes[] = $this->drupalCreateNode(['type' => 'article']);
// These 5 articles are inaccessible to the admin user doing the uninstalling.
$this->nodes[] = $this->drupalCreateNode(['type' => 'article', 'uid' => 0, 'private' => TRUE]);
}
// Create 3 top-level taxonomy terms, each with 11 children.
@ -110,7 +117,19 @@ class PrepareUninstallTest extends BrowserTestBase {
// Delete Node data.
$this->drupalGet('admin/modules/uninstall/entity/node');
// All 10 nodes should be listed.
// Only the 5 pages should be listed as the 5 articles are initially inaccessible.
foreach ($this->nodes as $node) {
if ($node->bundle() === 'page') {
$this->assertText($node->label());
}
else {
$node->set('private', FALSE)->save();
}
}
$this->assertText('And 5 more content items.');
// All 10 nodes should now be listed as none are still inaccessible.
$this->drupalGet('admin/modules/uninstall/entity/node');
foreach ($this->nodes as $node) {
$this->assertText($node->label());
}
@ -127,11 +146,9 @@ class PrepareUninstallTest extends BrowserTestBase {
// the first 10's labels.
$this->assertText('And 1 more content item.');
// Create another node so we have 12.
$this->nodes[] = $this->drupalCreateNode(['type' => 'article']);
// Create another node so we have 12, with one private.
$this->nodes[] = $this->drupalCreateNode(['type' => 'article', 'private' => TRUE]);
$this->drupalGet('admin/modules/uninstall/entity/node');
// Ensures singular case is used when a single entity is left after listing
// the first 10's labels.
$this->assertText('And 2 more content items.');
$this->submitForm([], 'Delete all content items');

View File

@ -6,6 +6,7 @@ use Drupal\comment\CommentInterface;
use Drupal\comment\Entity\Comment;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\User;
@ -280,6 +281,47 @@ class UserCancelTest extends BrowserTestBase {
$this->assertFalse($comment->isPublished(), 'Comment of the user has been unpublished.');
}
/**
* Tests nodes are unpublished even if inaccessible to cancelling user.
*/
public function testUserBlockUnpublishNodeAccess() {
\Drupal::service('module_installer')->install(['node_access_test', 'user_form_test']);
// Setup node access
node_access_rebuild();
node_access_test_add_field(NodeType::load('page'));
\Drupal::state()->set('node_access_test.private', TRUE);
$this->config('user.settings')->set('cancel_method', 'user_cancel_block_unpublish')->save();
// Create a user.
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
$account = $this->drupalCreateUser(['cancel account']);
// Load a real user object.
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
// Create a published private node.
$node = $this->drupalCreateNode([
'uid' => $account->id(),
'type' => 'page',
'status' => 1,
'private' => TRUE,
]);
// Cancel node author.
$admin_user = $this->drupalCreateUser(['cancel other accounts']);
$this->drupalLogin($admin_user);
$this->drupalPostForm('user_form_test_cancel/' . $account->id(), [], 'Cancel account');
// Confirm node has been unpublished, even though the admin user
// does not have permission to access it.
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$node_storage->resetCache([$node->id()]);
$test_node = $node_storage->load($node->id());
$this->assertFalse($test_node->isPublished(), 'Node of the user has been unpublished.');
}
/**
* Delete account and anonymize all content.
*/

View File

@ -45,6 +45,7 @@ function hook_user_cancel($edit, UserInterface $account, $method) {
// Unpublish nodes (current revisions).
module_load_include('inc', 'node', 'node.admin');
$nodes = \Drupal::entityQuery('node')
->accessCheck(FALSE)
->condition('uid', $account->id())
->execute();
node_mass_update($nodes, ['status' => 0], NULL, TRUE);
@ -54,6 +55,7 @@ function hook_user_cancel($edit, UserInterface $account, $method) {
// Anonymize nodes (current revisions).
module_load_include('inc', 'node', 'node.admin');
$nodes = \Drupal::entityQuery('node')
->accessCheck(FALSE)
->condition('uid', $account->id())
->execute();
node_mass_update($nodes, ['uid' => 0], NULL, TRUE);