Issue #3228000 by bbrala, larowlan, GuyPaddock, Wim Leers, bradjones1, alexpott, catch, e0ipso: Users deleted via JSON:API DELETE don't follow the site-wide cancel_method in the user settings

(cherry picked from commit 9d8e7da4fc)
merge-requests/561/head
Alex Pott 2021-10-11 09:08:35 +01:00
parent cccb885429
commit 7c0c951f76
No known key found for this signature in database
GPG Key ID: BDA67E7EE836E5CE
2 changed files with 215 additions and 2 deletions

View File

@ -369,7 +369,25 @@ class EntityResource {
* The response.
*/
public function deleteIndividual(EntityInterface $entity) {
$entity->delete();
// @todo Replace with entity handlers in: https://www.drupal.org/project/drupal/issues/3230434
if ($entity->getEntityTypeId() === 'user') {
$cancel_method = \Drupal::service('config.factory')->get('user.settings')->get('cancel_method');
// Allow other modules to act.
user_cancel([], $entity->id(), $cancel_method);
// Since user_cancel() is not invoked via Form API, batch processing
// needs to be invoked manually.
$batch =& batch_get();
// Mark this batch as non-progressive to bypass the progress bar and
// redirect.
$batch['progressive'] = FALSE;
batch_process();
}
else {
$entity->delete();
}
return new ResourceResponse(NULL, 204);
}

View File

@ -10,6 +10,7 @@ use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use GuzzleHttp\RequestOptions;
/**
@ -19,10 +20,12 @@ use GuzzleHttp\RequestOptions;
*/
class UserTest extends ResourceTestBase {
const BATCH_TEST_NODE_COUNT = 15;
/**
* {@inheritdoc}
*/
protected static $modules = ['user', 'jsonapi_test_user'];
protected static $modules = ['user', 'jsonapi_test_user', 'node'];
/**
* {@inheritdoc}
@ -119,6 +122,15 @@ class UserTest extends ResourceTestBase {
return $user;
}
/**
* {@inheritdoc}
*/
public function testDeleteIndividual() {
$this->config('user.settings')->set('cancel_method', 'user_cancel_delete')->save(TRUE);
parent::testDeleteIndividual();
}
/**
* {@inheritdoc}
*/
@ -597,6 +609,175 @@ class UserTest extends ResourceTestBase {
$this->assertEquals($original_name, $updated_user->get('name')->value);
}
/**
* Tests if JSON:API respects user.settings.cancel_method: user_cancel_block.
*/
public function testDeleteRespectsUserCancelBlock() {
$cancel_method = 'user_cancel_block';
$this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
$this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE);
$account = $this->createAnotherEntity($cancel_method);
$node = $this->drupalCreateNode(['uid' => $account->id()]);
$this->sendDeleteRequestForUser($account, $cancel_method);
$user_storage = $this->container->get('entity_type.manager')
->getStorage('user');
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertNotNull($account, 'User is not deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$this->assertTrue($account->isBlocked(), 'User is blocked after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$node_storage->resetCache([$node->id()]);
$test_node = $node_storage->load($node->id());
$this->assertNotNull($test_node, 'Node of the user is not deleted.');
$this->assertTrue($test_node->isPublished(), 'Node of the user is published.');
$test_node = node_revision_load($node->getRevisionId());
$this->assertTrue($test_node->isPublished(), 'Node revision of the user is published.');
}
/**
* Tests if JSON:API respects user.settings.cancel_method: user_cancel_block_unpublish.
*/
public function testDeleteRespectsUserCancelBlockUnpublish() {
$cancel_method = 'user_cancel_block_unpublish';
$this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
$this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE);
$account = $this->createAnotherEntity($cancel_method);
$node = $this->drupalCreateNode(['uid' => $account->id()]);
$this->sendDeleteRequestForUser($account, $cancel_method);
$user_storage = $this->container->get('entity_type.manager')
->getStorage('user');
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertNotNull($account, 'User is not deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$this->assertTrue($account->isBlocked(), 'User is blocked after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$node_storage->resetCache([$node->id()]);
$test_node = $node_storage->load($node->id());
$this->assertNotNull($test_node, 'Node of the user is not deleted.');
$this->assertFalse($test_node->isPublished(), 'Node of the user is no longer published.');
$test_node = node_revision_load($node->getRevisionId());
$this->assertFalse($test_node->isPublished(), 'Node revision of the user is no longer published.');
}
/**
* Tests if JSON:API respects user.settings.cancel_method: user_cancel_block_unpublish.
* @group jsonapi
*/
public function testDeleteRespectsUserCancelBlockUnpublishAndProcessesBatches() {
$cancel_method = 'user_cancel_block_unpublish';
$this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
$this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE);
$account = $this->createAnotherEntity($cancel_method);
$nodeCount = self::BATCH_TEST_NODE_COUNT;
$node_ids = [];
$nodes = [];
while ($nodeCount-- > 0) {
$node = $this->drupalCreateNode(['uid' => $account->id()]);
$nodes[] = $node;
$node_ids[] = $node->id();
}
$this->sendDeleteRequestForUser($account, $cancel_method);
$user_storage = $this->container->get('entity_type.manager')
->getStorage('user');
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertNotNull($account, 'User is not deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$this->assertTrue($account->isBlocked(), 'User is blocked after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$node_storage->resetCache($node_ids);
$test_nodes = $node_storage->loadMultiple($node_ids);
$this->assertCount(self::BATCH_TEST_NODE_COUNT, $test_nodes, 'Nodes of the user are not deleted.');
foreach ($test_nodes as $test_node) {
$this->assertFalse($test_node->isPublished(), 'Node of the user is no longer published.');
}
foreach ($nodes as $node) {
$test_node = node_revision_load($node->getRevisionId());
$this->assertFalse($test_node->isPublished(), 'Node revision of the user is no longer published.');
}
}
/**
* Tests if JSON:API respects user.settings.cancel_method: user_cancel_reassign.
*/
public function testDeleteRespectsUserCancelReassign() {
$cancel_method = 'user_cancel_reassign';
$this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
$this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE);
$account = $this->createAnotherEntity($cancel_method);
$node = $this->drupalCreateNode(['uid' => $account->id()]);
$this->sendDeleteRequestForUser($account, $cancel_method);
$user_storage = $this->container->get('entity_type.manager')
->getStorage('user');
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertNull($account, 'User is deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$node_storage->resetCache([$node->id()]);
$test_node = $node_storage->load($node->id());
$this->assertNotNull($test_node, 'Node of the user is not deleted.');
$this->assertTrue($test_node->isPublished(), 'Node of the user is still published.');
$this->assertEquals(0, $test_node->getOwnerId(), 'Node of the user has been attributed to anonymous user.');
$test_node = node_revision_load($node->getRevisionId());
$this->assertTrue($test_node->isPublished(), 'Node revision of the user is still published.');
$this->assertEquals(0, $test_node->getRevisionUser()->id(), 'Node revision of the user has been attributed to anonymous user.');
}
/**
* Tests if JSON:API respects user.settings.cancel_method: user_cancel_delete.
*/
public function testDeleteRespectsUserCancelDelete() {
$cancel_method = 'user_cancel_delete';
$this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
$this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE);
$account = $this->createAnotherEntity($cancel_method);
$node = $this->drupalCreateNode(['uid' => $account->id()]);
$url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), ['entity' => $account->uuid()]);
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
$this->setUpAuthorization('DELETE');
$response = $this->request('DELETE', $url, $request_options);
$this->assertResourceResponse(204, NULL, $response);
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertNull($account, 'User is deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method);
$node_storage->resetCache([$node->id()]);
$test_node = $node_storage->load($node->id());
$this->assertNull($test_node, 'Node of the user is deleted.');
}
/**
* {@inheritdoc}
*/
@ -620,4 +801,18 @@ class UserTest extends ResourceTestBase {
return parent::makeNormalizationInvalid($document, $entity_key);
}
/**
* @param \Drupal\user\UserInterface $account
* @param string $cancel_method
*/
private function sendDeleteRequestForUser(UserInterface $account, string $cancel_method) {
$url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), ['entity' => $account->uuid()]);
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
$this->setUpAuthorization('DELETE');
$response = $this->request('DELETE', $url, $request_options);
$this->assertResourceResponse(204, NULL, $response);
}
}