diff --git a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php index 62f5486..929dc7a 100644 --- a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php @@ -96,7 +96,7 @@ public function onKernelRequestFilterProvider(GetResponseEvent $event) { if (isset($this->filter) && $event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) { $request = $event->getRequest(); if ($this->authenticationProvider->applies($request) && !$this->filter->appliesToRoutedRequest($request, TRUE)) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedHttpException('Access denied in this route.'); } } } diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php index a5cb361..ed2ecae 100644 --- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php @@ -106,7 +106,7 @@ public static function create(ContainerInterface $container, array $configuratio public function get(EntityInterface $entity) { $entity_access = $entity->access('view', NULL, TRUE); if (!$entity_access->isAllowed()) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedHttpException('The current user does not have access to view the entity type {$entity->getEntityTypeId()} of bundle {$entity->bundle()}.'); } $response = new ResourceResponse($entity, 200); @@ -145,7 +145,7 @@ public function post(EntityInterface $entity = NULL) { } if (!$entity->access('create')) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedHttpException("Entity creation for entity type {$entity->getEntityTypeId()} of bundle {$entity->bundle()} is not allowed for the current user."); } $definition = $this->getPluginDefinition(); // Verify that the deserialized entity is of the type that we expect to @@ -200,7 +200,7 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity throw new BadRequestHttpException('Invalid entity type'); } if (!$original_entity->access('update')) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedHttpException("Entity update for entity type {$entity->getEntityTypeId()} of bundle {$entity->bundle()} is not allowed for the current user."); } // Overwrite the received properties. @@ -264,7 +264,8 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity */ public function delete(EntityInterface $entity) { if (!$entity->access('delete')) { - throw new AccessDeniedHttpException(); + // @todo Handle message for no bundle entities https://www.drupal.org/node/2300677. + throw new AccessDeniedHttpException("Entity deletion for entity type {$entity->getEntityTypeId()} of bundle {$entity->bundle()} is not allowed for the current user."); } try { $entity->delete(); diff --git a/core/modules/rest/tests/src/Functional/AnonResourceTestTrait.php b/core/modules/rest/tests/src/Functional/AnonResourceTestTrait.php index b05ddf2..0cd331f 100644 --- a/core/modules/rest/tests/src/Functional/AnonResourceTestTrait.php +++ b/core/modules/rest/tests/src/Functional/AnonResourceTestTrait.php @@ -24,8 +24,8 @@ /** * {@inheritdoc} */ - protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) { - throw new \LogicException('When testing for anonymous users, authentication cannot be missing.'); + protected function assertResponseWhenMissingAuthentication(ResponseInterface $response, $message = 'When testing for anonymous users, authentication cannot be missing.') { + throw new \LogicException($message); } /** diff --git a/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php b/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php index 6f8c621..00e205e 100644 --- a/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php +++ b/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php @@ -31,7 +31,7 @@ protected function getAuthenticationRequestOptions($method) { /** * {@inheritdoc} */ - protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) { + protected function assertResponseWhenMissingAuthentication(ResponseInterface $response, $message = '') { $this->assertResourceErrorResponse(401, 'No authentication credentials provided.', $response); } diff --git a/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php b/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php index 18dc296..5033d22 100644 --- a/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php +++ b/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php @@ -91,8 +91,8 @@ protected function getAuthenticationRequestOptions($method) { /** * {@inheritdoc} */ - protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) { - $this->assertResourceErrorResponse(403, '', $response); + protected function assertResponseWhenMissingAuthentication(ResponseInterface $response, $message = '') { + $this->assertResourceErrorResponse(403, $message, $response); } /** diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 973f5b9..23cea54 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -301,6 +301,7 @@ public function testGet() { // response because ?_format query string is present. $response = $this->request('GET', $url, $request_options); if ($has_canonical_url) { + // @todo Update message https://www.drupal.org/node/2808233 $this->assertResourceErrorResponse(403, '', $response); } else { @@ -332,7 +333,7 @@ public function testGet() { // response. if (static::$auth) { $response = $this->request('GET', $url, $request_options); - $this->assertResponseWhenMissingAuthentication($response); + $this->assertResponseWhenMissingAuthentication($response, "Authentication credentials required to see the requested entity."); } $request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions('GET')); @@ -341,7 +342,7 @@ public function testGet() { // DX: 403 when unauthorized. $response = $this->request('GET', $url, $request_options); // @todo Update the message in https://www.drupal.org/node/2808233. - $this->assertResourceErrorResponse(403, '', $response); + $this->assertResourceErrorResponse(403, 'The current user does not have access to view the requested entity.', $response); $this->setUpAuthorization('GET'); @@ -545,7 +546,11 @@ public function testPost() { // DX: forgetting authentication: authentication provider-specific error // response. $response = $this->request('POST', $url, $request_options); - $this->assertResponseWhenMissingAuthentication($response); + if ($bundle = $this->entity->bundle()) { + // @todo Handle message for no bundle entities https://www.drupal.org/node/2300677. + $this->assertResponseWhenMissingAuthentication($response, "Entity creation for entity type {$this->entity->getEntityTypeId()} of bundle $bundle is not allowed for the current user."); + } + } @@ -554,8 +559,10 @@ public function testPost() { // DX: 403 when unauthorized. $response = $this->request('POST', $url, $request_options); - // @todo Update the message in https://www.drupal.org/node/2808233. - $this->assertResourceErrorResponse(403, '', $response); + if ($bundle = $this->entity->bundle()) { + // @todo Handle message for no bundle entities https://www.drupal.org/node/2300677. + $this->assertResourceErrorResponse(403, "Entity creation for entity type {$this->entity->getEntityTypeId()} of bundle $bundle is not allowed for the current user.", $response); + } $this->setUpAuthorization('POST'); @@ -745,7 +752,10 @@ public function testPatch() { // DX: forgetting authentication: authentication provider-specific error // response. $response = $this->request('PATCH', $url, $request_options); - $this->assertResponseWhenMissingAuthentication($response); + if ($bundle = $this->entity->bundle()) { + // @todo Handle message for no bundle entities https://www.drupal.org/node/2300677. + $this->assertResponseWhenMissingAuthentication($response, "Entity update for entity type {$this->entity->getEntityTypeId()} of bundle $bundle is not allowed for the current user."); + } } @@ -754,8 +764,10 @@ public function testPatch() { // DX: 403 when unauthorized. $response = $this->request('PATCH', $url, $request_options); - // @todo Update the message in https://www.drupal.org/node/2808233. - $this->assertResourceErrorResponse(403, '', $response); + if ($bundle = $this->entity->bundle()) { + // @todo Handle message for no bundle entities https://www.drupal.org/node/2300677. + $this->assertResourceErrorResponse(403, "Entity update for entity type {$this->entity->getEntityTypeId()} of bundle $bundle is not allowed for the current user.", $response); + } $this->setUpAuthorization('PATCH'); @@ -902,7 +914,11 @@ public function testDelete() { // DX: forgetting authentication: authentication provider-specific error // response. $response = $this->request('DELETE', $url, $request_options); - $this->assertResponseWhenMissingAuthentication($response); + if ($bundle = $this->entity->bundle()) { + // @todo Handle message for no bundle entities https://www.drupal.org/node/2300677. + $this->assertResponseWhenMissingAuthentication($response, "Entity deletion for entity type {$this->entity->getEntityTypeId()} of bundle $bundle is not allowed for the current user."); + } + } @@ -911,8 +927,11 @@ public function testDelete() { // DX: 403 when unauthorized. $response = $this->request('DELETE', $url, $request_options); - // @todo Update the message in https://www.drupal.org/node/2808233. - $this->assertResourceErrorResponse(403, '', $response); + if ($bundle = $this->entity->bundle()) { + // @todo Handle message for no bundle entities https://www.drupal.org/node/2300677. + $this->assertResourceErrorResponse(403, "Entity deletion for entity type {$this->entity->getEntityTypeId()} of bundle $bundle is not allowed for the current user.", $response); + } + $this->setUpAuthorization('DELETE'); diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php b/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php index 75fcd08..f956bfa 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Role; use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait; +use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait; use Psr\Http\Message\ResponseInterface; /** @@ -37,12 +38,9 @@ class RoleJsonBasicAuthTest extends RoleResourceTestBase { */ protected static $auth = 'basic_auth'; - /** - * {@inheritdoc} - */ - protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) { - $this->assertSame(401, $response->getStatusCode()); - $this->assertSame('{"message":"A fatal error occurred: No authentication credentials provided."}', (string) $response->getBody()); + // @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage. + use JsonBasicAuthWorkaroundFor2805281Trait { + JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait; } } diff --git a/core/modules/rest/tests/src/Functional/JsonBasicAuthWorkaroundFor2805281Trait.php b/core/modules/rest/tests/src/Functional/JsonBasicAuthWorkaroundFor2805281Trait.php index 495bf5a..dc9d8c4 100644 --- a/core/modules/rest/tests/src/Functional/JsonBasicAuthWorkaroundFor2805281Trait.php +++ b/core/modules/rest/tests/src/Functional/JsonBasicAuthWorkaroundFor2805281Trait.php @@ -13,7 +13,7 @@ * * @todo Fix in https://www.drupal.org/node/2805281: remove this trait. */ - protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) { + protected function assertResponseWhenMissingAuthentication(ResponseInterface $response, $message = '') { $this->assertSame(401, $response->getStatusCode()); $this->assertSame([static::$expectedErrorMimeType], $response->getHeader('Content-Type')); // Note that strange 'A fatal error occurred: ' prefix, that should not