Problem/Motivation

I have the following media bundles and they are checked in that order...:
- Custom 1 (forbidden)
- Custom 2 (forbidden)
- Custom 3 (forbidden)
- Image (Allowed)
- Video (Allowed)
- File (Allowed)

The generic button Add media should show up when the a user has access to create at least one of the bundles in the media. Because of how my bundles are ordered, as well as how the code in Drupal\Core\Entity\EntityCreateAnyAccessCheck::access() is implemented, the result in the end is forbidden and the button Add media does not show when it should.

Steps to reproduce

  1. Create a 2 bundles of a specific entity type (first_bundle & last_bundle)
  2. Implement hook_ENTITY_TYPE_create_access() for the specific entity type and return AccessResult::forbidden() for last_bundle
  3. The /entity_type/add page is loaded and only allows to create "first_bundle" items
  4. Modify the hook above to return AccessResult::forbidden() for first_bundle
  5. The add page shows Access Denied, even if it is possible to create last_bundle entities and is possible to create them if you access directly to /entity_type/add/last_bundle

Proposed resolution

Tweaks the implementation to implement what is mentioned in the comments in the following piece of code:

// Check whether an entity of any bundle may be created.
foreach ($bundles as $bundle) {
  $access = $access->orIf($access_control_handler->createAccess($bundle, $account, [], TRUE));
  // In case there is a least one bundle user can create entities for,
  // access is allowed.
  if ($access->isAllowed()) {
    break;
  }
}

The first iteration get's a forbidden passed to the OR in here is causing the overall result to be Forbidden.

What I think is needed here is to aggregate the whole set of access permissions including their cache metadata. Breaking early might cause caheability metadata leaks, as the rest of the allowed access checks are fully ignored.

We can have the break changed, so it will build the whole set of access results and aggregate them (in terms of cache metadata).
Then have an Allow / Forbidden / Neutral based on tuning on the method...

Remaining tasks

Discussion, Patch...

User interface changes

None expected.

API changes

Small internal change to have correct access checking on entity level.

Data model changes

None.

Release notes snippet

TBD.
We will release a bit more actions on the system, as the bug is restricting the operations.
Possibly some more permissions will be alowed.

Comments

ndobromirov created an issue. See original summary.

ndobromirov’s picture

Status: Active » Needs review
Issue tags: +Needs tests
StatusFileSize
new1.48 KB

This implements the proposal form the description.
No tests are updated, but this is a good candidate for having test coverage added.

Needs review to trigger the CI builds on the patch...

ndobromirov’s picture

Issue summary: View changes

Updated IS to reflect the problem better.

ndobromirov’s picture

I have trouble finding any test code for this file in Core. Even though it's used on I think all entity types :(.

ndobromirov’s picture

Title: Can not create media » EntityCreateAnyAccessCheck::access() too restrictive
valthebald’s picture

From the issue description, it looks like it should happen for all (content) entities, not only media. Have you checked, just in case?

ndobromirov’s picture

No time to check my issue is only with media, but yes, it will likely fail on other entities as well.
I do not see how other types will differ if they are using this code.

You are free to update the IS if there is a reproducible case in other entity types.

berdir’s picture

I think I saw another issue before on the forbidden problem.

However, I don't think we also need to change the return early, that is pretty important on sites with a lot of bundles (unless you're the unlucky guy to only have access to the last of many bundles). We might not have all the cacheablity metadata, but we have enough to know that we can allow access and cacheability metdata for render/dynamic page cache is self-learning over time.

We did add quite a bit of test coverage for that access check, so shouldn't be too hard to extend it to not just have neutral but also explicitly forbidden access.

hchonov’s picture

StatusFileSize
new1020 bytes

However, I don't think we also need to change the return early, that is pretty important on sites with a lot of bundles (unless you're the unlucky guy to only have access to the last of many bundles). We might not have all the cacheablity metadata, but we have enough to know that we can allow access and cacheability metdata for render/dynamic page cache is self-learning over time.

@Berdir, I agree with this and here is a patch for that. We still need a test.

ndobromirov’s picture

Latest patch in #9 is better. Hiding mine from #2.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

upchuk’s picture

StatusFileSize
new2.52 KB
new3.58 KB

I added test coverage for this and improved a bit on #10. Not sure why you ran still an orIf between the existing access and the bundle access. We just need to return allowed or neutral and inherit the cacheability from each iteration on whatever we return (essentially collect cache metadata).

The last submitted patch, 16: 3048760-16-test-only.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 16: 3048760-16.patch, failed testing. View results

upchuk’s picture

Status: Needs work » Needs review
StatusFileSize
new4.05 KB

Forgot about the access reason it seems.

seanb’s picture

Issue tags: -Needs tests
StatusFileSize
new2.53 KB

Patch seems to do the trick for me. Attached is a test-only patch from #19 that should prove the bug exists and that it is indeed fixed by the patch in #19.

seanb’s picture

Just noticed #16 already contained the same test-only patch. Sorry about that. In that case I think this might be ready. Not sure I'm the best person to RTBC this, so I'll leave the status for now.

Also #3039629: Bug:"_entity_create_any_access" error using orIf method is probably a duplicate of this issue.

Status: Needs review » Needs work

The last submitted patch, 20: 3048760-19-test-only.patch, failed testing. View results

seanb’s picture

Status: Needs work » Needs review

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

rp7’s picture

I can confirm that the patch in #19 fixes this issue. Thank you!

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs steps to reproduce, +Needs Review Queue Initiative

This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.

I tried replicating this in D10.1.x by adding a custom media type
Giving the content editor role permission to create just image media types.
The admin media button appears fine.

Could use additional steps to show this issue.

plopesc’s picture

Issue summary: View changes

@smustgrave

I have been able to reproduce the bug and can confirm that patch in #19 works

Steps to reproduce:

  1. Create a 2 bundles of a specific entity type (first_bundle & last_bundle)
  2. Implement hook_ENTITY_TYPE_create_access() for the specific entity type and return AccessResult::forbidden() for last_bundle
  3. The /entity_type/add page is loaded and only allows to create "first_bundle" items
  4. Modify the hook above to return AccessResult::forbidden() for first_bundle
  5. The add page shows Access Denied, even if it is possible to create last_bundle entities and is possible to create them if you access directly to /entity_type/add/last_bundle

The bundle name is an important point in this issue, because bundles are loaded alphabetically, and the bug only happens if the forbidden bundle comes first.

bkosborne’s picture

Sigh, ran into this myself after lots of time debugging. Glad there's some movement on this and others find it buggy. I think this affects anyone that is using hook_create_access to forbid access to specific bundles of an entity type.

plopesc’s picture

Status: Needs work » Needs review
Issue tags: -Needs steps to reproduce
bkosborne’s picture

Status: Needs review » Reviewed & tested by the community

This works well for me

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

  • catch committed 1991a7b6 on 10.0.x
    Issue #3048760 by Upchuk, ndobromirov, seanB, hchonov, bkosborne, Berdir...

  • catch committed 60718c0f on 10.1.x
    Issue #3048760 by Upchuk, ndobromirov, seanB, hchonov, bkosborne, Berdir...

  • catch committed ee7aaf22 on 11.x
    Issue #3048760 by Upchuk, ndobromirov, seanB, hchonov, bkosborne, Berdir...

  • catch committed 2ab22294 on 9.5.x
    Issue #3048760 by Upchuk, ndobromirov, seanB, hchonov, bkosborne, Berdir...
catch’s picture

Title: EntityCreateAnyAccessCheck::access() too restrictive » EntityCreateAnyAccessCheck::access() wrongly forbids access when the first bundle forbids access
Version: 11.x-dev » 9.5.x-dev
Status: Reviewed & tested by the community » Fixed

Attempting a more descriptive title.

Committed/pushed to 11.x and cherry-picked back through to 9.5.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.