diff --git a/core/modules/hal/tests/src/Kernel/DenormalizeTest.php b/core/modules/hal/tests/src/Kernel/DenormalizeTest.php index 64c8311f7824fe1c17d69d9a89ca7a1ab2e5e331..8f64049917976381e40fe6afd81f66849653136e 100644 --- a/core/modules/hal/tests/src/Kernel/DenormalizeTest.php +++ b/core/modules/hal/tests/src/Kernel/DenormalizeTest.php @@ -7,7 +7,7 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; /** - * Tests that entities can be denormalized from HAL. + * Tests HAL denormalization edge cases for EntityResource. * * @group hal */ @@ -110,98 +110,4 @@ public function testMarkFieldForDeletion() { $this->assertEqual($entity->field_test_text->count(), 0); } - /** - * Test that non-reference fields can be denormalized. - */ - public function testBasicFieldDenormalization() { - $data = array( - '_links' => array( - 'type' => array( - 'href' => Url::fromUri('base:rest/type/entity_test/entity_test', array('absolute' => TRUE))->toString(), - ), - ), - 'uuid' => array( - array( - 'value' => 'e5c9fb96-3acf-4a8d-9417-23de1b6c3311', - ), - ), - 'field_test_text' => array( - array( - 'value' => $this->randomMachineName(), - 'format' => 'full_html', - ), - ), - 'field_test_translatable_text' => array( - array( - 'value' => $this->randomMachineName(), - 'format' => 'full_html', - ), - array( - 'value' => $this->randomMachineName(), - 'format' => 'filtered_html', - ), - array( - 'value' => $this->randomMachineName(), - 'format' => 'filtered_html', - 'lang' => 'de', - ), - array( - 'value' => $this->randomMachineName(), - 'format' => 'full_html', - 'lang' => 'de', - ), - ), - ); - - $expected_value_default = array( - array ( - 'value' => $data['field_test_translatable_text'][0]['value'], - 'format' => 'full_html', - ), - array ( - 'value' => $data['field_test_translatable_text'][1]['value'], - 'format' => 'filtered_html', - ), - ); - $expected_value_de = array( - array ( - 'value' => $data['field_test_translatable_text'][2]['value'], - 'format' => 'filtered_html', - ), - array ( - 'value' => $data['field_test_translatable_text'][3]['value'], - 'format' => 'full_html', - ), - ); - $denormalized = $this->serializer->denormalize($data, $this->entityClass, $this->format); - $this->assertEqual($data['uuid'], $denormalized->get('uuid')->getValue(), 'A preset value (e.g. UUID) is overridden by incoming data.'); - $this->assertEqual($data['field_test_text'], $denormalized->get('field_test_text')->getValue(), 'A basic text field is denormalized.'); - $this->assertEqual($expected_value_default, $denormalized->get('field_test_translatable_text')->getValue(), 'Values in the default language are properly handled for a translatable field.'); - $this->assertEqual($expected_value_de, $denormalized->getTranslation('de')->get('field_test_translatable_text')->getValue(), 'Values in a translation language are properly handled for a translatable field.'); - } - - /** - * Verifies that the denormalized entity is correct in the PATCH context. - */ - public function testPatchDenormalization() { - $data = array( - '_links' => array( - 'type' => array( - 'href' => Url::fromUri('base:rest/type/entity_test/entity_test', array('absolute' => TRUE))->toString(), - ), - ), - 'field_test_text' => array( - array( - 'value' => $this->randomMachineName(), - 'format' => 'full_html', - ), - ), - ); - $denormalized = $this->serializer->denormalize($data, $this->entityClass, $this->format, array('request_method' => 'patch')); - // Check that the one field got populated as expected. - $this->assertEqual($data['field_test_text'], $denormalized->get('field_test_text')->getValue()); - // Check the custom property that contains the list of fields to merge. - $this->assertEqual($denormalized->_restSubmittedFields, ['field_test_text']); - } - } diff --git a/core/modules/hal/tests/src/Kernel/EntityNormalizeTest.php b/core/modules/hal/tests/src/Kernel/EntityNormalizeTest.php deleted file mode 100644 index 88db403da976f0948b23805885ad9836f00f2542..0000000000000000000000000000000000000000 --- a/core/modules/hal/tests/src/Kernel/EntityNormalizeTest.php +++ /dev/null @@ -1,206 +0,0 @@ -<?php - -namespace Drupal\Tests\hal\Kernel; - -use Drupal\comment\Tests\CommentTestTrait; -use Drupal\comment\Entity\Comment; -use Drupal\node\Entity\Node; -use Drupal\user\Entity\User; -use Drupal\node\Entity\NodeType; -use Drupal\taxonomy\Entity\Term; -use Drupal\taxonomy\Entity\Vocabulary; - -/** - * Tests that nodes and terms are correctly normalized and denormalized. - * - * @group hal - */ -class EntityNormalizeTest extends NormalizerTestBase { - - use CommentTestTrait; - - /** - * Modules to enable. - * - * @var array - */ - public static $modules = array('node', 'taxonomy', 'comment'); - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - \Drupal::service('router.builder')->rebuild(); - $this->installSchema('system', array('sequences')); - $this->installSchema('comment', array('comment_entity_statistics')); - $this->installEntitySchema('taxonomy_term'); - $this->installConfig(['node', 'comment']); - } - - /** - * Tests the normalization of nodes. - */ - public function testNode() { - $node_type = NodeType::create(['type' => 'example_type']); - $node_type->save(); - - $user = User::create(['name' => $this->randomMachineName()]); - $user->save(); - - // Add comment type. - $this->container->get('entity.manager')->getStorage('comment_type')->create(array( - 'id' => 'comment', - 'label' => 'comment', - 'target_entity_type_id' => 'node', - ))->save(); - - $this->addDefaultCommentField('node', 'example_type'); - - $node = Node::create([ - 'title' => $this->randomMachineName(), - 'uid' => $user->id(), - 'type' => $node_type->id(), - 'status' => NODE_PUBLISHED, - 'promote' => 1, - 'sticky' => 0, - 'body' => [ - 'value' => $this->randomMachineName(), - 'format' => $this->randomMachineName() - ], - 'revision_log' => $this->randomString(), - ]); - $node->save(); - - $original_values = $node->toArray(); - - $normalized = $this->serializer->normalize($node, $this->format); - - /** @var \Drupal\node\NodeInterface $denormalized_node */ - $denormalized_node = $this->serializer->denormalize($normalized, 'Drupal\node\Entity\Node', $this->format); - - $this->assertEqual($original_values, $denormalized_node->toArray(), 'Node values are restored after normalizing and denormalizing.'); - } - - /** - * Tests the normalization of terms. - */ - public function testTerm() { - $vocabulary = Vocabulary::create(['vid' => 'example_vocabulary']); - $vocabulary->save(); - - $account = User::create(['name' => $this->randomMachineName()]); - $account->save(); - - // @todo Until https://www.drupal.org/node/2327935 is fixed, if no parent is - // set, the test fails because target_id => 0 is reserialized to NULL. - $term_parent = Term::create([ - 'name' => $this->randomMachineName(), - 'vid' => $vocabulary->id(), - ]); - $term_parent->save(); - $term = Term::create([ - 'name' => $this->randomMachineName(), - 'vid' => $vocabulary->id(), - 'description' => array( - 'value' => $this->randomMachineName(), - 'format' => $this->randomMachineName(), - ), - 'parent' => $term_parent->id(), - ]); - $term->save(); - - $original_values = $term->toArray(); - - $normalized = $this->serializer->normalize($term, $this->format, ['account' => $account]); - - /** @var \Drupal\taxonomy\TermInterface $denormalized_term */ - $denormalized_term = $this->serializer->denormalize($normalized, 'Drupal\taxonomy\Entity\Term', $this->format, ['account' => $account]); - - $this->assertEqual($original_values, $denormalized_term->toArray(), 'Term values are restored after normalizing and denormalizing.'); - } - - /** - * Tests the normalization of comments. - */ - public function testComment() { - $node_type = NodeType::create(['type' => 'example_type']); - $node_type->save(); - - $account = User::create(['name' => $this->randomMachineName()]); - $account->save(); - - // Add comment type. - $this->container->get('entity.manager')->getStorage('comment_type')->create(array( - 'id' => 'comment', - 'label' => 'comment', - 'target_entity_type_id' => 'node', - ))->save(); - - $this->addDefaultCommentField('node', 'example_type'); - - $node = Node::create([ - 'title' => $this->randomMachineName(), - 'uid' => $account->id(), - 'type' => $node_type->id(), - 'status' => NODE_PUBLISHED, - 'promote' => 1, - 'sticky' => 0, - 'body' => [[ - 'value' => $this->randomMachineName(), - 'format' => $this->randomMachineName() - ]], - ]); - $node->save(); - - $parent_comment = Comment::create(array( - 'uid' => $account->id(), - 'subject' => $this->randomMachineName(), - 'comment_body' => [ - 'value' => $this->randomMachineName(), - 'format' => NULL, - ], - 'entity_id' => $node->id(), - 'entity_type' => 'node', - 'field_name' => 'comment', - )); - $parent_comment->save(); - - $comment = Comment::create(array( - 'uid' => $account->id(), - 'subject' => $this->randomMachineName(), - 'comment_body' => [ - 'value' => $this->randomMachineName(), - 'format' => NULL, - ], - 'entity_id' => $node->id(), - 'entity_type' => 'node', - 'field_name' => 'comment', - 'pid' => $parent_comment->id(), - 'mail' => 'dries@drupal.org', - 'homepage' => 'http://buytaert.net', - )); - $comment->save(); - - $original_values = $comment->toArray(); - // Hostname will always be denied view access. - // No value will exist for name as this is only for anonymous users. - unset($original_values['hostname'], $original_values['name']); - - $normalized = $this->serializer->normalize($comment, $this->format, ['account' => $account]); - - // Assert that the hostname field does not appear at all in the normalized - // data. - $this->assertFalse(array_key_exists('hostname', $normalized), 'Hostname was not found in normalized comment data.'); - - /** @var \Drupal\comment\CommentInterface $denormalized_comment */ - $denormalized_comment = $this->serializer->denormalize($normalized, 'Drupal\comment\Entity\Comment', $this->format, ['account' => $account]); - - // Before comparing, unset values that are expected to differ. - $denormalized_comment_values = $denormalized_comment->toArray(); - unset($denormalized_comment_values['hostname'], $denormalized_comment_values['name']); - $this->assertEqual($original_values, $denormalized_comment_values, 'The expected comment values are restored after normalizing and denormalizing.'); - } - -} diff --git a/core/modules/hal/tests/src/Kernel/NormalizeTest.php b/core/modules/hal/tests/src/Kernel/NormalizeTest.php index 6ed064c3f3cc49cf16c1d89d15dc4ad766429c9e..cfffb4a8652d455f890db44e62733d00a7b6c2f8 100644 --- a/core/modules/hal/tests/src/Kernel/NormalizeTest.php +++ b/core/modules/hal/tests/src/Kernel/NormalizeTest.php @@ -7,7 +7,7 @@ use Drupal\entity_test\Entity\EntityTest; /** - * Tests that entities can be normalized in HAL. + * Tests HAL normalization edge cases for EntityResource. * * @group hal */ diff --git a/core/modules/rest/src/Tests/AuthTest.php b/core/modules/rest/src/Tests/AuthTest.php deleted file mode 100644 index 9f4224f64f1a58e4ce494f67a0e136e8f9f0b383..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/AuthTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\Core\Url; - -/** - * Tests authentication provider restrictions. - * - * @group rest - */ -class AuthTest extends RESTTestBase { - - /** - * Modules to install. - * - * @var array - */ - public static $modules = array('basic_auth', 'hal', 'rest', 'entity_test'); - - /** - * Tests reading from an authenticated resource. - */ - public function testRead() { - $entity_type = 'entity_test'; - - // Enable a test resource through GET method and basic HTTP authentication. - $this->enableService('entity:' . $entity_type, 'GET', NULL, array('basic_auth')); - - // Create an entity programmatically. - $entity = $this->entityCreate($entity_type); - $entity->save(); - - // Try to read the resource as an anonymous user, which should not work. - $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET'); - $this->assertResponse('401', 'HTTP response code is 401 when the request is not authenticated and the user is anonymous.'); - $this->assertRaw(json_encode(['message' => 'A fatal error occurred: No authentication credentials provided.'])); - - // Ensure that cURL settings/headers aren't carried over to next request. - unset($this->curlHandle); - - // Create a user account that has the required permissions to read - // resources via the REST API, but the request is authenticated - // with session cookies. - $permissions = $this->entityPermissions($entity_type, 'view'); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - - // Try to read the resource with session cookie authentication, which is - // not enabled and should not work. - $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET'); - $this->assertResponse('403', 'HTTP response code is 403 when the request was authenticated by the wrong authentication provider.'); - - // Ensure that cURL settings/headers aren't carried over to next request. - unset($this->curlHandle); - - // Now read it with the Basic authentication which is enabled and should - // work. - $this->basicAuthGet($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), $account->getUsername(), $account->pass_raw); - $this->assertResponse('200', 'HTTP response code is 200 for successfully authenticated requests.'); - $this->curlClose(); - } - - /** - * Performs a HTTP request with Basic authentication. - * - * We do not use \Drupal\simpletest\WebTestBase::drupalGet because we need to - * set curl settings for basic authentication. - * - * @param \Drupal\Core\Url $url - * A Url object. - * @param string $username - * The user name to authenticate with. - * @param string $password - * The password. - * @param string $mime_type - * The MIME type for the Accept header. - * - * @return string - * Curl output. - */ - protected function basicAuthGet(Url $url, $username, $password, $mime_type = NULL) { - if (!isset($mime_type)) { - $mime_type = $this->defaultMimeType; - } - $out = $this->curlExec( - array( - CURLOPT_HTTPGET => TRUE, - CURLOPT_URL => $url->setAbsolute()->toString(), - CURLOPT_NOBODY => FALSE, - CURLOPT_HTTPAUTH => CURLAUTH_BASIC, - CURLOPT_USERPWD => $username . ':' . $password, - CURLOPT_HTTPHEADER => array('Accept: ' . $mime_type), - ) - ); - - $this->verbose('GET request to: ' . $url->toString() . - '<hr />' . $out); - - return $out; - } - -} diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php deleted file mode 100644 index 2454d2a0cb92b6839470d406002890e3a9fb8c3c..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/CreateTest.php +++ /dev/null @@ -1,484 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\comment\Tests\CommentTestTrait; -use Drupal\Component\Serialization\Json; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Url; -use Drupal\entity_test\Entity\EntityTest; -use Drupal\node\Entity\Node; -use Drupal\user\Entity\User; -use Drupal\comment\Entity\Comment; - -/** - * Tests the creation of resources. - * - * @group rest - */ -class CreateTest extends RESTTestBase { - - use CommentTestTrait; - /** - * Modules to install. - * - * @var array - */ - public static $modules = array('hal', 'rest', 'entity_test', 'comment', 'node'); - - /** - * The 'serializer' service. - * - * @var \Symfony\Component\Serializer\Serializer - */ - protected $serializer; - - protected function setUp() { - parent::setUp(); - $this->addDefaultCommentField('node', 'resttest'); - // Get the 'serializer' service. - $this->serializer = $this->container->get('serializer'); - } - - /** - * Try to create a resource which is not REST API enabled. - */ - public function testCreateResourceRestApiNotEnabled() { - $entity_type = 'entity_test'; - // Enables the REST service for a specific entity type. - $this->enableService('entity:' . $entity_type, 'POST'); - - // Get the necessary user permissions to create the current entity type. - $permissions = $this->entityPermissions($entity_type, 'create'); - - // Create the user. - $account = $this->drupalCreateUser($permissions); - // Populate some entity properties before create the entity. - $entity_values = $this->entityValues($entity_type); - $entity = EntityTest::create($entity_values); - - // Serialize the entity before the POST request. - $serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]); - - // Disable all resource types. - $this->enableService(FALSE); - $this->drupalLogin($account); - - // POST request to create the current entity. GET request for CSRF token - // is included into the httpRequest() method. - $this->httpRequest('entity/entity_test', 'POST', $serialized, $this->defaultMimeType); - - // The resource is not enabled. So, we receive a 'not found' response. - $this->assertResponse(404); - $this->assertFalse(EntityTest::loadMultiple(), 'No entity has been created in the database.'); - } - - /** - * Ensure that an entity cannot be created without the restful permission. - */ - public function testCreateWithoutPermissionIfBcFlagIsOn() { - $rest_settings = $this->config('rest.settings'); - $rest_settings->set('bc_entity_resource_permissions', TRUE) - ->save(TRUE); - - $entity_type = 'entity_test'; - // Enables the REST service for 'entity_test' entity type. - $this->enableService('entity:' . $entity_type, 'POST'); - $permissions = $this->entityPermissions($entity_type, 'create'); - // Create a user without the 'restful post entity:entity_test permission. - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - // Populate some entity properties before create the entity. - $entity_values = $this->entityValues($entity_type); - $entity = EntityTest::create($entity_values); - - // Serialize the entity before the POST request. - $serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]); - - // Create the entity over the REST API. - $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); - $this->assertResponse(403); - $this->assertFalse(EntityTest::loadMultiple(), 'No entity has been created in the database.'); - - // Create a user with the 'restful post entity:entity_test permission and - // try again. This time, we should be able to create an entity. - $permissions[] = 'restful post entity:' . $entity_type; - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); - $this->assertResponse(201); - } - - /** - * Tests valid and invalid create requests for 'entity_test' entity type. - */ - public function testCreateEntityTest() { - $entity_type = 'entity_test'; - // Enables the REST service for 'entity_test' entity type. - $this->enableService('entity:' . $entity_type, 'POST'); - // Create two accounts with the required permissions to create resources. - // The second one has administrative permissions. - $accounts = $this->createAccountPerEntity($entity_type); - - // Verify create requests per user. - foreach ($accounts as $key => $account) { - $this->drupalLogin($account); - // Populate some entity properties before create the entity. - $entity_values = $this->entityValues($entity_type); - $entity = EntityTest::create($entity_values); - - // Serialize the entity before the POST request. - $serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]); - - // Create the entity over the REST API. - $this->assertCreateEntityOverRestApi($entity_type, $serialized); - // Get the entity ID from the location header and try to read it from the - // database. - $this->assertReadEntityIdFromHeaderAndDb($entity_type, $entity, $entity_values); - - // Try to create an entity with an access protected field. - // @see entity_test_entity_field_access() - $normalized = $this->serializer->normalize($entity, $this->defaultFormat, ['account' => $account]); - $normalized['field_test_text'][0]['value'] = 'no access value'; - $this->httpRequest('entity/' . $entity_type, 'POST', $this->serializer->serialize($normalized, $this->defaultFormat, ['account' => $account]), $this->defaultMimeType); - $this->assertResponse(403); - $this->assertFalse(EntityTest::loadMultiple(), 'No entity has been created in the database.'); - - // Try to create a field with a text format this user has no access to. - $entity->field_test_text->value = $entity_values['field_test_text'][0]['value']; - $entity->field_test_text->format = 'full_html'; - - $serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]); - $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); - // The value selected is not a valid choice because the format must be - // 'plain_txt'. - $this->assertResponse(422); - $this->assertFalse(EntityTest::loadMultiple(), 'No entity has been created in the database.'); - - // Restore the valid test value. - $entity->field_test_text->format = 'plain_text'; - $serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]); - - // 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); - - // Try to create an entity without proper permissions. - $this->assertCreateEntityWithoutProperPermissions($entity_type, $serialized, ['account' => $account]); - - } - - } - - /** - * Tests several valid and invalid create requests for 'node' entity type. - */ - public function testCreateNode() { - $entity_type = 'node'; - // Enables the REST service for 'node' 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); - - // Verify create requests per user. - foreach ($accounts as $key => $account) { - $this->drupalLogin($account); - // Populate some entity properties before create the entity. - $entity_values = $this->entityValues($entity_type); - $entity = Node::create($entity_values); - - // Verify that user cannot create content when trying to write to fields - // where it is not possible. - if (!$account->hasPermission('administer nodes')) { - $serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]); - $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); - $this->assertResponse(403); - // Remove fields where non-administrative users cannot write. - $entity = $this->removeNodeFieldsForNonAdminUsers($entity); - } - else { - // Changed and revision_timestamp fields can never be added. - $entity->set('changed', NULL); - $entity->set('revision_timestamp', NULL); - } - - $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); - - // Try to create an entity without proper permissions. - $this->assertCreateEntityWithoutProperPermissions($entity_type, $serialized); - - } - - } - - /** - * 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. - */ - public function testCreateUser() { - $entity_type = 'user'; - // Enables the REST service for 'user' 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); - - foreach ($accounts as $key => $account) { - $this->drupalLogin($account); - $entity_values = $this->entityValues($entity_type); - $entity = User::create($entity_values); - - // Verify that only administrative users can create users. - if (!$account->hasPermission('administer users')) { - $serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]); - $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); - $this->assertResponse(403); - continue; - } - - // Changed field can never be added. - $entity->set('changed', NULL); - - $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); - } - - } - - /** - * Creates user accounts that have the required permissions to create - * resources via the REST API. The second one has administrative permissions. - * - * @param string $entity_type - * Entity type needed to apply user permissions. - * @return array - * An array that contains user accounts. - */ - public function createAccountPerEntity($entity_type) { - $accounts = array(); - // Get the necessary user permissions for the current $entity_type creation. - $permissions = $this->entityPermissions($entity_type, 'create'); - // Create user without administrative permissions. - $accounts[] = $this->drupalCreateUser($permissions); - // Add administrative permissions for nodes and users. - $permissions[] = 'administer nodes'; - $permissions[] = 'administer users'; - $permissions[] = 'administer comments'; - // Create an administrative user. - $accounts[] = $this->drupalCreateUser($permissions); - - return $accounts; - } - - /** - * Creates the entity over the REST API. - * - * @param string $entity_type - * The type of the entity that should be created. - * @param string $serialized - * The body for the POST request. - */ - public function assertCreateEntityOverRestApi($entity_type, $serialized = NULL) { - // Note: this will fail with PHP 5.6 when always_populate_raw_post_data is - // set to something other than -1. See https://www.drupal.org/node/2456025. - // Try first without the CSRF token, which should fail. - $url = Url::fromUri('internal:/entity/' . $entity_type)->setOption('query', ['_format' => $this->defaultFormat]); - $this->httpRequest($url, 'POST', $serialized, $this->defaultMimeType, FALSE); - $this->assertResponse(403); - $this->assertRaw('X-CSRF-Token request header is missing'); - // Then try with an invalid CSRF token. - $this->httpRequest($url, 'POST', $serialized, $this->defaultMimeType, 'invalid-csrf-token'); - $this->assertResponse(403); - $this->assertRaw('X-CSRF-Token request header is invalid'); - // Then try with the CSRF token. - $response = $this->httpRequest($url, 'POST', $serialized, $this->defaultMimeType); - $this->assertResponse(201); - - // Make sure that the response includes an entity in the body and check the - // UUID as an example. - $request = Json::decode($serialized); - $response = Json::decode($response); - $this->assertEqual($request['uuid'][0]['value'], $response['uuid'][0]['value'], 'Got new entity created as response after successful POST over Rest API'); - } - - /** - * Gets the new entity ID from the location header and tries to read it from - * the database. - * - * @param string $entity_type - * Entity type we need to load the entity from DB. - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity we want to check that was inserted correctly. - * @param array $entity_values - * The values of $entity. - */ - public function assertReadEntityIdFromHeaderAndDb($entity_type, EntityInterface $entity, array $entity_values = array()) { - // Get the location from the HTTP response header. - $location_url = $this->drupalGetHeader('location'); - $url_parts = explode('/', $location_url); - $id = end($url_parts); - - // Get the entity using the ID found. - $loaded_entity = \Drupal::entityManager()->getStorage($entity_type)->load($id); - $this->assertNotIdentical(FALSE, $loaded_entity, 'The new ' . $entity_type . ' was found in the database.'); - $this->assertEqual($entity->uuid(), $loaded_entity->uuid(), 'UUID of created entity is correct.'); - - // Verify that the field values sent and received from DB are the same. - foreach ($entity_values as $property => $value) { - $actual_value = $loaded_entity->get($property)->value; - $send_value = $entity->get($property)->value; - $this->assertEqual($send_value, $actual_value, 'Created property ' . $property . ' expected: ' . $send_value . ', actual: ' . $actual_value); - } - - // Delete the entity loaded from DB. - $loaded_entity->delete(); - } - - /** - * Try to send invalid data that cannot be correctly deserialized. - * - * @param string $entity_type - * The type of the entity that should be created. - */ - public function assertCreateEntityInvalidData($entity_type) { - $this->httpRequest('entity/' . $entity_type, 'POST', 'kaboom!', $this->defaultMimeType); - $this->assertResponse(400); - } - - /** - * Try to send no data at all, which does not make sense on POST requests. - * - * @param string $entity_type - * The type of the entity that should be created. - */ - public function assertCreateEntityNoData($entity_type) { - $this->httpRequest('entity/' . $entity_type, 'POST', NULL, $this->defaultMimeType); - $this->assertResponse(400); - } - - /** - * Send an invalid UUID to trigger the entity validation. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity we want to check that was inserted correctly. - * @param string $entity_type - * The type of the entity that should be created. - * @param array $context - * Options normalizers/encoders have access to. - */ - public function assertCreateEntityInvalidSerialized(EntityInterface $entity, $entity_type, array $context = array()) { - // Add a UUID that is too long. - $entity->set('uuid', $this->randomMachineName(129)); - $invalid_serialized = $this->serializer->serialize($entity, $this->defaultFormat, $context); - - $response = $this->httpRequest(Url::fromRoute("rest.entity.$entity_type.POST")->setRouteParameter('_format', $this->defaultFormat), 'POST', $invalid_serialized, $this->defaultMimeType); - - // Unprocessable Entity as response. - $this->assertResponse(422); - - // Verify that the text of the response is correct. - $error = Json::decode($response); - $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nuuid.0.value: <em class=\"placeholder\">UUID</em>: may not be longer than 128 characters.\n"); - } - - /** - * Try to create an entity without proper permissions. - * - * @param string $entity_type - * The type of the entity that should be created. - * @param string $serialized - * The body for the POST request. - */ - public function assertCreateEntityWithoutProperPermissions($entity_type, $serialized = NULL) { - $this->drupalLogout(); - $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); - // Forbidden Error as response. - $this->assertResponse(403); - $this->assertFalse(\Drupal::entityManager()->getStorage($entity_type)->loadMultiple(), 'No entity has been created in the database.'); - } - -} diff --git a/core/modules/rest/src/Tests/CsrfTest.php b/core/modules/rest/src/Tests/CsrfTest.php deleted file mode 100644 index f0063ad8e1cd4544e08ff78601b19a97b5125aa7..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/CsrfTest.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\Core\Url; - -/** - * Tests the CSRF protection. - * - * @group rest - */ -class CsrfTest extends RESTTestBase { - - /** - * Modules to install. - * - * @var array - */ - public static $modules = array('hal', 'rest', 'entity_test', 'basic_auth'); - - /** - * A testing user account. - * - * @var \Drupal\user\Entity\User - */ - protected $account; - - /** - * The serialized entity. - * - * @var string - */ - protected $serialized; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->enableService('entity:' . $this->testEntityType, 'POST', 'hal_json', array('basic_auth', 'cookie')); - - // Create a user account that has the required permissions to create - // resources via the REST API. - $permissions = $this->entityPermissions($this->testEntityType, 'create'); - $this->account = $this->drupalCreateUser($permissions); - - // Serialize an entity to a string to use in the content body of the POST - // request. - $serializer = $this->container->get('serializer'); - $entity_values = $this->entityValues($this->testEntityType); - $entity = $this->container->get('entity_type.manager') - ->getStorage($this->testEntityType) - ->create($entity_values); - $this->serialized = $serializer->serialize($entity, $this->defaultFormat); - } - - /** - * Tests that CSRF check is not triggered for Basic Auth requests. - */ - public function testBasicAuth() { - $curl_options = $this->getCurlOptions(); - $curl_options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; - $curl_options[CURLOPT_USERPWD] = $this->account->getUsername() . ':' . $this->account->pass_raw; - $this->curlExec($curl_options); - $this->assertResponse(201); - // Ensure that the entity was created. - $loaded_entity = $this->loadEntityFromLocationHeader($this->drupalGetHeader('location')); - $this->assertTrue($loaded_entity, 'An entity was created in the database'); - } - - /** - * Tests that CSRF check is triggered for Cookie Auth requests. - * - * @deprecated as of Drupal 8.2.x, will be removed before Drupal 9.0.0. Use - * \Drupal\Tests\system\Functional\CsrfRequestHeaderTest::testRouteAccess - * instead. - */ - public function testCookieAuth() { - $this->drupalLogin($this->account); - - $curl_options = $this->getCurlOptions(); - - // Try to create an entity without the CSRF token. - // Note: this will fail with PHP 5.6 when always_populate_raw_post_data is - // set to something other than -1. See https://www.drupal.org/node/2456025. - $this->curlExec($curl_options); - $this->assertResponse(403); - // Ensure that the entity was not created. - $storage = $this->container->get('entity_type.manager') - ->getStorage($this->testEntityType); - $storage->resetCache(); - $this->assertFalse($storage->loadMultiple(), 'No entity has been created in the database.'); - - // Create an entity with the CSRF token. - $token = $this->drupalGet('rest/session/token'); - $curl_options[CURLOPT_HTTPHEADER][] = "X-CSRF-Token: $token"; - $this->curlExec($curl_options); - $this->assertResponse(201); - // Ensure that the entity was created. - $loaded_entity = $this->loadEntityFromLocationHeader($this->drupalGetHeader('location')); - $this->assertTrue($loaded_entity, 'An entity was created in the database'); - } - - /** - * Gets the cURL options to create an entity with POST. - * - * @return array - * The array of cURL options. - */ - protected function getCurlOptions() { - return array( - CURLOPT_HTTPGET => FALSE, - CURLOPT_POST => TRUE, - CURLOPT_POSTFIELDS => $this->serialized, - CURLOPT_URL => Url::fromRoute('rest.entity.' . $this->testEntityType . '.POST')->setAbsolute()->toString(), - CURLOPT_NOBODY => FALSE, - CURLOPT_HTTPHEADER => array( - "Content-Type: {$this->defaultMimeType}", - ), - ); - } - -} diff --git a/core/modules/rest/src/Tests/DeleteTest.php b/core/modules/rest/src/Tests/DeleteTest.php deleted file mode 100644 index 88db0fd9df7ca630aa1f766ad3c4b68a0be6ffb8..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/DeleteTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\Core\Url; - -/** - * Tests the deletion of resources. - * - * @group rest - */ -class DeleteTest extends RESTTestBase { - - /** - * Modules to install. - * - * @var array - */ - public static $modules = array('hal', 'rest', 'entity_test', 'node'); - - /** - * Tests several valid and invalid delete requests on all entity types. - */ - public function testDelete() { - // Define the entity types we want to test. - // @todo expand this test to at least users once their access - // controllers are implemented. - $entity_types = array('entity_test', 'node'); - foreach ($entity_types as $entity_type) { - $this->enableService('entity:' . $entity_type, 'DELETE'); - // Create a user account that has the required permissions to delete - // resources via the REST API. - $permissions = $this->entityPermissions($entity_type, 'delete'); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - - // Create an entity programmatically. - $entity = $this->entityCreate($entity_type); - $entity->save(); - // Try first to delete over REST API without the CSRF token. - $url = $entity->toUrl()->setRouteParameter('_format', $this->defaultFormat); - $this->httpRequest($url, 'DELETE', NULL, 'application/hal+json', FALSE); - $this->assertResponse(403); - $this->assertRaw('X-CSRF-Token request header is missing'); - // Then try with an invalid CSRF token. - $this->httpRequest($url, 'DELETE', NULL, 'application/hal+json', 'invalid-csrf-token'); - $this->assertResponse(403); - $this->assertRaw('X-CSRF-Token request header is invalid'); - // Delete it over the REST API. - $response = $this->httpRequest($url, 'DELETE'); - $this->assertResponse(204); - // Clear the static cache with entity_load(), otherwise we won't see the - // update. - $storage = $this->container->get('entity_type.manager') - ->getStorage($entity_type); - $storage->resetCache([$entity->id()]); - $entity = $storage->load($entity->id()); - $this->assertFalse($entity, $entity_type . ' entity is not in the DB anymore.'); - $this->assertResponse('204', 'HTTP response code is correct.'); - $this->assertEqual($response, '', 'Response body is empty.'); - - // Try to delete an entity that does not exist. - $response = $this->httpRequest(Url::fromRoute('entity.' . $entity_type . '.canonical', [$entity_type => 9999]), 'DELETE'); - $this->assertResponse(404); - $this->assertText('The requested page could not be found.'); - - // Try to delete an entity without proper permissions. - $this->drupalLogout(); - // Re-save entity to the database. - $entity = $this->entityCreate($entity_type); - $entity->save(); - $this->httpRequest($entity->urlInfo(), 'DELETE'); - $this->assertResponse(403); - $storage->resetCache([$entity->id()]); - $this->assertNotIdentical(FALSE, $storage->load($entity->id()), - 'The ' . $entity_type . ' entity is still in the database.'); - } - // Try to delete a resource which is not REST API enabled. - $this->enableService(FALSE); - $account = $this->drupalCreateUser(); - $this->drupalLogin($account); - $this->httpRequest($account->urlInfo(), 'DELETE'); - $user_storage = $this->container->get('entity.manager')->getStorage('user'); - $user_storage->resetCache(array($account->id())); - $user = $user_storage->load($account->id()); - $this->assertEqual($account->id(), $user->id(), 'User still exists in the database.'); - $this->assertResponse(405); - } - -} diff --git a/core/modules/rest/src/Tests/NodeTest.php b/core/modules/rest/src/Tests/NodeTest.php deleted file mode 100644 index 95dc475e0e83d07ecb1ba0af7406cdf054b4cc76..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/NodeTest.php +++ /dev/null @@ -1,197 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\Core\Url; -use Drupal\node\Entity\Node; - -/** - * Tests special cases for node entities. - * - * @group rest - */ -class NodeTest extends RESTTestBase { - - /** - * Modules to install. - * - * Ensure that the node resource works with comment module enabled. - * - * @var array - */ - public static $modules = array('hal', 'rest', 'comment', 'node'); - - /** - * Enables node specific REST API configuration and authentication. - * - * @param string $method - * The HTTP method to be tested. - * @param string $operation - * The operation, one of 'view', 'create', 'update' or 'delete'. - */ - protected function enableNodeConfiguration($method, $operation) { - $this->enableService('entity:node', $method); - $permissions = $this->entityPermissions('node', $operation); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - } - - /** - * Serializes and attempts to create a node via a REST "post" http request. - * - * @param array $data - */ - protected function postNode($data) { - // Enable node creation via POST. - $this->enableNodeConfiguration('POST', 'create'); - $this->enableService('entity:node', 'POST', 'json'); - - // Create a JSON version of a simple node with the title. - $serialized = $this->container->get('serializer')->serialize($data, 'json'); - - // Post to the REST service to create the node. - $this->httpRequest('/entity/node', 'POST', $serialized, 'application/json'); - } - - /** - * Tests the title on a newly created node. - * - * @param array $data - * @return \Drupal\node\Entity\Node - */ - protected function assertNodeTitleMatch($data) { - /** @var \Drupal\node\Entity\Node $node */ - // Load the newly created node. - $node = Node::load(1); - - // Test that the title is the same as what we posted. - $this->assertEqual($node->title->value, $data['title'][0]['value']); - - return $node; - } - - /** - * Performs various tests on nodes and their REST API. - */ - public function testNodes() { - $node_storage = $this->container->get('entity.manager')->getStorage('node'); - $this->enableNodeConfiguration('GET', 'view'); - - $node = $this->entityCreate('node'); - $node->save(); - $this->httpRequest($node->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET'); - $this->assertResponse(200); - $this->assertHeader('Content-type', $this->defaultMimeType); - - // Also check that JSON works and the routing system selects the correct - // REST route. - $this->enableService('entity:node', 'GET', 'json'); - $this->httpRequest($node->urlInfo()->setRouteParameter('_format', 'json'), 'GET'); - $this->assertResponse(200); - $this->assertHeader('Content-type', 'application/json'); - - // Check that a simple PATCH update to the node title works as expected. - $this->enableNodeConfiguration('PATCH', 'update'); - - // Create a PATCH request body that only updates the title field. - $new_title = $this->randomString(); - $data = array( - '_links' => array( - 'type' => array( - 'href' => Url::fromUri('base:rest/type/node/resttest', array('absolute' => TRUE))->toString(), - ), - ), - 'title' => array( - array( - 'value' => $new_title, - ), - ), - ); - $serialized = $this->container->get('serializer')->serialize($data, $this->defaultFormat); - $this->httpRequest($node->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(200); - - // Reload the node from the DB and check if the title was correctly updated. - $node_storage->resetCache(array($node->id())); - $updated_node = $node_storage->load($node->id()); - $this->assertEqual($updated_node->getTitle(), $new_title); - // Make sure that the UUID of the node has not changed. - $this->assertEqual($node->get('uuid')->getValue(), $updated_node->get('uuid')->getValue(), 'UUID was not changed.'); - } - - /** - * Test creating a node using json serialization. - */ - public function testCreate() { - // Data to be used for serialization. - $data = [ - 'type' => [['target_id' => 'resttest']], - 'title' => [['value' => $this->randomString() ]], - ]; - - $this->postNode($data); - - // Make sure the response is "CREATED". - $this->assertResponse(201); - - // Make sure the node was created and the title matches. - $node = $this->assertNodeTitleMatch($data); - - // Make sure the request returned a redirect header to view the node. - $this->assertHeader('Location', $node->url('canonical', ['absolute' => TRUE])); - } - - /** - * Test bundle normalization when posting bundle as a simple string. - */ - public function testBundleNormalization() { - // Data to be used for serialization. - $data = [ - 'type' => 'resttest', - 'title' => [['value' => $this->randomString() ]], - ]; - - $this->postNode($data); - - // Make sure the response is "CREATED". - $this->assertResponse(201); - - // Make sure the node was created and the title matches. - $this->assertNodeTitleMatch($data); - } - - /** - * Test bundle normalization when posting using a simple string. - */ - public function testInvalidBundle() { - // Data to be used for serialization. - $data = [ - 'type' => 'bad_bundle_name', - 'title' => [['value' => $this->randomString() ]], - ]; - - $this->postNode($data); - - // Make sure the response is "Bad Request". - $this->assertResponse(400); - $this->assertResponseBody('{"error":"\"bad_bundle_name\" is not a valid bundle type for denormalization."}'); - } - - /** - * Test when the bundle is missing. - */ - public function testMissingBundle() { - // Data to be used for serialization. - $data = [ - 'title' => [['value' => $this->randomString() ]], - ]; - - // testing - $this->postNode($data); - - // Make sure the response is "Bad Request". - $this->assertResponse(400); - $this->assertResponseBody('{"error":"A string must be provided as a bundle value."}'); - } - -} diff --git a/core/modules/rest/src/Tests/PageCacheTest.php b/core/modules/rest/src/Tests/PageCacheTest.php deleted file mode 100644 index 66e57f5a54d65da3dc37084f845cc0b922afc690..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/PageCacheTest.php +++ /dev/null @@ -1,162 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\Component\Serialization\Json; -use Drupal\Core\Url; -use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait; - -/** - * Tests page caching for REST GET requests. - * - * @group rest - */ -class PageCacheTest extends RESTTestBase { - - use AssertPageCacheContextsAndTagsTrait; - - /** - * Modules to install. - * - * @var array - */ - public static $modules = array('hal'); - - /** - * The 'serializer' service. - * - * @var \Symfony\Component\Serializer\Serializer - */ - protected $serializer; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - // Get the 'serializer' service. - $this->serializer = $this->container->get('serializer'); - } - - /** - * Tests that configuration changes also clear the page cache. - */ - public function testConfigChangePageCache() { - // Allow anonymous users to issue GET requests. - user_role_grant_permissions('anonymous', ['view test entity', 'restful get entity:entity_test']); - - $this->enableService('entity:entity_test', 'POST'); - $permissions = [ - 'administer entity_test content', - ]; - $account = $this->drupalCreateUser($permissions); - - // Create an entity and test that the response from a post request is not - // cacheable. - $entity = $this->entityCreate('entity_test'); - $entity->set('field_test_text', 'custom cache tag value'); - $serialized = $this->serializer->serialize($entity, $this->defaultFormat); - // Log in for creating the entity. - $this->drupalLogin($account); - $this->httpRequest('entity/entity_test', 'POST', $serialized, $this->defaultMimeType); - $this->assertResponse(201); - - if ($this->getCacheHeaderValues('x-drupal-cache')) { - $this->fail('Post request is cached.'); - } - $this->drupalLogout(); - - $url = Url::fromUri('internal:/entity_test/1?_format=' . $this->defaultFormat); - - // Read it over the REST API. - $this->enableService('entity:entity_test', 'GET'); - $this->httpRequest($url, 'GET', NULL, $this->defaultMimeType); - $this->assertResponse(200, 'HTTP response code is correct.'); - $this->assertHeader('x-drupal-cache', 'MISS'); - $this->assertCacheTag('config:rest.resource.entity.entity_test'); - $this->assertCacheTag('entity_test:1'); - $this->assertCacheTag('entity_test_access:field_test_text'); - - // Read it again, should be page-cached now. - $this->httpRequest($url, 'GET', NULL, $this->defaultMimeType); - $this->assertResponse(200, 'HTTP response code is correct.'); - $this->assertHeader('x-drupal-cache', 'HIT'); - $this->assertCacheTag('config:rest.resource.entity.entity_test'); - $this->assertCacheTag('entity_test:1'); - $this->assertCacheTag('entity_test_access:field_test_text'); - - // Trigger a resource config save which should clear the page cache, so we - // should get a cache miss now for the same request. - $this->resourceConfigStorage->load('entity.entity_test')->save(); - $this->httpRequest($url, 'GET', NULL, $this->defaultMimeType); - $this->assertResponse(200, 'HTTP response code is correct.'); - $this->assertHeader('x-drupal-cache', 'MISS'); - $this->assertCacheTag('config:rest.resource.entity.entity_test'); - $this->assertCacheTag('entity_test:1'); - $this->assertCacheTag('entity_test_access:field_test_text'); - - // Log in for deleting / updating entity. - $this->drupalLogin($account); - - // Test that updating an entity is not cacheable. - $this->enableService('entity:entity_test', 'PATCH'); - - // Create a second stub entity for overwriting a field. - $patch_values['field_test_text'] = [0 => [ - 'value' => 'patched value', - 'format' => 'plain_text', - ]]; - $patch_entity = $this->container->get('entity_type.manager') - ->getStorage('entity_test') - ->create($patch_values); - // We don't want to overwrite the UUID. - $patch_entity->set('uuid', NULL); - $serialized = $this->container->get('serializer') - ->serialize($patch_entity, $this->defaultFormat); - - // Update the entity over the REST API. - $this->httpRequest($url, 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(200); - - if ($this->getCacheHeaderValues('x-drupal-cache')) { - $this->fail('Patch request is cached.'); - } - - // Test that the response from a delete request is not cacheable. - $this->enableService('entity:entity_test', 'DELETE'); - $this->httpRequest($url, 'DELETE'); - $this->assertResponse(204); - - if ($this->getCacheHeaderValues('x-drupal-cache')) { - $this->fail('Patch request is cached.'); - } - } - - /** - * Tests HEAD support when a REST resource supports GET. - */ - public function testHeadSupport() { - user_role_grant_permissions('anonymous', ['view test entity', 'restful get entity:entity_test']); - - // Create an entity programatically. - $this->entityCreate('entity_test')->save(); - - $url = Url::fromUri('internal:/entity_test/1?_format=' . $this->defaultFormat); - - $this->enableService('entity:entity_test', 'GET'); - - $this->httpRequest($url, 'HEAD', NULL, $this->defaultMimeType); - $this->assertResponse(200, 'HTTP response code is correct.'); - $this->assertHeader('X-Drupal-Cache', 'MISS'); - $this->assertResponseBody(''); - - $response = $this->httpRequest($url, 'GET', NULL, $this->defaultMimeType); - $this->assertResponse(200, 'HTTP response code is correct.'); - $this->assertHeader('X-Drupal-Cache', 'HIT'); - $this->assertCacheTag('config:rest.resource.entity.entity_test'); - $this->assertCacheTag('entity_test:1'); - $data = Json::decode($response); - $this->assertEqual($data['type'][0]['value'], 'entity_test'); - } - -} diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php index 7cb0f1bde4d4993fe593b6ad6c0f7424f1dfda75..c755967eabbe1823e14c098e5382a91fb2caae2a 100644 --- a/core/modules/rest/src/Tests/RESTTestBase.php +++ b/core/modules/rest/src/Tests/RESTTestBase.php @@ -9,6 +9,8 @@ /** * Test helper class that provides a REST client method to send HTTP requests. + * + * @deprecated in Drupal 8.3.x-dev and will be removed before Drupal 9.0.0. Use \Drupal\Tests\rest\Functional\ResourceTestBase and \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase instead. Only retained for contributed module tests that may be using this base class. */ abstract class RESTTestBase extends WebTestBase { diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php deleted file mode 100644 index dc6f5743793df6bc3f42ee7693468a9eb488e8bf..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/ReadTest.php +++ /dev/null @@ -1,205 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\Component\Serialization\Json; -use Drupal\Core\Config\Entity\ConfigEntityInterface; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Url; - -/** - * Tests the retrieval of resources. - * - * @group rest - */ -class ReadTest extends RESTTestBase { - - /** - * Modules to install. - * - * @var array - */ - public static $modules = [ - 'hal', - 'rest', - 'node', - 'entity_test', - 'config_test', - 'taxonomy', - 'block', - ]; - - /** - * Tests several valid and invalid read requests on all entity types. - */ - public function testRead() { - // @todo Expand this at least to users. - // Define the entity types we want to test. - $entity_types = [ - 'entity_test', - 'node', - 'config_test', - 'taxonomy_vocabulary', - 'block', - 'user_role', - ]; - foreach ($entity_types as $entity_type) { - $this->enableService('entity:' . $entity_type, 'GET'); - // Create a user account that has the required permissions to read - // resources via the REST API. - $permissions = $this->entityPermissions($entity_type, 'view'); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - - // Create an entity programmatically. - $entity = $this->entityCreate($entity_type); - $entity->save(); - - // Verify that it exists: use a HEAD request. - $this->httpRequest($this->getReadUrl($entity), 'HEAD'); - $this->assertResponseBody(''); - $head_headers = $this->drupalGetHeaders(); - - // Read it over the REST API. - $response = $this->httpRequest($this->getReadUrl($entity), 'GET'); - $get_headers = $this->drupalGetHeaders(); - $this->assertResponse('200', 'HTTP response code is correct.'); - - // Verify that the GET and HEAD responses are the same, that the only - // difference is that there's no body. - unset($get_headers['date']); - unset($head_headers['date']); - unset($get_headers['content-length']); - unset($head_headers['content-length']); - unset($get_headers['x-drupal-dynamic-cache']); - unset($head_headers['x-drupal-dynamic-cache']); - $this->assertIdentical($get_headers, $head_headers); - $this->assertResponse('200', 'HTTP response code is correct.'); - - $this->assertHeader('content-type', $this->defaultMimeType); - $data = Json::decode($response); - // Only assert one example property here, other properties should be - // checked in serialization tests. - if ($entity instanceof ConfigEntityInterface) { - $this->assertEqual($data['uuid'], $entity->uuid(), 'Entity UUID is correct'); - } - else { - $this->assertEqual($data['uuid'][0]['value'], $entity->uuid(), 'Entity UUID is correct'); - } - - // Try to read the entity with an unsupported mime format. - $this->httpRequest($this->getReadUrl($entity, 'wrongformat'), 'GET'); - $this->assertResponse(406); - $this->assertHeader('Content-type', 'application/json'); - - // Try to read an entity that does not exist. - $response = $this->httpRequest($this->getReadUrl($entity, $this->defaultFormat, 9999), 'GET'); - $this->assertResponse(404); - switch ($entity_type) { - case 'node': - $path = '/node/{node}'; - break; - - case 'entity_test': - $path = '/entity_test/{entity_test}'; - break; - - default: - $path = "/entity/$entity_type/{" . $entity_type . '}'; - } - $expected_message = Json::encode(['message' => 'The "' . $entity_type . '" parameter was not converted for the path "' . $path . '" (route name: "rest.entity.' . $entity_type . '.GET.hal_json")']); - $this->assertIdentical($expected_message, $response, 'Response message is correct.'); - - // Make sure that field level access works and that the according field is - // not available in the response. Only applies to entity_test. - // @see entity_test_entity_field_access() - if ($entity_type == 'entity_test') { - $entity->field_test_text->value = 'no access value'; - $entity->save(); - $response = $this->httpRequest($this->getReadUrl($entity), 'GET'); - $this->assertResponse(200); - $this->assertHeader('content-type', $this->defaultMimeType); - $data = Json::decode($response); - $this->assertFalse(isset($data['field_test_text']), 'Field access protected field is not visible in the response.'); - } - } - // Try to read a resource, the user entity, which is not REST API enabled. - $account = $this->drupalCreateUser(); - $this->drupalLogin($account); - $response = $this->httpRequest($this->getReadUrl($account), 'GET'); - - // \Drupal\Core\Routing\RequestFormatRouteFilter considers the canonical, - // non-REST route a match, but a lower quality one: no format restrictions - // means there's always a match and hence when there is no matching REST - // route, the non-REST route is used, but can't render into - // application/hal+json, so it returns a 406. - $this->assertResponse('406', 'HTTP response code is 406 when the resource does not define formats, because it falls back to the canonical, non-REST route.'); - $this->assertEqual($response, Json::encode([ - 'message' => 'Not acceptable format: hal_json', - ])); - } - - /** - * Tests the resource structure. - */ - public function testResourceStructure() { - // Enable a service with a format restriction but no authentication. - $this->enableService('entity:node', 'GET', 'json'); - // Create a user account that has the required permissions to read - // resources via the REST API. - $permissions = $this->entityPermissions('node', 'view'); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - - // Create an entity programmatically. - $entity = $this->entityCreate('node'); - $entity->save(); - - // Read it over the REST API. - $this->httpRequest($this->getReadUrl($entity, 'json'), 'GET'); - $this->assertResponse('200', 'HTTP response code is correct.'); - } - - /** - * Gets the read URL object for the entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to get the URL for. - * @param string $format - * The format to request the entity in. - * @param string $entity_id - * The entity ID to use in the URL, defaults to the entity's ID if know - * given. - * - * @return \Drupal\Core\Url - * The Url object. - */ - protected function getReadUrl(EntityInterface $entity, $format = NULL, $entity_id = NULL) { - if (!$format) { - $format = $this->defaultFormat; - } - if (!$entity_id) { - $entity_id = $entity->id(); - } - $entity_type = $entity->getEntityTypeId(); - if ($entity->hasLinkTemplate('canonical')) { - $url = $entity->toUrl('canonical'); - } - else { - $route_name = 'rest.entity.' . $entity_type . ".GET."; - // If testing unsupported format don't use the format to construct route - // name. This would give a RouteNotFoundException. - if ($format == 'wrongformat') { - $route_name .= $this->defaultFormat; - } - else { - $route_name .= $format; - } - $url = Url::fromRoute($route_name); - } - $url->setRouteParameter($entity_type, $entity_id); - $url->setRouteParameter('_format', $format); - return $url; - } - -} diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php deleted file mode 100644 index d3784ed66691af025548a8bfd69d1478547c16db..0000000000000000000000000000000000000000 --- a/core/modules/rest/src/Tests/UpdateTest.php +++ /dev/null @@ -1,396 +0,0 @@ -<?php - -namespace Drupal\rest\Tests; - -use Drupal\comment\Entity\Comment; -use Drupal\comment\Tests\CommentTestTrait; -use Drupal\Component\Serialization\Json; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Session\AccountInterface; -use Drupal\entity_test\Entity\EntityTest; -use Symfony\Component\HttpFoundation\Response; - -/** - * Tests the update of resources. - * - * @group rest - */ -class UpdateTest extends RESTTestBase { - - use CommentTestTrait; - - /** - * Modules to install. - * - * @var array - */ - public static $modules = ['hal', 'rest', 'entity_test', 'node', 'comment']; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - $this->addDefaultCommentField('entity_test', 'entity_test'); - } - - /** - * Tests several valid and invalid partial update requests on test entities. - */ - public function testPatchUpdate() { - $serializer = $this->container->get('serializer'); - // @todo Test all other entity types here as well. - $entity_type = 'entity_test'; - - $this->enableService('entity:' . $entity_type, 'PATCH'); - // Create a user account that has the required permissions to create - // resources via the REST API. - $permissions = $this->entityPermissions($entity_type, 'update'); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); - - $context = ['account' => $account]; - - // Create an entity and save it to the database. - $entity = $this->entityCreate($entity_type); - $entity->save(); - - // Create a second stub entity for overwriting a field. - $patch_values['field_test_text'] = array(0 => array( - 'value' => $this->randomString(), - 'format' => 'plain_text', - )); - $patch_entity = $this->container->get('entity_type.manager') - ->getStorage($entity_type) - ->create($patch_values); - // We don't want to overwrite the UUID. - $patch_entity->set('uuid', NULL); - $serialized = $serializer->serialize($patch_entity, $this->defaultFormat, $context); - - // Update the entity over the REST API but forget to specify a Content-Type - // header, this should throw the proper exception. - $this->httpRequest($entity->toUrl(), 'PATCH', $serialized, 'none'); - $this->assertResponse(Response::HTTP_UNSUPPORTED_MEDIA_TYPE); - $this->assertRaw('No route found that matches "Content-Type: none"'); - - // Update the entity over the REST API. - $response = $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(200); - - // Make sure that the response includes an entity in the body, check the - // updated field as an example. - $request = Json::decode($serialized); - $response = Json::decode($response); - $this->assertEqual($request['field_test_text'][0]['value'], $response['field_test_text'][0]['value']); - unset($request['_links']); - unset($response['_links']); - unset($response['id']); - unset($response['uuid']); - unset($response['name']); - $this->assertEqual($request, $response); - - // Re-load updated entity from the database. - $storage = $this->container->get('entity_type.manager') - ->getStorage($entity_type); - $storage->resetCache([$entity->id()]); - $entity = $storage->load($entity->id()); - $this->assertEqual($entity->field_test_text->value, $patch_entity->field_test_text->value, 'Field was successfully updated.'); - - // Make sure that the field does not get deleted if it is not present in the - // PATCH request. - $normalized = $serializer->normalize($patch_entity, $this->defaultFormat, $context); - unset($normalized['field_test_text']); - $serialized = $serializer->encode($normalized, $this->defaultFormat); - $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(200); - - $storage->resetCache([$entity->id()]); - $entity = $storage->load($entity->id()); - $this->assertNotNull($entity->field_test_text->value . 'Test field has not been deleted.'); - - // Try to empty a field. - $normalized['field_test_text'] = array(); - $serialized = $serializer->encode($normalized, $this->defaultFormat); - - // Update the entity over the REST API. - $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(200); - - // Re-load updated entity from the database. - $storage->resetCache([$entity->id()]); - $entity = $storage->load($entity->id(), TRUE); - $this->assertNull($entity->field_test_text->value, 'Test field has been cleared.'); - - // Enable access protection for the text field. - // @see entity_test_entity_field_access() - $entity->field_test_text->value = 'no edit access value'; - $entity->field_test_text->format = 'plain_text'; - $entity->save(); - - // Try to empty a field that is access protected. - $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(403); - - // Re-load the entity from the database. - $storage->resetCache([$entity->id()]); - $entity = $storage->load($entity->id()); - $this->assertEqual($entity->field_test_text->value, 'no edit access value', 'Text field was not deleted.'); - - // Try to update an access protected field. - $normalized = $serializer->normalize($patch_entity, $this->defaultFormat, $context); - $normalized['field_test_text'][0]['value'] = 'no access value'; - $serialized = $serializer->serialize($normalized, $this->defaultFormat, $context); - $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(403); - - // Re-load the entity from the database. - $storage->resetCache([$entity->id()]); - $entity = $storage->load($entity->id()); - $this->assertEqual($entity->field_test_text->value, 'no edit access value', 'Text field was not updated.'); - - // Try to update the field with a text format this user has no access to. - // First change the original field value so we're allowed to edit it again. - $entity->field_test_text->value = 'test'; - $entity->save(); - $patch_entity->set('field_test_text', array( - 'value' => 'test', - 'format' => 'full_html', - )); - $serialized = $serializer->serialize($patch_entity, $this->defaultFormat, $context); - $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(422); - - // Re-load the entity from the database. - $storage->resetCache([$entity->id()]); - $entity = $storage->load($entity->id()); - $this->assertEqual($entity->field_test_text->format, 'plain_text', 'Text format was not updated.'); - - // Restore the valid test value. - $entity->field_test_text->value = $this->randomString(); - $entity->save(); - - // Try to send no data at all, which does not make sense on PATCH requests. - $this->httpRequest($entity->urlInfo(), 'PATCH', NULL, $this->defaultMimeType); - $this->assertResponse(400); - - // Try to update a non-existing entity with ID 9999. - $this->httpRequest($entity_type . '/9999', 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(404); - $storage->resetCache([9999]); - $loaded_entity = $storage->load(9999); - $this->assertFalse($loaded_entity, 'Entity 9999 was not created.'); - - // Try to send invalid data to trigger the entity validation constraints. - // Send a UUID that is too long. - $entity->set('uuid', $this->randomMachineName(129)); - $invalid_serialized = $serializer->serialize($entity, $this->defaultFormat, $context); - $response = $this->httpRequest($entity->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $invalid_serialized, $this->defaultMimeType); - $this->assertResponse(422); - $error = Json::decode($response); - $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nuuid.0.value: <em class=\"placeholder\">UUID</em>: may not be longer than 128 characters.\n"); - - // Try to update an entity without proper permissions. - $this->drupalLogout(); - $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(403); - - // Try to update a resource which is not REST API enabled. - $this->enableService(FALSE); - $this->drupalLogin($account); - $this->httpRequest($entity->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(405); - } - - /** - * Tests several valid and invalid update requests for the 'user' entity type. - */ - public function testUpdateUser() { - $serializer = $this->container->get('serializer'); - $entity_type = 'user'; - // Enables the REST service for 'user' entity type. - $this->enableService('entity:' . $entity_type, 'PATCH'); - $permissions = $this->entityPermissions($entity_type, 'update'); - $account = $this->drupalCreateUser($permissions); - $account->set('mail', 'old-email@example.com'); - $this->drupalLogin($account); - - // Create an entity and save it to the database. - $account->save(); - $account->set('changed', NULL); - - // Try and set a new email without providing the password. - $account->set('mail', 'new-email@example.com'); - $context = ['account' => $account]; - $normalized = $serializer->normalize($account, $this->defaultFormat, $context); - $serialized = $serializer->serialize($normalized, $this->defaultFormat, $context); - $response = $this->httpRequest($account->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(422); - $error = Json::decode($response); - $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Email</em>.\n"); - - // Try and send the new email with a password. - $normalized['pass'][0]['existing'] = 'wrong'; - $serialized = $serializer->serialize($normalized, $this->defaultFormat, $context); - $response = $this->httpRequest($account->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(422); - $error = Json::decode($response); - $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Email</em>.\n"); - - // Try again with the password. - $normalized['pass'][0]['existing'] = $account->pass_raw; - $serialized = $serializer->serialize($normalized, $this->defaultFormat, $context); - $this->httpRequest($account->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(200); - - // Try to change the password without providing the current password. - $new_password = $this->randomString(); - $normalized = $serializer->normalize($account, $this->defaultFormat, $context); - $normalized['pass'][0]['value'] = $new_password; - $serialized = $serializer->serialize($normalized, $this->defaultFormat, $context); - $response = $this->httpRequest($account->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(422); - $error = Json::decode($response); - $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Password</em>.\n"); - - // Try again with the password. - $normalized['pass'][0]['existing'] = $account->pass_raw; - $serialized = $serializer->serialize($normalized, $this->defaultFormat, $context); - $this->httpRequest($account->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType); - $this->assertResponse(200); - - // Verify that we can log in with the new password. - $account->pass_raw = $new_password; - $this->drupalLogin($account); - } - - /** - * Test patching a comment using both HAL+JSON and JSON. - */ - public function testUpdateComment() { - $entity_type = 'comment'; - // Enables the REST service for 'comment' entity type. - $this->enableService('entity:' . $entity_type, 'PATCH', ['hal_json', 'json']); - $permissions = $this->entityPermissions($entity_type, 'update'); - $account = $this->drupalCreateUser($permissions); - $account->set('mail', 'old-email@example.com'); - $this->drupalLogin($account); - - // Create & save an entity to comment on, plus a comment. - $entity_test = EntityTest::create(); - $entity_test->save(); - $entity_values = $this->entityValues($entity_type); - $entity_values['entity_id'] = $entity_test->id(); - $entity_values['uid'] = $account->id(); - $comment = Comment::create($entity_values); - $comment->save(); - - $this->pass('Test case 1: PATCH comment using HAL+JSON.'); - $comment->setSubject('Initial subject')->save(); - $read_only_fields = [ - 'name', - 'created', - 'changed', - 'status', - 'thread', - 'entity_type', - 'field_name', - 'entity_id', - 'uid', - ]; - $this->assertNotEqual('Updated subject', $comment->getSubject()); - $comment->setSubject('Updated subject'); - $this->patchEntity($comment, $read_only_fields, $account, 'hal_json', 'application/hal+json'); - $comment = Comment::load($comment->id()); - $this->assertEqual('Updated subject', $comment->getSubject()); - - $this->pass('Test case 1: PATCH comment using JSON.'); - $comment->setSubject('Initial subject')->save(); - $read_only_fields = [ - 'pid', // Extra compared to HAL+JSON. - 'entity_id', - 'uid', - 'name', - 'homepage', // Extra compared to HAL+JSON. - 'created', - 'changed', - 'status', - 'thread', - 'entity_type', - 'field_name', - ]; - $this->assertNotEqual('Updated subject', $comment->getSubject()); - $comment->setSubject('Updated subject'); - $this->patchEntity($comment, $read_only_fields, $account, 'json', 'application/json'); - $comment = Comment::load($comment->id()); - $this->assertEqual('Updated subject', $comment->getSubject()); - } - - /** - * Patches an existing entity using the passed in (modified) entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The updated entity to send. - * @param string[] $read_only_fields - * Names of the fields that are read-only, in validation order. - * @param \Drupal\Core\Session\AccountInterface $account - * The account to use for serialization. - * @param string $format - * A serialization format. - * @param string $mime_type - * The MIME type corresponding to the specified serialization format. - */ - protected function patchEntity(EntityInterface $entity, array $read_only_fields, AccountInterface $account, $format, $mime_type) { - $serializer = $this->container->get('serializer'); - - $url = $entity->toUrl()->setRouteParameter('_format', $this->defaultFormat); - $context = ['account' => $account]; - // Certain fields are always read-only, others this user simply is not - // allowed to modify. For all of them, ensure they are not serialized, else - // we'll get a 403 plus an error message. - for ($i = 0; $i < count($read_only_fields); $i++) { - $field = $read_only_fields[$i]; - - $normalized = $serializer->normalize($entity, $format, $context); - if ($format !== 'hal_json') { - // The default normalizer always keeps fields, even if they are unset - // here because they should be omitted during a PATCH request. Therefore - // manually strip them - // @see \Drupal\Core\Entity\ContentEntityBase::__unset() - // @see \Drupal\serialization\Normalizer\EntityNormalizer::normalize() - // @see \Drupal\hal\Normalizer\ContentEntityNormalizer::normalize() - $read_only_fields_so_far = array_slice($read_only_fields, 0, $i); - $normalized = array_diff_key($normalized, array_flip($read_only_fields_so_far)); - } - $serialized = $serializer->serialize($normalized, $format, $context); - - $this->httpRequest($url, 'PATCH', $serialized, $mime_type); - $this->assertResponse(403); - $this->assertResponseBody('{"message":"Access denied on updating field \\u0027' . $field . '\\u0027."}'); - - if ($format === 'hal_json') { - // We've just tried with this read-only field, now unset it. - $entity->set($field, NULL); - } - } - - // Finally, with all read-only fields unset, the request should succeed. - $normalized = $serializer->normalize($entity, $format, $context); - if ($format !== 'hal_json') { - $normalized = array_diff_key($normalized, array_combine($read_only_fields, $read_only_fields)); - } - $serialized = $serializer->serialize($normalized, $format, $context); - - // Try first without CSRF token which should fail. - $this->httpRequest($url, 'PATCH', $serialized, $mime_type, FALSE); - $this->assertResponse(403); - $this->assertRaw('X-CSRF-Token request header is missing'); - // Then try with an invalid CSRF token. - $this->httpRequest($url, 'PATCH', $serialized, $mime_type, 'invalid-csrf-token'); - $this->assertResponse(403); - $this->assertRaw('X-CSRF-Token request header is invalid'); - // Then try with CSRF token. - $this->httpRequest($url, 'PATCH', $serialized, $mime_type); - $this->assertResponse(200); - } - -} diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 70b1de7638b3162b91bd33a8d33eb77d6bbac8dc..c946967d35ff284b2b282273ab4a806b506e7520 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -977,6 +977,7 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques // DX: 400 when incorrect entity type bundle is specified. + // @todo Change to 422 in https://www.drupal.org/node/2827084. $response = $this->request($method, $url, $request_options); // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. // $this->assertResourceErrorResponse(400, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response); @@ -991,6 +992,7 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques // DX: 400 when no entity type bundle is specified. + // @todo Change to 422 in https://www.drupal.org/node/2827084. $response = $this->request($method, $url, $request_options); // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. // $this->assertResourceErrorResponse(400, 'A string must be provided as a bundle value.', $response);