Problem/Motivation

We currently allow entities to specify an "admin permission". Entity types such as Node and others provide a separate "overview" permission that is useful for granting people access the entity view, even if they do not have the admin permission. For example you may want to allow some people to edit entities, but not delete them or people may have access to edit and delete some entities but not others. In such cases it's necessary to access the entity overview but not have the admin permission.

Proposed resolution

- Allow entities to provide a "collection permission" in addition to the admin permission. ("collection" is chosen as a term instead of "overview" to comply with the collection link template and route.) Just like the admin permission the actual name of the permission (i.e. "access $foo overview") is determined by an annotation key.
- Update the generated collection route to allow access to either the admin permission or the collection permission.

Issue fork drupal-2953566

Command icon 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

tstoeckler created an issue. See original summary.

mglaman’s picture

The only problem I see is that core has no method for generating permissions. Although I guess that is the case for "admin permission". It's used and not generated unless the developer explicitly provides .permissions.yml containing it.

Would choosing "collection" prevent us from landing in the 8.x cycle? Just because the community and core-ish pattern is to use overview.

access content overview:
  title: 'Access the Content overview page'
view own unpublished content:

and

access taxonomy overview:
  title: 'Access the taxonomy vocabulary overview page'
  description: 'Get an overview of all taxonomy vocabularies.'

While I agree collection is better. Maybe we should first get core to support "overview" permission for collection routes, and then follow up to fix naming pattern for D9?

berdir’s picture

The idea would be to make it an entity type annotation, just like admin_permission. Then the permission name can be anything, including the existing access XY overview permissions.

+1 on the idea.

mglaman’s picture

+1 on #4, then!

tstoeckler’s picture

Yes, I was thinking in the same direction as #4. Added that to the issue summary now. Also good point about the permissions, linking the relevant issue here, as well.

vijaycs85’s picture

Status: Active » Needs review
StatusFileSize
new4.37 KB

Initial patch...

Status: Needs review » Needs work

The last submitted patch, 7: 2953566-7.patch, failed testing. View results

vijaycs85’s picture

Status: Needs work » Needs review
StatusFileSize
new1.11 KB
new5.12 KB

- Probably we can avoid those getAdminPermission and getCollectionPermission calls, but going to leave it as it's for now.
- Might worth adding few more dataset for the new condition.

tstoeckler’s picture

Status: Needs review » Needs work

Wow, I am barely awake and you already made my day :-) Awesome patch, thank you a lot.

I think we should add some more test coverage (see below), so marking needs work for that, but I really couldn't find much to complain about ;-)

  1. +++ b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
    @@ -318,7 +318,11 @@ protected function getDeleteFormRoute(EntityTypeInterface $entity_type) {
    +    $permissions = array_filter([
    +      $entity_type->getAdminPermission(),
    +      $entity_type->getCollectionPermission(),
    +    ]);
    

    Neat trick, I like this!

  2. +++ b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
    @@ -318,7 +318,11 @@ protected function getDeleteFormRoute(EntityTypeInterface $entity_type) {
    +    if ($entity_type->hasLinkTemplate('collection') && $entity_type->hasListBuilderClass() && !empty($permissions)) {
    

    Minor, but you don't need the !empty() around $permissions

  3. +++ b/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php
    @@ -280,10 +284,12 @@ public function providerTestGetCollectionRoute() {
    -    $entity_type4->getAdminPermission()->willReturn('administer the entity type');
    +    $entity_type4->getAdminPermission()->willReturn(FALSE);
    +    $entity_type4->getCollectionPermission()->willReturn('administer the entity type');
    

    The test cases above already test the case when there is neither an admin permission nor a collection permission, so I think we should add test cases for the following:
    1. There is an admin permission but no collection permission
    2. There is a collection permission, but no admin permission
    3. There are both

    The patch in its current form only does 2.

mohit1604’s picture

Status: Needs work » Needs review
StatusFileSize
new764 bytes
new5.11 KB

Thanks for the patch. Fixed #10.2 , #10.3 still to be done.

vijaycs85’s picture

StatusFileSize
new7.95 KB
new4.03 KB

Wow, I am barely awake and you already made my day :-)

awww... surely this one did mine :)

All scenarios in 10.3 addressed in this patch.

hchonov’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
    @@ -385,6 +385,17 @@ public function setHandlerClass($handler_type, $value);
    +   * The default \Drupal\Core\Entity\EntityAccessControlHandler class checks
    +   * this permission for collection route.
    

    I would exchange this sentence simply with @see \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider::getCollectionRoute().

  2. +++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
    @@ -385,6 +385,17 @@ public function setHandlerClass($handler_type, $value);
    +   *   String collection permission name, FALSE otherwise.
    

    This is a bit confusing. What about - "The collection permission name, or FALSE if none."?

vijaycs85’s picture

StatusFileSize
new815 bytes
new7.91 KB

Thanks @hchonov. Addressed both doc review comments.

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Yay, that's exactly what I was going for. Let's do it!

tstoeckler’s picture

Ahh, that was a crosspost and Drupal.org is weird. #14 is good to go, though

berdir’s picture

Looks good to me as well, should we already set this here on the entity types that have a collection permission? Neither node nor taxonomy_term do currently use the default html route provider but this should simplify the transition?

Media does and does have an access overview permission. File also has an overview permission, but doesn't provide a non-views fallback lost builder/route at all currently.

amateescu’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -614,6 +621,14 @@ public function getAdminPermission() {
+    return $this->collection_permission ?: FALSE;

+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -385,6 +385,16 @@ public function setHandlerClass($handler_type, $value);
+   * @return string|bool
+   *   The collection permission name, or FALSE if none.

I think we were trying to move away from this pattern of returning FALSE instead of the proper return type (NULL in this case).

vijaycs85’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new3.38 KB
new7.9 KB

Thanks @amateescu. I was thinking about on the initial patch and thought keeping it aligned with admin permission :)

hchonov’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -614,6 +621,14 @@ public function getAdminPermission() {
+    return $this->collection_permission ?: NULL;

Now when we return NULL if the property isn't set, then we can simply return it without any checks.

vijaycs85’s picture

StatusFileSize
new474 bytes
new7.89 KB

Sure!

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Fine with me, still looks good. Thanks!

tstoeckler’s picture

Re #17: I think I would prefer that being a follow-up issue, at least for media, because there it would actually be a behavior change. I think it will be a very-much welcomed change, but still. For node and user I don't feel strongly about adding it here or not, I wouldn't mind it, but would also not hold it up on that personally.

tstoeckler’s picture

Status: Reviewed & tested by the community » Needs work

OK, with #2702683: Add plural labels to entity types we have a precedent of these kinds of things being part of the annotation without actually being used (yet) in the route provider. So I think that's an argument for adding the overview permissions for nodes (access content overview) and terms (access taxonomy overview). In looking into this, I found that VocabularyRouteProvider::getCollectionRoute() actually implements this precisely. So I think we should add a collection permission to vocabularies as well and then remove that override. The behavior is then identical to HEAD.

I still feel strongly, though, that we should not add a collection permission to media, because that is actually a behavior change. And while it is a bug fix more than anything else, we will want to add test coverage for that, and I don't think that would be in scope here.

Marking needs work, though for the node, term and vocabulary changes.

vijaycs85’s picture

Status: Needs work » Needs review
StatusFileSize
new2.33 KB
new10.23 KB

OK, with #2702683: Add plural labels to entity types we have a precedent of these kinds of things being part of the annotation without actually being used (yet) in the route provider

+1

So I think that's an argument for adding the overview permissions for nodes (access content overview) and terms (access taxonomy overview).

Right, so we don't have collection link on node and term, added but not used. behaviour still the same.

I found that VocabularyRouteProvider::getCollectionRoute() actually implements this precisely. So I think we should add a collection permission to vocabularies as well and then remove that override. The behavior is then identical to HEAD.

Done, we have both admin and collection permission on override and removing it would have same permissions from generic parent.

I still feel strongly, though, that we should not add a collection permission to media, because that is actually a behavior change. And while it is a bug fix more than anything else, we will want to add test coverage for that, and I don't think that would be in scope here.

Probably a follow up?

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Opened #2954866: Add a collection permission to media entities for the follow-up. Thanks for the patch. Looks perfect now. Really nice that we were already able to remove some custom code in favor of this generic solution, even though we still have three more cases in core alone which will be able to utilize this.

gabesullice’s picture

I need to take a deeper look at this, but at first glance this seems like it will break JSON API module or at least require it to add new access checks. I.e. as soon as this is added, then theres a potential for an access bypass via JSON API collection routes.

This addition seems like it's trying to put things that should be route specific on the entity type definition.

Given that it's already only ever used in conjunction with an EntityListBuilder, perhaps it would be more appropriate to have a static method on that class.

gabesullice’s picture

Status: Reviewed & tested by the community » Needs review

Thinking a bit more...

What's conceptually different between "admin_permission" and "collection_permission" is that "admin_permission" can be used as a shorthand for "allow all operations on any entity of this type", but "collection_permission" has no such feature. It's simply an indirect way of adding a different _permission check to a automatically generated route.

However, given that it's on the entity type definition, I imagine it will be confused/interpreted to mean that there's a difference between 'view' and 'view in the context of many'.

IOW, if this is added, should JSON API be required to add a new matching route permission for our entity listings? If so, perhaps this breaks BC.

If not, then perhaps all this confusion could be added by reducing the scope of the addition by renaming it to list_builder_permission.

mglaman’s picture

That's part of my concern in #3. In the Entity API module we have a permission provider and access handler which matches it. Drupal core does not. So it feels slightly awkward.

list_builder_permission would mitigate the semantics issue around the route being "collection" and permissions being named "overview". The use case we're working against is the list builder, not an API resource with a collection of entities.

berdir’s picture

> This addition seems like it's trying to put things that should be route specific on the entity type definition.

Isn't the whole point of the route provider to generate routes based on the entity type definition? We're just adding one more option/feature to it.

> IOW, if this is added, should JSON API be required to add a new matching route permission for our entity listings? If so, perhaps this breaks BC.

The same argument could be made about any key that we added to entity type definitions, that adding it requires modules to use and consider on it. We could never add anything with that argument. I don't see how this is differnt to adding new plural labels, metadata revision keys or additional handlers.

I don't think this opens up a security issue. If it does *not* respect that setting then it simply doesn't allow access and will only allow with the admin permission for example. That's a bug at best but not a security issue.

We should have a change record for it, so that modules know they can use this now, but then it should be fine IMHO.

gabesullice’s picture

If it does *not* respect that setting then it simply doesn't allow access and will only allow with the admin permission for example. That's a bug at best but not a security issue.

Currently there is no such permission and we've allowed access by default to the JSON API collection route. We rely on $entity->access('view') to filter out entities to which a user does not have access. This is why i said there's a potential access bypass if it's assumed that "collection_permission" applies to "viewing a collection of entities" when really it means "access the entity listing page.”

If it's the former meaning, then anything that lists entities will need to take this into account because they won't currently be checking access to listings at all.

A name change to list_builder_permission seems like a simple way to skirt this confusion. It's more accurate for its intended purpose anyway.

tstoeckler’s picture

Re #27 - #29:

Sorry but I have to strongly disagree with your assessments. Not going straight back to RTBC because I know how it feels to be on the other side of the status ping-pong game but having to conjure some serious restraint to not do that, to be honest.

OK, let's go through this point by point:

this seems like it will break JSON API module or at least require it to add new access checks. I.e. as soon as this is added, then theres a potential for an access bypass via JSON API collection routes.

Can you elaborate on that? With just core this is 100% backwards-compatible and I'm having trouble coming up with any way this could break contrib, so let's just table this point until there is more information on what actually breaks.

This addition seems like it's trying to put things that should be route specific on the entity type definition.

Aren't the paths defined in the links part of the annotation route-specific, as well? There are many more examples of this. The reason this is currently "route-specific" is because we have no generic way of checking access for a list or "collection" of entities. If we had that, this permission would maybe also be used in the access control handler.

Given that it's already only ever used in conjunction with an EntityListBuilder, perhaps it would be more appropriate to have a static method on that class.

I think that would be a big architectural mistake, while theoretically possible. List builders are very similar to normal (route) controllers, but it's an important aspect of the Symfony Routing system to not couple the controller itself with the access checks of the route. Doing that here would feel very much out of place.

What's conceptually different between "admin_permission" and "collection_permission" is that "admin_permission" can be used as a shorthand for "allow all operations on any entity of this type", but "collection_permission" has no such feature. It's simply an indirect way of adding a different _permission check to a automatically generated route.

I disagree. The collection permission is a shorthand for "access a list of entities of this type". If we didn't have to consider BC we could consider adding a check for that permission to every entity query, so that accounts without that permission simply get an empty list back. I don't think that's on the table, but I don't think that makes the collection permission in some more "custom" or "specific" than any other.

However, given that it's on the entity type definition, I imagine it will be confused/interpreted to mean that there's a difference between 'view' and 'view in the context of many'.

Well there actually can be a difference between "view" and "view in the context of many". We already have the "view label" operation to mitigate that for certain contexts. Furthermore the concept of a "view" permission is quite ambiguous in its own right. I.e. does "view"ing a block config entity mean viewing the rendered output of the block or viewing the block's configuration via Rest? So this doesn't make anything confusing that's not already confusing in the first place.

IOW, if this is added, should JSON API be required to add a new matching route permission for our entity listings? If so, perhaps this breaks BC.

Again, I can't say anything about JSON API, but please be more specific before making any claims that this is BC-breaking.

If not, then perhaps all this confusion could be added by reducing the scope of the addition by renaming it to list_builder_permission.

Again, I have to disagree. Collection is the correct name for this, not in some theoretical software architecture sense, but given the APIs we already have. We currently generate a collection route, and that is determined by a collection link template, the list builder, and the collection label. Now that we are adding a permission to the mix, there really is only one sensible choice on what to call the permission.

That's part of my concern in #3. In the Entity API module we have a permission provider and access handler which matches it. Drupal core does not. So it feels slightly awkward.

I already linked the issue to add the permission providers to core above. I don't see how it is awkward, in particular I don't see how it is any more awkward than the admin permission.

list_builder_permission would mitigate the semantics issue around the route being "collection" and permissions being named "overview". The use case we're working against is the list builder, not an API resource with a collection of entities.

If we were building an API from scratch, I might agree with you, but given the API we have, this statement is simply false. The collection link template long preceded even the notion of GraphQL or JSON API or anything like it. It has denoted the path to the HTML list of entities since day 1. Maybe that was problematic in terms of naming, I'm not sure. But that's what it is and questioning that naming scheme is simply not on the table (at least not in the scope of this issue).

berdir’s picture

@tstoeckler: Wow. That might be the first time ever that someone agreed with me in what is basically a crosspost *and* used more words to explain that than I did. respect ;)

> Currently there is no such permission and we've allowed access by default to the JSON API collection route.

If there is a security issue then that's it. Just because there is no standard way to describe access to the collection doesn't mean that it is available for anyone.

If you do check per-entity view access then it's probably not a big deal either way (with that collection thing existing or not), although I wonder how you deal with paging then.

tstoeckler’s picture

Oh wow, yeah massive crosspost. Still stands, though.

gabesullice’s picture

Sorry but I have to strongly disagree with your assessments. Not going straight back to RTBC because I know how it feels to be on the other side of the status ping-pong game but having to conjure some serious restraint to not do that, to be honest.

I really am sorry to swoop in and "un-RTBC" this. I too know that it can be really frustrating. But RTBC stands for "Reviewed and Tested by the Community". Even if JSON API is in contrib (at the moment), we're part of the community and this will introduce a question for us that hasn't been sufficiently addressed. I think that merits more review. I really didn't mean to be rude by doing it.

Can you elaborate on that? With just core this is 100% backwards-compatible and I'm having trouble coming up with any way this could break contrib, so let's just table this point until there is more information on what actually breaks.

I think you probably were writing this as I posted #31. Do you need more clarification since then? I would be happy to provide it :)

I disagree. The collection permission is a shorthand for "access a list of entities of this type".

So this is actually what I was worried it would be simply be confused to be.

So this doesn't make anything confusing that's not already confusing in the first place.

Unfortunately it does, or I wouldn't be confused.

Collection is the correct name for this, not in some theoretical software architecture sense, but given the APIs we already have. We currently generate a collection route, and that is determined by a collection link template, the list builder, and the collection label. ... The collection link template long preceded even the notion of GraphQL or JSON API or anything like it. It has denoted the path to the HTML list of entities since day 1.

This seems to contradict: "The collection permission is a shorthand for "access a list of entities of this type". Is this about adding a permission to control 'view many' or is it about adding a permission to the collection route (the path to the HTML list of entities)?

wim leers’s picture

First off: what an excellent proposal and an even more impressively thoughtful discussion! Go Drupal community!


@tstoeckler: Wow. That might be the first time ever that someone agreed with me in what is basically a crosspost *and* used more words to explain that than I did. respect ;)

😄😄😄😄 LOVE THIS! Go Berdir & tstoeckler!


It's not clear to me what access a list of entities of this type means exactly. Does it mean you can view:

  1. their IDs or UUIDs, i.e. the existence of a number of entities of a certain type?
  2. their labels?
  3. the entire entities (yet of course still respecting field access), i.e. if you have this permission, does that then override per-entity view access?

And how does it relate to the node grants API? And #777578: Add an entity query access API and deprecate hook_query_ENTITY_TYPE_access_alter()?

There is one statement that I have a hard time with:

The collection link template long preceded even the notion of GraphQL or JSON API or anything like it. It has denoted the path to the HTML list of entities since day 1. Maybe that was problematic in terms of naming, I'm not sure.

I understand where you're coming from and sympathize with it. But if we really want to make Drupal API-First, then this line of reasoning is dangerous. We cannot add permissions and assume it only applies to HTML responses/routes/controllers. If we do that, that would then mean HTTP API-providing modules (such as REST, JSON API and GraphQL) all need to invent their own permissions … That is not tenable. This is why all HTTP API-providing modules actually do rely on existing entity and field access: to be able to offer consistent, understandable, predictable behavior across HTML responses, REST responses, JSON API responses and GraphQL responses.

Tagging API-First Initiative because it is very important that we discuss and agree on how this should affect HTTP API behavior. Otherwise we take a step away from being API-First.

That's all I ask: that we carefully consider how it affects or should affect HTTP API behavior. It's not clear to me at the moment. I think it's important that we have clarity on this, if not, then this would be a step back. It seems that @tstoeckler has a clear idea in his head of how this would/should affect HTTP APIs? Could you explain, Tobias? I know you were enthusiastic about \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase, so I know you care about HTTP APIs. How would you see this affect that?

This should be documented in entity.api.php, just like admin_permission is already documented there.


So, to attempt to answer my questions, I reviewed the patch.

  1. The two pre-existing permissions that are being "elevated" to become collection_permissions are access content overview (for Node entities) and access taxonomy overview (for Term and Vocabulary entities).
    These permissions are already being used for the Node, Term and Vocabulary listings.
    Let's look at the prime example: the views.view.content View (which is accessible at /admin/content). I have two initial questions that should get us all to a better joint understanding:
    1. This patch currently only affects the page (HTML) display of the view. How should this affect the rest_exportdisplay?
    2. The (Views-powered) listings can be customized by site builders, thus exposing additional fields. What if this is customized to show a different set of fields? (Some of which may contain sensitive data.)
  2. \Drupal\taxonomy\VocabularyListBuilder::getDefaultOperations() has
        if ($entity->access('access taxonomy overview')) {
          $operations['list'] = [
            'title' => t('List terms'),
            'weight' => 0,
            'url' => $entity->toUrl('overview-form'),
          ];
        }
    

    This check AFAICT become obsolete.

tstoeckler’s picture

X-post this time with @Wim Leers. Response coming up.

I really didn't mean to be rude by doing it.

You certainly weren't rude, sorry in case I implied that. My frustration stems from the fact that I'd like to see this go in as soon as possible, as I see some push being done for #2809177: Introduce entity permission providers currently and I'd like for this one to go in first. But that's certainly not your fault. And it seems we're getting somewhere at least (if not consensus) with the discussion, so all is good.

I think you probably were writing this as I posted #31. Do you need more clarification since then? I would be happy to provide it :)

Like @Berdir said, I don't think there's anything for you to do in JSON API. Certainly this cannot be considered a BC break in any sensible meaning of the word. You can decide to respect the collection permission, or you can choose to (continue to) ignore it, but that's up to you. The current behavior is not changed by this patch.

This seems to contradict: "The collection permission is a shorthand for "access a list of entities of this type". Is this about adding a permission to control 'view many' or is it about adding a permission to the collection route (the path to the HTML list of entities)?

So - just like the 'view' operation is ambiguous and not clearly defined - such a 'view many' operation is also very ambiguous. My point above was simply that there is nothing inherent in the collection permission that prevents us from considering further uses in the future. I.e. if we expand the list builder concept in the future so that there are multiple lists per entity type and not one "global" collection, maybe that permission could be used for all lists? We are also working to generalize the node access grants/records concept for all entity types, so maybe that will be a chance to further generalize this concept and re-use the permission (probably in an "opt-in" fashion, but still).

In Decoupled/Rest land I guess the concept of "collection" is maybe defined in a narrower scope, but unfortunately since we have decided to use the term "collection" not (only) for that realm but also for HTML lists things are simply not very clear cut. Just like the admin permission was originally introduced just as a "shortcut" to grant access to all operations and then later re-used for the collection route we are now introducing a general concept with a very clear and closely defined first purpose.

tstoeckler’s picture

But if we really want to make Drupal "API-First", then this line of reasoning is dangerous. We cannot add permissions and assume it only applies to HTML responses/routes/controllers. If we do that, that would then mean HTTP API-providing modules (such as REST, JSON API and GraphQL) all need to invent their own permissions … That is not tenable.

As I've tried to make very clear above there is nothing that implies this. The whole point of elevating this to the entity type annotation is to make it a generic concept. That way the API First Initiative can rely on it in some way if it chooses to do so. Sorry, but I am really quite confused with this statement. Why would anyone need to invent their own permissions?

That's all I ask: that we carefully consider how it affects or should affect HTTP API behavior. It's not clear to me at the moment. I think it's important that we have clarity on this, if not, then this would be a step back. It seems that @tstoeckler has a clear idea in his head of how this would/should affect HTTP APIs? Could you explain, Tobias?

No, I actually don't have anything tought out in my head. But I still fail to see why not having a clear vision is a problem. This patch introduces a generic concept with a first usage. We can then see how to build on that in core and in API-First-land. As stated multiple times now this doesn't change any existing behavior and also doesn't lock in any other behavior future - except that specifically of the generated collection route.

At first glance it seems quite sensible for JSON API to respect this permission and only show collections to users who have this permission. But that is entirely up to the maintainers of that module who have to consider for example whether that would constitute a BC break for them but also whether they feel this concept matches their concept of a collection. It seems to me that there is a pretty close match, but I have absolutely no Decoupled experience, so I'm the wrong person to ask that.

And - again - the fact that I say it's up to those maintainers to decide that is not just me throwing the ball in someone else's court because I don't want to make a decision. It's completely unrealistic to ask for clear-cut definition of such a concept as the concept of "entity list"/"collection"/"overview" already has more than 5 different meanings in core alone and JSON API and GraphQL bring at least 1 more to the party. I think we should strive to make this permission as meaningful as it can be given this foggy situation both in core and in contrib - but the fact that we have a foggy situation in the first place should not prevent us from at least adding some lighthouses in the fog for people to find some orientation.

tstoeckler’s picture

This patch currently only affects the page (HTML) display of the view. How should this affect the rest_exportdisplay?

How so? This patch should not affect Views in any way.

The (Views-powered) listings can be customized by site builders, thus exposing additional fields. What if this is customized to show a different set of fields? (Some of which may contain sensitive data.)

Similarly I don't understand how that is in any way relevant to this patch.

This check AFAICT become obsolete.

Even though this is slightly confusing (because the naming collides), the check is actually checking an entity operation, not a permission, so no, this is not affected.

hchonov’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record

Re #35:

I really am sorry to swoop in and "un-RTBC" this. I too know that it can be really frustrating. But RTBC stands for "Reviewed and Tested by the Community". Even if JSON API is in contrib (at the moment), we're part of the community and this will introduce a question for us that hasn't been sufficiently addressed. I think that merits more review. I really didn't mean to be rude by doing it.

I think that the answers from @Berdir and @tstoeckler are sufficient and I agree with them both, that there are no BC breaks and JSON API should be fine further doing the checks on the entity level.

"Needs change record" per #30:

We should have a change record for it, so that modules know they can use this now

Re #36:
Setting to needs work for

This should be documented in entity.api.php, just like admin_permission is already documented there.

and

\Drupal\taxonomy\VocabularyListBuilder::getDefaultOperations() has

if ($entity->access('access taxonomy overview')) {
      $operations['list'] = [
        'title' => t('List terms'),
        'weight' => 0,
        'url' => $entity->toUrl('overview-form'),
      ];
    }

This check AFAICT become obsolete.

hchonov’s picture

Re #39:
Well that operation checks against the same permission in \Drupal\taxonomy\VocabularyAccessControlHandler::checkAccess():

protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
    switch ($operation) {
      case 'access taxonomy overview':
      case 'view':
        return AccessResult::allowedIfHasPermissions($account, ['access taxonomy overview', 'administer taxonomy'], 'OR');

Therefore I think that the check in \Drupal\taxonomy\VocabularyListBuilder::getDefaultOperations() isn't needed anymore as it will be performed as part of the route access checking.

tstoeckler’s picture

Re #41: But that is a separate access operation. So a module could implement hook_entity_access() and grant or deny access for the 'access taxonomy overview' operation on a per-entity basis. That is a bit far-fetched, but still removing that check would be a behavior change which is completely unnecessary in general and certainly out of scope here.

The entity.api.php change is fair, that does make sense.

tstoeckler’s picture

Issue tags: -Needs change record

Added a change notice. Now we just need a hunk in entity.api.php.

berdir’s picture

> Re #41: But that is a separate access operation. So a module could implement hook_entity_access() and grant or deny access for the 'access taxonomy overview' operation on a per-entity basis. That is a bit far-fetched, but still removing that check would be a behavior change which is completely unnecessary in general and certainly out of scope here.

That's actually not that far-fetched. That's exactly why I implemented it like that and the reason we decided that it is enough for core to have a single permission that grants access to the taxonomy overviews.

Edit: it is also explicitly documented like that on https://www.drupal.org/node/2902390

hchonov’s picture

Thank you both for the clarification. Then I agree on everything with @Berdir and @tstoeckler.

wim leers’s picture

So - just like the 'view' operation is ambiguous and not clearly defined

How is this ambiguous at all?

- such a 'view many' operation is also very ambiguous

The 'view many' operation (collection permission) is here intended to override whatever the "view" operation says: even if you can't access an individual entity, you still can access the entity listing. How this overriding works exactly is what is ambiguous.
Yes, the ambiguity already exists for access content overview and access taxonomy overview. But that's just those two permissions for specific entity types. This issue is making that a standard for all entity types. That's why extra scrutiny is necessary.

That way the API First Initiative can rely on it in some way if it chooses to do so.

This is what makes no sense: "rely on it some way" and "IF it chooses to do so". It's important that listings/collections behave the same, regardless of whether you access them via HTML, RSS, JSON, XML or whatever else.
To make it more concrete: this issue should define how rest_export displays are affected.

This patch introduces a generic concept with a first usage. We can then […]

Once people start using it, we can't change it anymore. That's why I argue it doesn't make sense to do this only with a single usage.

How so? This patch should not affect Views in any way.

core/modules/node/config/optional/views.view.content.yml contains:

display:
  default:
    display_options:
      access:
        type: perm
        options:
          perm: 'access content overview'
Even though this is slightly confusing (because the naming collides), the check is actually checking an entity operation, not a permission, so no, this is not affected.

You're right! :)

The (Views-powered) listings can be customized by site builders, thus exposing additional fields. What if this is customized to show a different set of fields? (Some of which may contain sensitive data.)

Similarly I don't understand how that is in any way relevant to this patch.

… and I don't understand how you don't understand that it is relevant :)


It's all really quite simple:

  1. Entity/field access describe requirements to access certain data.
  2. We respect this when individual entities as HTML or RSS or JSON or XML (or JSON API/GraphQL in contrib).
  3. Respecting this is crucial, because otherwise you could perhaps see for example an image field in RSS but not in HTML, which would easily lead to unintentional access bypass issues.
  4. Entity access control handlers already respect the admin_permissions consistently and automatically. This means that everything in the entity type definition is respected and also applies to HTTP APIs: both any custom access control handler and any admin permission.
  5. This issue is making a prominent addition to entity access logic: collection_permission. Just like we're making sure entity access and field access work consistently everywhere, we also need to make sure that entity collection access logic works consistently everywhere. If we'd add this just for the (HTML) collection route, then it'd be the very first access-related thing that we cannot automatically respect in HTTP APIs. This is dangerous, because people rely on access control to protect their data.

That's all.

tstoeckler’s picture

OK, so I guess there are easy issues and then there are issues like this one. Let's see...

How is this ambiguous at all?

To "view" an entity simply means something different for different kinds of entities. For starters there is #2820315: Blocks' 'view' operation is interpreted as "render on page" instead of "read this config entity", then there is this in MenuLinkContentAccessControlHandler:

      case 'view':
        // There is no direct viewing of a menu link, but still for purposes of
        // content_translation we need a generic way to check access.
        return AccessResult::allowedIfHasPermission($account, 'administer menu');

Wait, so there's no way to view a menu link? I guess that's arguable, but it depends on how to interpret the word "view". Interestingly, there is no analogous piece of code for shortcuts, which I would have expected, but maybe viewing a shortcut means something different than viewing a menu link? Not really sure.

Then there is the whole issue of what it means to "view" a config entity such as a node type or an image style. We have sort of avoided this problem for the most part by introducing the "view label" operation, which is good for that reason, but it's also a bit out of place given that we also have the ability to check field access, so we now have the situation where $entity->access('view label') !== $entity->get('label')->access('view').

So in the hypothetical, analogous situation where we would be newly introducing the "view" operation of entitites to check access for the canonical HTML route and you would ask me what that means for the API-First Initiative and whether JSON API should allow access to e.g. node type, menu link or block config entities based on that operation my answer would be the same I have given above: "Maybe. We are introducing a generic concept with a first implementation and it may make sense for JSON API to pick it up and use it in the same way - or it may not." I guess in this particular case JSON API did decide to just use the view operation for everything (although I didn't check) and I think that's fair enough, but it's unrealistic to expect things like that to be defined up-front. They always depend on the context they are used in. And since JSON API is a fundamentally different concept it's unrealistic to expect everything to be defined in a way that just works in that context. We can and should (and do, I think) strive for concepts that are generic enough to work for all or at least most contexts, but to expect this issue to give a concrete implementation plan for how to use this new permission in JSON is completely unfair. We can try our best to explain what we are trying to achieve with this concept and we can give recommendations (I said above that I think it would make sense for JSON API to pick up this permission for its collection routes) but it's not possible to give a clear-cut and definitive plan.

The 'view many' operation (collection permission) is here intended to override whatever the "view" operation says: even if you can't access an individual entity, you still can access the entity listing. How this overriding works exactly is what is ambiguous.

I think that's a backwards formulation. There is no overriding here. Ignoring for now the ambiguity described above of whether showing entities in a list is equivalent to "view"ing them, "view" access should not be overridden. The collection permission simply guards whether the collection/listing can be viewed in the first place. Entities to which one does not have "view" access should still not be visible. So since I don't see how anything is being overridden, by extension I can't say anything about the ambiguity of that overriding.

This is what makes no sense: "rely on it some way" and "IF it chooses to do so".

I already elaborated above why this makes perfect sense and it is in fact the only way it can be. Again: We can and should try as best we can to create generic concepts which are versatile and sensible in different contexts and to document and explain them as good as possible, but at the end of the day the implementation depends on the context. There's no way around it.

It's important that listings/collections behave the same, regardless of whether you access them via HTML, RSS, JSON, XML or whatever else.

As a broad statement I agree with that. But different listings are different, so they necessarily behave differently. We show the results of the frontpage view to people who do not have the "access content overview" permission, both are listings/collections of the same entity type. If I have a node reference field with an autocomplete widget I will also see a list of entities in those autocomplete results. Again, I don't think we should be checking the collection permission for that.

To make it more concrete: this issue should define how rest_export displays are affected.

I guess you are talking about Views displays here. As stated above, I don't see where you are going with the Views argument. I can configure a View to check access for basically anything, including any permission that I deem appropriate for my view. I.e. I can change the default content overview as well as any rest export display I create to only be visible to users who have the administer unit tests permission. Is that a sensible choice? Probably not. But Views is a free-for-all in terms of access checking, so I don't know how to comment on this at all.

Once people start using it, we can't change it anymore. That's why I argue it doesn't make sense to do this only with a single usage.

I disagree 100% with that logic and history is certainly on my side when you look at when we've generalized concepts before. I think because we can't change what we introduce, we need to be cautious and not commit ourselves to things we are not certain about.

… and I don't understand how you don't understand that it is relevant :)

I tried to explain above why I don't get this at all.

It's all really quite simple:

Funny you say that, 4 or 5 booklength comments ago I might have believed you...

If we'd add this just for the (HTML) collection route, then it'd be the very first access-related thing that we cannot automatically respect in HTTP APIs.

This is certainly not true as is stated here, because in its implementation and in particular in its relation to the collection route the collection permission is analogous to the admin permission so it's definitely not the first. Maybe you can elaborate and what you mean.

gabesullice’s picture

From the IS:

Entity types such as Node and others provide a separate "overview" permission that is useful for granting people access the entity view, even if they do not have the admin permission. For example you may want to allow some people to edit entities, but not delete them or people may have access to edit and delete some entities but not others. In such cases it's necessary to access the entity overview but not have the admin permission.

I've strengthened the phrases that I think are the explain the original motivation for this issue.

It is very clearly about access to the view builder route because of the operations that are accessible there. Not about the distinction between "allowed to view one entity at a time" and "allowed to view more than one entity at a time".

The proposed resolution has been to provide a permission for "allowed to view more than one entity at a time" and then use it to grant people access the entity view.

However, I think introducing a concept of "allowed to view more than one entity at a time" is far too broad to solve this need. That is the heart of what Wim and I are getting at. This feels like a conceptual Trojan horse.

The relatively small missing feature of a default route permission is being used to introduce a much much broader concept. Indeed, similar reasoning for why this is okay is already in the comments explicitly:

OK, with #2702683: Add plural labels to entity types we have a precedent of these kinds of things being part of the annotation without actually being used (yet) in the route provider.

Which suffers from exactly the same Trojan horse logic: Plural labels are obviously good on their own and it was never stated in that issue that it would be setting a precedent. I doubt the committers thought about it that way. The idea that somehow the floodgates to add things that aren't used yet is now open can't be extrapolated from that issue. These things need to be considered on a case by case basis. WRT plural labels the only "bug" that might be introduced if modules don't use them is purely cosmetic. However, when you're talking about access control, the stakes are much higher.

The idea that this change could introduce a behavioral change is even acknowledged in the following paragraph of the same comment:

we should not add a collection permission to media, because that is actually a behavior change. And while it is a bug fix more than anything else, we will want to add test coverage for that, and I don't think that would be in scope here.

However, when I came into the issue to say that this permission will be a behavioral change for every site using JSON API, the answer is essentially "well, you can just ignore the permission."

This is not an acceptable answer if the introduced permission is about "allowed to view more than one entity at a time." It is only acceptable if the issue is about adding a permission for the list builder page.

So, which is it?

We can't move this discussion forward without answering that question.

If it's the former, I can't just ignore the permission. That would be an access bypass as soon as people start using the permission as you seem to intend it be used.

If it's about the latter, then let's just add a list builder permission or come up with a more targeted resolution that's within the scope of the present issue summary.

We can't introduce a completely new access control concept without understanding its consequences while pretending that we're only solving a relatively innocuous thing like a simple route default permission.

That's obviously the fundamental disagreement in this issue:

I still fail to see why not having a clear vision is a problem. This patch introduces a generic concept with a first usage.

Because while that makes perfect sense to you, I think that attitude is deeply concerning.


So, how do I propose we break out of this stalemate?

  1. If this issue is about a consistent way to permit access to an "overview" page. Then we need to be explicit about limiting the scope of the permission to this need or come up with an alternative resolution (I've proposed a couple already).
  2. If this issue is about adding an access control mechanism for "view many". Then we need to either update the issue summary or create a new issue.

I would welcome both, but I think we're going to loop endlessly if we say we "need to do #2 to solve #1".

If you'd like to argue that we should do #2 and have a discussion about the need for a generic permission for "view many", then we need to have that discussion, and with the potential for "hey, this will also be useful as a list builder permission too!"

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wim leers’s picture

Related issues:
tstoeckler’s picture

OK, after the frustration went away with the time (and then came back after re-reading this) here's a response:

The proposed resolution has been to provide a permission for "allowed to view more than one entity at a time" and then use it to grant people access the entity view.

That's not at all true.

I'm simply ignoring a bunch of points that are based on this assumption.

The relatively small missing feature of a default route permission is being used to introduce a much much broader concept.

This points to a grave misunderstanding of core concepts of the entity API, namely: We have a generic entity access API, that is independent of particular routes. Entity routes then use the entity access API to check route access.

Thinking in these terms makes this issue's scope very clear. We have an entity route (the one that uses the list builder) that has a need for access checking that is not covered by the current entity access API. Thus, we need to extend the entity access API (API is used in broad terms here) to satisfy that need.

Note that the entity access API is inherently and explicitly more generic than entity route access checking and changing that is not on the table.

Which suffers from exactly the same Trojan horse logic: Plural labels are obviously good on their own and it was never stated in that issue that it would be setting a precedent. I doubt the committers thought about it that way.

Not sure what you mean by that. Any code in core, or to be more specific, any issue that gets code into core is a precedent on how things get into core and serves as an example on what is accepted in core. That's the definition of the word precedent.

The idea that somehow the floodgates to add things that aren't used yet is now open can't be extrapolated from that issue

But it is used!

Again skipping some points that are obviously based on an incorrect assumption.

The idea that this change could introduce a behavioral change is even acknowledged in the following paragraph of the same comment:

we should not add a collection permission to media, because that is actually a behavior change. And while it is a bug fix more than anything else, we will want to add test coverage for that, and I don't think that would be in scope here.

However, when I came into the issue to say that this permission will be a behavioral change for every site using JSON API, the answer is essentially "well, you can just ignore the permission."

You are mixing up two different things here. The plan is certainly to use the collection permission for media entities in the future, just not in the scope of this issue. There the question is "Should we use the collection permission for the list builder route of another entity?" and the answer is a clear yes. For JSON API the question "Should we use the collection permission for other kinds of routes?" and the answer is maybe, but probably not, if only for backwards-compatibility.

This is not an acceptable answer if the introduced permission is about "allowed to view more than one entity at a time."

As stated that is not what the issue about.

It is only acceptable if the issue is about adding a permission for the list builder page.

You are setting up a false dichotomy here. There is not a binary choice between "allowed to view more than one entity at a time" and "allowed to view the list builder page". The semantics of the collection permission lie somewhere in the middle, probably pretty close to the latter, but as stated above its scope cannot be limited to a specific route because that would break the fundamental design of the entity access API.

If it's about the latter, then let's just add a list builder permission or come up with a more targeted resolution that's within the scope of the present issue summary.

(Since it's certainly not "the former", let's say the condition for this if is true, despite the previous point.)

Again, we cannot do that. The scope of this issue is as close as possible to that goal without violating the API principles of the entity access API. But that requires making it more generic than what you are asking for here.

We can't introduce a completely new access control concept without understanding its consequences while pretending that we're only solving a relatively innocuous thing like a simple route default permission.

You are really making it very hard to reply non-emotionally but I will do my best:

  1. Calling this a "completely new concept" is a vast overstatement, in my opinion, but that's just a minor point, I guess.
  2. I don't even remotely understand what you mean by "without understanding its consequences". I think it's quite easy to understand the consequences by simply looking at the patch. Since you are a maintainer of the module, the consequences to JSON API are really up to you and one possible consequence is "nothing".
  3. Also not at all sure what you mean by "while pretending that we're only solving a relatively innocuous thing like a simple route default permission". Barring the point made above about route access vs. entity access I'm not sure where anyone is pretending anything.

I think that attitude is deeply concerning.

Yet you don't elaborate what specifically or why you find it concerning, so not sure what to respond to that.

So, how do I propose we break out of this stalemate? ...

The answer is 1. or in other words "this issue is about a consistent way to permit access to an "overview" page". I have asked above and will ask here again what specifically you have in mind when you say "limiting the scope of the permission". If you could clarify this point maybe we can move forward here.

kristiaanvandeneynde’s picture

Playing the devil's advocate here, but if the worry is that JSON API is currently providing a collection route that assumes access based on individual view permissions and said route would now become "insecure" because there is a new view collection permission which is not being checked, is that not a problem specific to JSON API?

I would assume that it was up to JSON API in the first place to either use the existing convention (admin permission needed) or to add a new permission that grants access to said route (as node does, among others) and then check for said permission in the first place.

Aren't modules ultimately responsible for using the right permissions for the right routes or am I missing something here? Don't mean to offend, but I too am a bit puzzled why something so seemingly simple is being discussed so elaborately.

gabesullice’s picture

The answer is 1. or in other words "this issue is about a consistent way to permit access to an "overview" page". I have asked above and will ask here again what specifically you have in mind when you say "limiting the scope of the permission". If you could clarify this point maybe we can move forward here.

Great. I'm glad we've finally agreed on a path forward :)

I'm sorry, I didn't realize that I had not already made that point clear in my many comments. So, now, here it is unambiguously:

To limit its scope, I believe that the permission should be named or heavily documented in such a way that it cannot be conflated with "access to view more than one entity at a time". IOW, It should be very clear that it is about accessing the administrative overview page, not viewing lists of entities in general. To do that, I propose renaming it to list_builder_permission or overview_permission since that name will be more familiar to Drupal site builders.

Prior to you last comment, it seemed pointless to make concrete recommendations because it seemed we fundamentally disagreed about the meaning of the permission. In #32, you said:

The collection permission is a shorthand for "access a list of entities of this type".

Thankfully, I think we are no longer in conflict, because you say in #51:

[In] other words "this issue is about a consistent way to permit access to an "overview" page".


@kristiaanvandeneynde. No offense taken! Hopefully my explanation will be clarifying for everyone.

JSON API does not have Views exports. It has "collection resources", they live at /jsonapi/{entity_type}/{bundle}.

This resource is what one would use to fetch a "recent blog posts" listing: /jsonapi/node/post?sort=-created

Any anonymous or authenticated visitor should have access to that resource—it's essentially the RESTful version of the "Frontpage" view.

Notably, the frontpage view is protected by the view published content permission not access content overview permission.

We decided not to require the view published content permission to access that resource for three reasons:

First, it would simply be duplicative. We check individual access to each entity in the returned collection. IOW, if the user does not have the view published content permission, the response is empty, there's just no point in sending a 403 Forbidden.

Second, there's no equivalent of admin_permission on entity type annotations for a view permission. There's just no way for us to determine which view permission should be used across all entity types. We'd have to hardcode an appropriate access check for every entity type in core and in contrib.

Finally, node itself has view published content but it also has view own unpublished content. What if a user only has the view own unpublished content permission? If we guarded the collection resource with the view published content, it would be impossible for a user to get a list of their own content.

With that established, let's think about what it would mean if a collection_permission were added.

There's two obvious interpretations:

  1. It's a permission controlling access to a listing of entities
  2. It's a permission controlling access to the overview page

If it's to be interpreted as #1. Then it would be wrong of us to not respect it. It is a new access restriction on listings that did not previously exist. What should we do? Respect it, and we break the thousands of sites which will not have this permission assigned to their users. Disrespect it and we are bypassing something that could rightly be understood as an access restriction.

If it's to be interpreted as #2. Then we should ignore it. It's irrelevant to us. But of course, as it's currently named, I fear some people will expect that it means #1 and will mistakenly believe it's providing an access restriction. Thus, I proposed to name it differently to preclude that possibility.

The worst possible case is that it ended up that both were true. I.e., that the collection permission is documented as #1 (forcing JSON API to respect it) and then also used as #2. That unfortunate combination would mean that any site wishing to use JSON API would need to give every one of their users the collection_permission (JSON API is effectively useless without it). In doing so, they'd then be giving all of their users access to the administrative overview page! Something that I think we would all agree would be an unintended outcome.

And in #32, @tstoeckler confirmed that this third possible case was actually the reality:

The collection permission is a shorthand for "access a list of entities of this type".

Thankfully, @tstoeckler seems to have changed his mind. In #51, he said:

[In] other words "this issue is about a consistent way to permit access to an "overview" page".


All of that means... I think that this issue is no longer blocked since we've appeared to be in consensus about the permission's meaning! 🎉

Now, how do we guarantee that it's documented and understood as such? I've proposed some ideas above. What does everyone think of those?

kristiaanvandeneynde’s picture

Thanks for the elaborate explanation Gabe.

I think you're reading too much into it, though. You're arguing a case using /node as an example, whereas the "collection" link template for nodes has always been /admin/content*. Node even has a collection (overview) permission which it uses for exactly that page.

For all intents and purposes, core uses the "collection" link template as "here you may find the overview page", so in my book there can be no confusion as to what a collection permission means: Something to access the collection link template with.

The fact that Node or JSON API expose multiple listings has zero to do with the collection link template; that one always remains the same. I'd even argue that JSON API is free to choose whether or not to respect said permission when building its own listings: If it mimics /node, don't do anything; if it mimics /admin/content, check for the permission. (Even though both are really similar. Thanks, Node.)

In short: A "collection permission" granting access to the page defined in the "collection" link template seems perfectly clear to me. Would you see any issue in an "edit form permission" granting access to the "edit-form" link template?

* Core does not literally state it as such, but there seem to be a couple core content entity types which forget to specify a collection link template. Node and Comment don't specify one, User and BlockContent do...

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

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

robertom’s picture

StatusFileSize
new9.96 KB
new10.87 KB
aaronmchale’s picture

Re #56:

--- a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
+++ b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
@@ -318,7 +318,11 @@ protected function getDeleteFormRoute(EntityTypeInterface $entity_type) {
   protected function getCollectionRoute(EntityTypeInterface $entity_type) {
     // If the entity type does not provide an admin permission, there is no way
     // to control access, so we cannot provide a route in a sensible way.

Documentation here needs updating to indicate this is now collection or admin permission.

The patch adds collection_permission to Vocabulary, are there any other Core Entity Types which we can modify in this patch? @Berdir provided some insight in #17, although in #23 @tstoeckler indicated we might be better to use follow-up issues. Personally I don't mind if this patch addressed it for some Core Entity Types where it's a simple drop-in replacement (i.e. those Entity Types already provide a simple collection/overview permission where they only apply it to their collection route, even if they use their own route provider class). I wonder how easy Node and User would be, given those also use Views where available. In #24 @tstoeckler strongly objected to this being added to Media in this issue (citing behaviour changes which would need a follow-up), but also pointed out that we could still add the key to Entity Annotations without actually implementing the usage in that Entity Type's custom Routes/Route Provider, I'm not against doing that but leaving it half implemented leaves room for confusion further down the road, so that leads me back to just using follow-ups for each Entity Type where it's not a simple drop-in replacement.

Adding #2954866: Add a collection permission to media entities to the related issues.


After reading through the lengthy discussion from #27 through to #54, I agree with the final comment in that series by @kristiaanvandeneynde (#54) which I think helps to bring some context to parts of the discussion.

When it comes down to it granting access to the collection route (whether that be HTML, JSON API, or another form) has no real security implications, because unless you also grant access to view or edit or delete the Entities which are supposed to be listed, the user will always see an empty list, regardless of whether they happen to be viewing a HTML table or a JSON list. We're also not introducing any new concepts here, we're simply making some code changes which make it easier for participating Entity Types to provide a permission for "access X collection/overview page". With or without the patch, permissions for these existing pages stay the same and JSON API still returns a populated or empty list, this patch and the scope of this issue don't change that. So the implications for JSON API and others is to actively choose whether or not to alter their processes to check collection_permission, and that's an active choice which should be done in follow-ups, the passive choice for these modules of doing nothing means that nothing changes, everything works the same.

The above is true in all cases, even a contrib module which provides its own Entity Type, the collection permission for that Entity Type will apply to the collection route provided by that module, that's true with or without this patch, the only difference is that with the patch the module can provide the collection permission in the Entity Annotation and save on a few lines of custom code. Again, even in this case, there are no changes for how JSON API behaves, with or without the patch JSON API will still return either a populated or empty list based on if the user has access to view those Entities. In the event that JSON API (and others) make an active choice to change that behaviour from returning an empty list to doing something else is entirely up to JSON API (and those others), but again it's an active choice which must be made in follow-up issues, the passive (non BC breaking) option is to still return the empty list.

I'm hopeful we're at a point where everyone is satisfied, and we can progress this.

aaronmchale’s picture

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.

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.

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.

vlad.dribas’s picture

I know it is probably outdated, but think can be useful for someone

For some cases you can just add collection route to .routing.yml

entity.ENTITY_NAME.collection:
  path: 'PATH_TO_LIST_FROM_ENTITY_ANNOTATION'
  defaults:
    _entity_list:  'ENTITY_ID'
    _title: 'TITLE'
  requirements:
    _permission: 'YOUR CUSTOM PERMISSION'

And in ENTITYAccessControlHandler.php check access for update/delete/view

  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface {

    switch ($operation) {

      case 'update':
        return AccessResult::allowedIfHasPermissions(
          $account,
          [
            'YOUR CUSTOM EDIT PERMISSION',
            'administer ENTITY',
          ],
          'OR'
        );

      case 'delete':
        return AccessResult::allowedIfHasPermissions(
          $account,
          [
            'YOUR CUSTOM DELETE PERMISSION',
            'administer ENTITY',
          ],
          'OR'
        );

      case 'view':

        return AccessResult::allowedIfHasPermissions(
          $account,
          [
            'YOUR CUSTOM VIEW PERMISSION',
            'administer ENTITY',
          ],
          'OR'
        );

      default:
        return AccessResult::neutral();
    }

  }

For creating another method

  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
    return AccessResult::allowedIfHasPermissions(
      $account,
      [
        'YOUR CUSTOM CREATE PERMISSION',
        'administer ENTITY',
      ],
      'OR'
    );
  }

And of course add all custom permissions to .permissions.yml, aplly to any role, so that role will get access for view/edit/create/update and don't get administer access

randalv’s picture

I'd like to bump this issue again, I see it's been silent for a long time now and yet this issue persists in Drupal 9.5.x and I'm guessing 10.x.x as well.

Not being able to simply allow editors/site builders to see the media collection is a *huge* issue in my opinion, granting them "administer media"-permissions is far from ideal and in fact a completely ridiculous idea.

I've "solved" this in one of our projects by simply altering the entity.media.collection route by subscribing to the routes alter event as shown below.

Yet, this is not the solution. I refuse to apply this to all our projects, this is something so trivial that simply needs to land in Core.

Can we make any progress with this issue and hopefully come up with a solution that works for everyone?

<?php

namespace Drupal\[MODULE_NAME]\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Subscribes to route alter events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritDoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    if ($route = $collection->get('entity.media.collection')) {
      $route->setRequirement('_permission', $route->getRequirement('_permission') . '+access media overview');
    }
  }

}

mrweiner’s picture

Also want to bump this. The semantics arguments around viewing one vs many entities was a complete departure from the actual goal of this issue: to provide a permission for granting access to the entity.type.collection route. This really had nothing to do with json api or general entity access.

@mglaman had a perfect solution for all of this in #29

list_builder_permission would mitigate the semantics issue around the route being "collection" and permissions being named "overview". The use case we're working against is the list builder, not an API resource with a collection of entities.

Or even just...collection_route_permission. IMO this would be even more specific.

mrweiner’s picture

Alright, I've created an MR that includes the patch from #56 and a fix for the documentation issue noted in #57. After reading through things again today, I think that @AaronMcHale was spot on in #57, so I've left the permission name as-is instead of changing it to list_builder_permission or whatever else. I agree that followups are probably the place to handle additional core entity types. If tests pass then I will mark this RTBC in the hopes that we can move things forward.

mrweiner’s picture

Status: Needs work » Reviewed & tested by the community

Alright, given that tests are passing, and reiterating that I think #57 perfectly sums up why this approach is okay, I'm marking this RTBC. Let's see if we can't get this in after 5 years.

aaronmchale’s picture

Status: Reviewed & tested by the community » Needs work

#1975064: Add more granular block content permissions introduce a access block library permission for block_content in 10.1.x, so we could also add that to the block content entity type annotation.

anybody’s picture

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.

tstoeckler’s picture

Status: Needs work » Needs review
Issue tags: -Needs documentation

So I went ahead and rebased this for the 11.x branch. I initially pushed this to the existing MR, but then realized that I cannot change the target branch there, so I then created a new MR. Sorry if this caused any confusion. I also added the collection permission to content blocks per #72.

Since this was tagged "Needs documentation" I wanted to go ahead and take a stab at that, but I have to disagree with #36 ("This should be documented in entity.api.php, just like admin_permission is already documented there." @WimLeers). The only mention of the admin permission in entity.api.php is the following:

most entities can just use the 'admin_permission' annotation property instead [of declaring a custom access control handler]

That sentence is still valid with this issue and I contend that that counts as actual documentation of that property. I agree, however, that we should have proper documentation for the admin permission, so I opened #3367439: Add proper documentation for entity type's admin_permission and collection_permission. Depending how that goes, we should add documentation for the collection permission here. I'm removing the "Needs documentation" tag for that reason.

Finally, I re-read the discussion above about whether or not this should impact JSON:API and I still strongly believe that it should not and I think the fact that JSON:API is now in core makes this an easier case to make than when we had the discussion before. Before we were discussing whether or not adding the concept of a collection permission in core would lead to the expectation that JSON:API in contrib should be affected by that in any way. Now that JSON:API is in core we no longer have this ambiguity of expectations: by not changing anything in JSON:API we make it very explicit that we do not consider exposing an empty list for a user without the collection permission a security issue. Another decoupled module may or not make the same choice that JSON:API did, but in any case it cannot be considered a security vulnerability because core sets this precedent.

Furthermore, one thing that @gabesullice brought up above (#31) that I did not pick up completely in the discussion previous that I think is the nail in the coffin of any reservations against my argument is the fact that JSON:API already grants access to the JSON:API collection for users without the admin permission. While the collection permission is useful for editorial entities, we will always have lots of entity types, in particular config entity types, which only declare an admin permission and not a collection permission. And since their JSON:API collection routes will always be available (albeit empty) for anyone regardless of any permissions, it would be horribly inconsistent for JSON:API to start denying access to its collection as soon as a collection route is declared.

To sum up, I think this is finally ready to land.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record updates

Can the change record be updated please.

tstoeckler’s picture

What exactly do you think needs updating?

smustgrave’s picture

Was last updated 5 years ago to be introduced in 8.6

tstoeckler’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record updates

Ahh OK, missed that. Done.

smustgrave’s picture

Thanks! Will try and review this evening or tomorrow if no one beats me to it.

One nit picky I see is if the new get function could be type hinted. Probably can be handled during commit but if you get time.

tstoeckler’s picture

Great, looking forward to it @smustgrave, thanks!

Added the return typehint.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Needs Review Queue Initiative

Hiding patches as the fix is in MR 4201

Test coverage appears to cover the different combos of collection and admin permission.

I don't see any lose for those using just admin permission either.

Think this is ready for committer review.

kristiaanvandeneynde’s picture

Just had a quick look at the MR and it also looks fine to me.

catch’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +10.2.0 release highlights

Committed/pushed to 11.x, thanks!

This is very API focused but tagging for 10.2 release highlights in case there's something to group it with.

  • catch committed 4077f20c on 11.x
    Issue #2953566 by vijaycs85, tstoeckler, mrweiner, robertom, mohit1604,...
wim leers’s picture

Thank you so much @tstoeckler for your detailed write-up in #76! I was wondering how this impacted JSON:API and was surprised to see no mention at all in either the diff or the change record. I do think it'd be valuable to update the change record with how exactly this does or does not impact "the API use case" — no matter whether it's REST, Views REST export displays, JSON:API or GraphQL.

Would it be possible to still add that? 🙏

tstoeckler’s picture

I added a note to the change record about JSON:API, fair enough. Not sure what to say about the other examples, though:

  • Rest doesn't have anything like a collection, so not sure where people could possibly expect the collection permission to be relevant.
  • Views Rest export displays can be a collection, but you can configure the access control, how you want, so you can just configure whatever permission you want to be checked for your export and in fact you could use "access content overview" or any other collection permission for the display. So not exactly sure what we could add to the change notice in regards to that
  • Not really sure about GraphQL as I have never properly used it so couldn't really say if or in how far the introduction of the collection permission might need clarification there.

So yeah, totally fine with making the change notice more explicit and ideally more helpful for people, but not really sure what to add, in particular.

wim leers’s picture

I think explicitly documenting that they are not affected would be very valuable! People reading this will wonder "how does this affect me?", and seeing JSON:API + routes-for-your-entity-type information is great, but I think the first 2 bullets you listed in #88 would make excellent additions to the change record! 🤩

tstoeckler’s picture

OK, added some more info about Rest and Views. To be perfectly honest, I personally find the change record less clear after the last diff, but if you find this clarifying then I guess there's a decent chance others will, as well, so let's go with it.

opauwlo’s picture

if you have issues with the permission this maybe can help:

entity.ENTITY_NAME.collection:
  path: 'PATH_TO_LIST_FROM_ENTITY_ANNOTATION'
  defaults:
    _entity_list:  'ENTITY_ID'
    _title: 'TITLE'
  requirements:
    _permission: 'YOUR CUSTOM PERMISSION'
    _role: YOUR ROLE

it's very like to the one shared by vlad.dribas but with you don't pass the role sometimes don't work correctly.

Status: Fixed » Closed (fixed)

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