Issue #2430669 by larowlan, marthinal, clemens.tolboom, jibran: Cannot create comments from REST - field access is too strict

8.0.x
Alex Pott 2015-08-14 13:08:26 +01:00
parent 877fd18762
commit db390cded0
4 changed files with 126 additions and 7 deletions

View File

@ -80,15 +80,26 @@ class CommentAccessControlHandler extends EntityAccessControlHandler {
// No user can change read-only fields. // No user can change read-only fields.
$read_only_fields = array( $read_only_fields = array(
'hostname', 'hostname',
'uuid', 'changed',
'cid', 'cid',
'thread', 'thread',
);
// These fields can be edited during comment creation.
$create_only_fields = [
'comment_type', 'comment_type',
'pid', 'uuid',
'entity_id', 'entity_id',
'entity_type', 'entity_type',
'field_name', 'field_name',
); 'pid',
];
if ($items && ($entity = $items->getEntity()) && $entity->isNew() && in_array($field_definition->getName(), $create_only_fields, TRUE)) {
// We are creating a new comment, user can edit create only fields.
return AccessResult::allowedIfHasPermission($account, 'post comments')->addCacheableDependency($entity);
}
// We are editing an existing comment - create only fields are now read
// only.
$read_only_fields = array_merge($read_only_fields, $create_only_fields);
if (in_array($field_definition->getName(), $read_only_fields, TRUE)) { if (in_array($field_definition->getName(), $read_only_fields, TRUE)) {
return AccessResult::forbidden(); return AccessResult::forbidden();
} }

View File

@ -53,15 +53,23 @@ class CommentFieldAccessTest extends EntityUnitTestBase {
protected $readOnlyFields = array( protected $readOnlyFields = array(
'changed', 'changed',
'hostname', 'hostname',
'uuid',
'cid', 'cid',
'thread', 'thread',
'comment_type', );
/**
* These fields can be edited on create only.
*
* @var array
*/
protected $createOnlyFields = [
'uuid',
'pid', 'pid',
'comment_type',
'entity_id', 'entity_id',
'entity_type', 'entity_type',
'field_name', 'field_name',
); ];
/** /**
* These fields can only be edited by the admin or anonymous users if allowed. * These fields can only be edited by the admin or anonymous users if allowed.
@ -252,6 +260,28 @@ class CommentFieldAccessTest extends EntityUnitTestBase {
} }
} }
// Check create-only fields.
foreach ($this->createOnlyFields as $field) {
// Check view operation.
foreach ($permutations as $set) {
$may_view = $set['comment']->{$field}->access('view', $set['user']);
$may_update = $set['comment']->{$field}->access('edit', $set['user']);
$this->assertEqual($may_view, $field != 'hostname' && ($set['user']->hasPermission('administer comments') ||
($set['comment']->isPublished() && $set['user']->hasPermission('access comments'))), SafeMarkup::format('User @user !state view field !field on comment @comment', [
'@user' => $set['user']->getUsername(),
'!state' => $may_view ? 'can' : 'cannot',
'@comment' => $set['comment']->getSubject(),
'!field' => $field,
]));
$this->assertEqual($may_update, $set['user']->hasPermission('post comments') && $set['comment']->isNew(), SafeMarkup::format('User @user !state update field !field on comment @comment', [
'@user' => $set['user']->getUsername(),
'!state' => $may_update ? 'can' : 'cannot',
'@comment' => $set['comment']->getSubject(),
'!field' => $field,
]));
}
}
// Check contact fields. // Check contact fields.
foreach ($this->contactFields as $field) { foreach ($this->contactFields as $field) {
// Check view operation. // Check view operation.

View File

@ -7,11 +7,13 @@
namespace Drupal\rest\Tests; namespace Drupal\rest\Tests;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\entity_test\Entity\EntityTest; use Drupal\entity_test\Entity\EntityTest;
use Drupal\node\Entity\Node; use Drupal\node\Entity\Node;
use Drupal\user\Entity\User; use Drupal\user\Entity\User;
use Drupal\comment\Entity\Comment;
/** /**
* Tests the creation of resources. * Tests the creation of resources.
@ -20,12 +22,13 @@ use Drupal\user\Entity\User;
*/ */
class CreateTest extends RESTTestBase { class CreateTest extends RESTTestBase {
use CommentTestTrait;
/** /**
* Modules to install. * Modules to install.
* *
* @var array * @var array
*/ */
public static $modules = array('hal', 'rest', 'entity_test'); public static $modules = array('hal', 'rest', 'entity_test', 'comment');
/** /**
* The 'serializer' service. * The 'serializer' service.
@ -36,6 +39,7 @@ class CreateTest extends RESTTestBase {
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
$this->addDefaultCommentField('node', 'resttest');
// Get the 'serializer' service. // Get the 'serializer' service.
$this->serializer = $this->container->get('serializer'); $this->serializer = $this->container->get('serializer');
} }
@ -224,6 +228,52 @@ class CreateTest extends RESTTestBase {
} }
/**
* Test comment creation.
*/
protected function testCreateComment() {
$node = Node::create([
'type' => 'resttest',
'title' => 'some node',
]);
$node->save();
$entity_type = 'comment';
// Enable the REST service for 'comment' entity type.
$this->enableService('entity:' . $entity_type, 'POST');
// Create two accounts that have the required permissions to create
// resources, The second one has administrative permissions.
$accounts = $this->createAccountPerEntity($entity_type);
$account = end($accounts);
$this->drupalLogin($account);
$entity_values = $this->entityValues($entity_type);
$entity_values['entity_id'] = $node->id();
$entity = Comment::create($entity_values);
// Changed field can never be added.
unset($entity->changed);
$serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]);
// Create the entity over the REST API.
$this->assertCreateEntityOverRestApi($entity_type, $serialized);
// Get the new entity ID from the location header and try to read it from
// the database.
$this->assertReadEntityIdFromHeaderAndDb($entity_type, $entity, $entity_values);
// Try to send invalid data that cannot be correctly deserialized.
$this->assertCreateEntityInvalidData($entity_type);
// Try to send no data at all, which does not make sense on POST requests.
$this->assertCreateEntityNoData($entity_type);
// Try to send invalid data to trigger the entity validation constraints.
// Send a UUID that is too long.
$this->assertCreateEntityInvalidSerialized($entity, $entity_type);
}
/** /**
* Tests several valid and invalid create requests for 'user' entity type. * Tests several valid and invalid create requests for 'user' entity type.
*/ */
@ -293,6 +343,7 @@ class CreateTest extends RESTTestBase {
// Add administrative permissions for nodes and users. // Add administrative permissions for nodes and users.
$permissions[] = 'administer nodes'; $permissions[] = 'administer nodes';
$permissions[] = 'administer users'; $permissions[] = 'administer users';
$permissions[] = 'administer comments';
// Create an administrative user. // Create an administrative user.
$accounts[] = $this->drupalCreateUser($permissions); $accounts[] = $this->drupalCreateUser($permissions);

View File

@ -213,6 +213,17 @@ abstract class RESTTestBase extends WebTestBase {
); );
case 'user': case 'user':
return array('name' => $this->randomMachineName()); return array('name' => $this->randomMachineName());
case 'comment':
return [
'subject' => $this->randomMachineName(),
'entity_type' => 'node',
'comment_type' => 'comment',
'comment_body' => $this->randomString(),
'entity_id' => 'invalid',
'field_name' => 'comment',
];
default: default:
return array(); return array();
} }
@ -313,6 +324,22 @@ abstract class RESTTestBase extends WebTestBase {
return array('delete any resttest content'); return array('delete any resttest content');
} }
case 'comment':
switch ($operation) {
case 'view':
return ['access comments'];
case 'create':
return ['post comments', 'skip comment approval'];
case 'update':
return ['edit own comments'];
case 'delete':
return ['administer comments'];
}
break;
case 'user': case 'user':
switch ($operation) { switch ($operation) {
case 'view': case 'view':