Problem/Motivation
The implementation of decoration of access_check.latest_revision service has a small mistake that causes the Access Unpublished check src/Access/LatestRevisionCheck::access() to be called twice in row.
- Once with all other services decorating the
access_check.latest_revision. It might be called together with:- Service
gcontent_moderation.latest_revisionfrom https://www.drupal.org/project/gcontent_moderation contrib with its Access Check ingcontent_moderation/src/Access/LatestRevisionCheck::access() - ...
- Service
- Second separately as
access_unpublished.access_check.latest_revisionservice alone.
As a result, you might experience the following Access Denied Warning:
Path: /node/1/latest. Drupal\Core\Http\Exception\CacheableAccessDeniedHttpException: The following permissions are required: 'view latest version' AND 'view any unpublished content'. in Drupal\Core\Routing\AccessAwareRouter->checkAccess() (line 115 of /app/web/core/lib/Drupal/Core/Routing/AccessAwareRouter.php).
The definition of the Service access_unpublished.access_check.latest_revision adds the following Tag to the definition:
->addTag('access_check', ['applies_to' => ‘_content_moderation_latest_version']);
which is WRONG as it already decorated the Service access_check.latest_revision and thus it does NOT need to be called as a separate “Content moderation latest version Access Check” AGAIN.
The Tag is added in src/AccessUnpublishedServiceProvider.php:
$container->register('access_unpublished.access_check.latest_revision', LatestRevisionCheck::class)
->setDecoratedService('access_check.latest_revision')
->addArgument(new Reference('access_unpublished.access_check.latest_revision.inner'))
->addTag('access_check', ['applies_to' => '_content_moderation_latest_version']);
The Service access_check.latest_revision defined in the core/modules/content_moderation/content_moderation.services.yml contains tag definition with values: { name: access_check, applies_to: _content_moderation_latest_version } so when decorating there's no reason to define the same values for the service that decorates it.
access_check.latest_revision:
class: Drupal\content_moderation\Access\LatestRevisionCheck
arguments: ['@content_moderation.moderation_information']
tags:
- { name: access_check, applies_to: _content_moderation_latest_version }
Steps to reproduce
The check is being called twice all the time.
- If you debug
core/lib/Drupal/Core/Access/AccessManager::checkRequest()which callscore/lib/Drupal/Core/Access/AccessManager::check() - And you review the result of the line
$checks = $route->getOption('_access_checks') ?: []; - You should see both checks in:
access_check.latest_revisionand
access_unpublished.access_check.latest_revision
Or more complicated situation: Use the following contribs together
- https://www.drupal.org/project/group
- https://www.drupal.org/project/gcontent_moderation
- https://www.drupal.org/project/access_unpublished
- access_unpublished_group submodule from https://www.drupal.org/project/access_unpublished added in patch from https://www.drupal.org/project/access_unpublished/issues/3405874#comment...
- Get some content node to Draft state and visit the
/node/[nid]/latest - The Group access checks will be shadowed by Access Unpublished check.
Proposed resolution
Remove the service Tag mentioned above.
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | 3421278-4-removed-service-tag.patch | 1.21 KB | dabbor |
Issue fork access_unpublished-3421278
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
dabbor commentedComment #4
dabbor commentedAdding in a simple file patch for https://git.drupalcode.org/project/access_unpublished/-/merge_requests/1...
To make referencing in composer safer.
I tested the patch to make sure the Access check in question is not running twice in row + The Access Unpublished Tokens are working OK.
Comment #5
dabbor commentedComment #6
generalredneckAfter reading through your explanation in the issue, it seems to make sense to me. and I can follow your steps to reproduce. I'm not sure there is an easy way to write a test for this.
Comment #7
generalredneckThis will be in 1.x-dev release.