.../src/Functional/BasicAuthResourceTestTrait.php | 13 +++++++- .../src/Functional/CookieResourceTestTrait.php | 4 ++- .../EntityResource/Block/BlockResourceTestBase.php | 23 ++++++++++---- .../BlockContent/BlockContentResourceTestBase.php | 4 +-- .../Comment/CommentResourceTestBase.php | 9 ------ .../EntityResource/EntityResourceTestBase.php | 37 ++++++++++++++++++++-- .../EntityResource/Media/MediaResourceTestBase.php | 4 +-- .../SearchPage/SearchPageResourceTestBase.php | 4 +-- .../EntityResource/User/UserResourceTestBase.php | 4 +-- 9 files changed, 74 insertions(+), 28 deletions(-) diff --git a/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php b/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php index 08b17e1..29f698c 100644 --- a/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php +++ b/core/modules/rest/tests/src/Functional/BasicAuthResourceTestTrait.php @@ -35,7 +35,18 @@ protected function getAuthenticationRequestOptions($method) { */ protected function assertResponseWhenMissingAuthentication($method, ResponseInterface $response) { $expected_page_cache_header_value = $method === 'GET' ? 'MISS' : FALSE; - $this->assertResourceErrorResponse(401, 'No authentication credentials provided.', $response, ['4xx-response', 'config:system.site', 'config:user.role.anonymous', 'http_response'], ['user.permissions', 'user.roles:anonymous'], $expected_page_cache_header_value, FALSE); + $expected_cacheability = $this->getExpectedUnauthorizedAccessCacheability() + ->addCacheableDependency($this->getExpectedUnauthorizedEntityAccessCacheability(FALSE)) + // @see \Drupal\basic_auth\Authentication\Provider\BasicAuth::challengeException() + ->addCacheableDependency($this->config('system.site')) + // @see \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber::onRespond() + ->addCacheTags(['config:user.role.anonymous']); + // Only add the 'user.roles:anonymous' cache context if its parent cache + // context is not already present. + if (!in_array('user.roles', $expected_cacheability->getCacheContexts(), TRUE)) { + $expected_cacheability->addCacheContexts(['user.roles:anonymous']); + } + $this->assertResourceErrorResponse(401, 'No authentication credentials provided.', $response, $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), $expected_page_cache_header_value, FALSE); } /** diff --git a/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php b/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php index 50a7eb8..2d25b94 100644 --- a/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php +++ b/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php @@ -99,7 +99,9 @@ protected function assertResponseWhenMissingAuthentication($method, ResponseInte // @see \Drupal\user\Authentication\Provider\Cookie // @todo https://www.drupal.org/node/2847623 if ($method === 'GET') { - $expected_cookie_403_cacheability = $this->getExpectedUnauthorizedAccessCacheability(); + $expected_cookie_403_cacheability = $this->getExpectedUnauthorizedAccessCacheability() + // @see \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber::onRespond() + ->addCacheableDependency($this->getExpectedUnauthorizedEntityAccessCacheability(FALSE)); // - \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber applies // to cacheable anonymous responses: it updates their cacheability. // - A 403 response to a GET request is cacheable. diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php index 370e0bc..9d60c0b 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Block; use Drupal\block\Entity\Block; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase; abstract class BlockResourceTestBase extends EntityResourceTestBase { @@ -143,17 +144,25 @@ protected function getExpectedUnauthorizedAccessMessage($method) { /** * {@inheritdoc} + * + * @todo Fix this in https://www.drupal.org/node/2820315. */ protected function getExpectedUnauthorizedAccessCacheability() { + return (new CacheableMetadata()) + ->setCacheTags(['4xx-response', 'http_response']) + ->setCacheContexts(['user.roles']); + } + + /** + * {@inheritdoc} + */ + protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) { // @see \Drupal\block\BlockAccessControlHandler::checkAccess() - return parent::getExpectedUnauthorizedAccessCacheability() - ->setCacheTags([ - '4xx-response', + return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) + ->addCacheTags([ 'config:block.block.llama', - 'http_response', - static::$auth ? 'user:2' : 'user:0', - ]) - ->setCacheContexts(['user.roles']); + $is_authenticated ? 'user:2' : 'user:0', + ]); } } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php index 5766817..f9194c2 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php @@ -176,9 +176,9 @@ protected function getExpectedUnauthorizedAccessMessage($method) { /** * {@inheritdoc} */ - protected function getExpectedUnauthorizedAccessCacheability() { + protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) { // @see \Drupal\block_content\BlockContentAccessControlHandler() - return parent::getExpectedUnauthorizedAccessCacheability() + return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) ->addCacheTags(['block_content:1']); } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php index ac99e86..25ca463 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php @@ -375,13 +375,4 @@ public function testPostSkipCommentApproval() { $this->assertTrue($unserialized->getStatus()); } - /** - * {@inheritdoc} - */ - protected function getExpectedUnauthorizedAccessCacheability() { - // @see \Drupal\comment\CommentAccessControlHandler::checkAccess() - return parent::getExpectedUnauthorizedAccessCacheability() - ->addCacheTags(['comment:1']); - } - } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 62fad0a..647047b 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -366,6 +366,20 @@ protected function getExpectedUnauthorizedAccessCacheability() { } /** + * The cacheability of unauthorized 'view' entity access. + * + * @param bool $is_authenticated + * Whether the current request is authenticated or not. This matters for + * some entity access control handlers, but not for most. + * + * @return \Drupal\Core\Cache\CacheableMetadata + * The expected cacheability. + */ + protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) { + return new CacheableMetadata(); + } + + /** * The expected cache tags for the GET/HEAD response of the test entity. * * @see ::testGet @@ -427,7 +441,17 @@ public function testGet() { // response because ?_format query string is present. $response = $this->request('GET', $url, $request_options); if ($has_canonical_url) { - $this->assertResourceErrorResponse(403, $this->getExpectedUnauthorizedAccessMessage('GET'), $response, ['4xx-response', 'config:user.role.anonymous', 'http_response'], ['user.permissions'], 'MISS', FALSE); + $expected_cacheability = $this->getExpectedUnauthorizedAccessCacheability() + // @see \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber::onRespond() + ->addCacheTags(['config:user.role.anonymous']); + $entity_type = $this->entity->getEntityType(); + // When the canonical URL *is* the same as the edit form's, the canonical + // is just an alias, and hence an 'update' entity access is checked, not + // 'view'. Hence only add entity access cacheability conditionally. + if ($this->entity->hasLinkTemplate('edit-form') && $entity_type->getLinkTemplate('canonical') !== $entity_type->getLinkTemplate('edit-form')) { + $expected_cacheability->addCacheableDependency($this->getExpectedUnauthorizedEntityAccessCacheability(FALSE)); + } + $this->assertResourceErrorResponse(403, $this->getExpectedUnauthorizedAccessMessage('GET'), $response, $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), 'MISS', FALSE); } else { $this->assertResourceErrorResponse(404, 'No route found for "GET ' . str_replace($this->baseUrl, '', $this->getEntityResourceUrl()->setAbsolute()->toString()) . '"', $response); @@ -474,7 +498,8 @@ public function testGet() { // DX: 403 when unauthorized. $response = $this->request('GET', $url, $request_options); - $expected_403_cacheability = $this->getExpectedUnauthorizedAccessCacheability(); + $expected_403_cacheability = $this->getExpectedUnauthorizedAccessCacheability() + ->addCacheableDependency($this->getExpectedUnauthorizedEntityAccessCacheability(static::$auth !== FALSE)); $this->assertResourceErrorResponse(403, $this->getExpectedUnauthorizedAccessMessage('GET'), $response, $expected_403_cacheability->getCacheTags(), $expected_403_cacheability->getCacheContexts(), static::$auth ? FALSE : 'MISS', FALSE); $this->assertArrayNotHasKey('Link', $response->getHeaders()); @@ -653,6 +678,14 @@ public function testGet() { // DX: 403 when unauthorized. $response = $this->request('GET', $url, $request_options); + $expected_403_cacheability = $this->getExpectedUnauthorizedAccessCacheability(); + // Permission checking now happens first, so it's the only cache context we + // could possibly vary by. + $expected_403_cacheability->setCacheContexts(['user.permissions']); + // @see \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber::onRespond() + if (static::$auth === FALSE) { + $expected_403_cacheability->addCacheTags(['config:user.role.anonymous']); + } $this->assertResourceErrorResponse(403, $this->getExpectedUnauthorizedAccessMessage('GET'), $response, $expected_403_cacheability->getCacheTags(), $expected_403_cacheability->getCacheContexts(), static::$auth ? FALSE : 'MISS', FALSE); $this->grantPermissionsToTestedRole(['restful get entity:' . static::$entityTypeId]); diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php index 416a7a6..000014a 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php @@ -266,9 +266,9 @@ public function testPost() { /** * {@inheritdoc} */ - protected function getExpectedUnauthorizedAccessCacheability() { + protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) { // @see \Drupal\media\MediaAccessControlHandler::checkAccess() - return parent::getExpectedUnauthorizedAccessCacheability() + return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) ->addCacheTags(['media:1']); } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php index eca9ca8..9912ca6 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php @@ -102,9 +102,9 @@ protected function getExpectedUnauthorizedAccessMessage($method) { /** * {@inheritdoc} */ - protected function getExpectedUnauthorizedAccessCacheability() { + protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) { // @see \Drupal\search\SearchPageAccessControlHandler::checkAccess() - return parent::getExpectedUnauthorizedAccessCacheability() + return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) ->addCacheTags(['config:search.page.hinode_search']); } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php index 3d8a1c9..3567160 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php @@ -320,9 +320,9 @@ protected function getExpectedUnauthorizedAccessMessage($method) { /** * {@inheritdoc} */ - protected function getExpectedUnauthorizedAccessCacheability() { + protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) { // @see \Drupal\user\UserAccessControlHandler::checkAccess() - return parent::getExpectedUnauthorizedAccessCacheability() + return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) ->addCacheTags(['user:3']); }