Problem/Motivation
While working on #2691337: Merge crm_core_contacts_ui into crm_core_contacts we discovered the following:
- addPage() checks createAccess() for bundles of the wrong entity type, and the tests aren't complete enough to catch it.
- there is a missing access check to ensure the user can create an entity of at least one bundle, before showing addPage()
- there is an $entity_type_id marker set in route requirements in \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider::getAddFormRoute() which as a result prevents us to access dynamically created "add-form" route.
Proposed resolution
- Replace bundle entity type with correct entity type (entity type of an entity that's being created) in EntityController::addPage()
- Implement EntityCreateAnyAccessCheck to ensure a user can create an entity of at least one bundle.
- Remove placeholders for $entity_type_id in _entity_create_access requirement in \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider::getAddFormRoute()
- Provide tests
| Comment | File | Size | Author |
|---|---|---|---|
| #50 | incomplete_broken-follow-up-2696669-50.patch | 473 bytes | jibran |
| #45 | incomplete_broken-2696669-45.patch | 20.81 KB | sanja_m |
Comments
Comment #2
mbovan commentedAdded related issue where getAddPageRoute() was introduced.
Comment #3
mbovan commentedHere is the initial patch, no tests yet. Some thoughts?
Comment #4
bojanz commentedThanks for getting the ball rolling on this.
You should inject the entityTypeManager, not the entityManager (it's deprecated).
Comment #5
berdiruse entity type manager.
Don't hardcode that there is a bundle entity type. Instead, get the bundles from the EntityBundleInfo service. Also, check if there is a bundle entity key, if not, call createAccess() once, without a bundle.
Comment #6
berdirMaybe we can extend the tests from the issue that added add-page, and e.g. extend the test entity type create permissions to be per-bundle optionally and then in that test, give the user only bundle specific permissions?
Comment #8
mbovan commentedUpdates based on the feedback.
There are still test (and non-test) fails when it comes to displaying the list of bundles in case a user has "create {bundle} entities" permission only...
Comment #10
mbovan commentedOops, didn't include
EntityTestPermissionsfile.Comment #12
berdir"any entity creation" isn't very specific.
not sure what a good short description would be. Maybe "for creating an entity of any bundle."'
$entity_type_id and then you can use $entity_type for the definition.
this should also pass TRUE to return an access result object.
IIRC, getBundleInfo() returns a bundle_info array. Either name it like that, but since you don't need it anyway, you could also do an array_keys() on getBundleInfo()
// Create a bundle that the user does not have create permissions for?
I would limit it to the permissions we actually need, we can always extend it when necessary.
Also, not sure if we should get the entity type in there to avoid clashes, especially since it is a test module. E.g. create test entity_test_with_bundle entities or so.
Comment #13
mbovan commentedFixed the points above.
It looks like there is another bug which appears when we want to list all bundles (
EntityController::addPage())... It could be thatEntityAccessControlHandler::checkCreateAccess()checks only for an admin permission and not for "create {bundle}" as well... Also, it might not be possible to know the "create bundle" permissions at that time, not sure...Here is what I get on

/entity_test_with_bundle/addwith a user having "create {bundle} entity_test_with_bundle" permission only. Note: there are 2 created bundles.Edit:
Will update permissions within the next patch.
Comment #15
berdirThere is indeed another bug here.
I now finally understand why @mbovan tried to implement access checks on the bundle entity type in the core_crm issue that caused us to find this.
And that's that we check create access for the bundle entity type with the given bundle, instead of the entity type we want to create an entity of. That of course doesn't make any sense and caused it to fail.
After that, there was just one fail remaining. And thinking about this, I finally understand the administer content types permission check in NodeAddAccessCheck.
The question is, what should happen if there are no bundles yet. The test currently expects that you get a page that allows you to create one. But if there's no entity types, we had nothing to check and access was denied.
The answer is to check if the user is allowed to create bundle entities. If so, we allow access. That ties the access check to EntityController::addPage(), though, other pages might not want that behavior, so maybe we need to make it configurable with a special requirement flag?
Comment #16
bojanz commentedRetitling.
We have two issues here:
- addPage() checks createAccess() for bundles of the wrong entity type, and the tests aren't complete enough to catch it.
- there is a missing access check to ensure the user can create an entity of at least one bundle, before showing addPage()
The patch fixes both and adds missing test coverage.
Nitpick: EntityTestPermissions is new code, but uses array(). If a reroll happens, let's use [].
That makes sense. I don't see a need for a special flag though, the additional logic feels reasonable.
Let's say I have a content entity form where the bundle is selected with #ajax. If there are no bundles I still wouldn't want a 403, I'd want an opportunity to present my own message/UI logic, and the bundle entity type check makes sense again.
Comment #17
mbovan commentedUpdated #15 with [] syntax.
Also, extended test to check if we can actually create entities. I would expect that adding "create test entity_test_with_bundle entities" is allowing us to create an entity of that bundle, which doesn't seem to happen?
Maybe unrelated, but, what happens with the case when we want to create just an entity, not an entity of the bundle? I guess in that case we would need to check if a user has create access for that specific bundle - what we can check in an access handler for bundle. In crm_core case, ContactTypeAccessControlHandler::checkCreateAccess()?Edit: Got the explanation from @Berdir, we will not have $entity_bundle in bundle access control handler because... there are no bundles for bundles. :D
Comment #18
mbovan commentedThe patch.
Comment #19
mbovan commentedUpdated the issue summary based on #16.
Comment #21
slashrsm commentedThis is entity *type* manager. Super nitpik.
I quickly checked this and it seems we are passing "entity_test_bundle" instead of "test" as $entity_bundle to checkCreateAccess() on access control handler. This is wrong as this isn't id of the bundle but it's entity type id.
Quickly poking deeper it seems that the value of _entity_create_access on the route might be wrong.
Comment #22
dawehnerNice, I wasn't aware that this is possible!
Do you think the test is wrong or is there some actual bug in the code still?
Comment #23
mbovan commentedUpdated the issue summary with the new bug from #18. Working on a new patch.
Comment #24
mbovan commentedProvided a fix based on @Berdir's proposal on IRC and fixed #21.1.
Comment #25
dawehnerOh wow
Comment #27
mbovan commentedCreated a followup for link templates in test entities #2699959: Update link templates of test entities
Comment #28
mbovan commentedThe patch and the interdiff against #24.
The proposed patch/fix doesn't cover the case of EntityTestMul that uses $bundle_key in its add-form link template/route. DefaultHtmlProviderTest should be fixed.
Comment #30
berdirThis fixes the unit test, the unit test now actually has to use a path that makes sense and there's now always a call getEntityBundleType().
Comment #31
heddn+1 on the approach. I ran into this a while back but wasn't able to get my PR merged into entity in time. And this is a much cleaner solution.
Comment #32
bojanz commentedLooks like we're good to go. I'm happy with punting the general entity_test route cleanup to a followup.
Comment #33
catchMostly nits. I don't have good ideas for the naming, feels like we're running into the limit of terminology a bit.
Tagging this as rc target, also leaving it RTBC in case someone else wants to commit it before RC - but probably won't be me.
Not sure about the service name. It's not for creating any entity.
Similarly not sure about the classname - it's not for creating any access checks.
Comment isn't 100% clear whether it's any bundle of an entity.
Or any bundle entity.
Now we're not creating entities but entity types.
typo.
inconsistent.
Comment #34
mbovan commentedFixed #33.4 - #33.6.
Comment #35
berdirComment changes look good to me.
Comment #36
wim leersWhy this break? What if a particular bundle returns FORBIDDEN as its access result?
Back to NW for this.
So say that you don't have any bundles for
Node, this is saying that the user will be able to see a "Create node bundle" link?I don't see where that is happening in
EntityController::addPage()?Comment #37
berdir1. Forbidden is fine. Just because one is forbidden doesn't mean that you can't create any others. This isn't combined access for a single thing. It's a real OR, not an AccessResult or.
2.
Yes, it exists.
Comment #38
wim leers#37:
LGTM.
Comment #39
slashrsm commented+1 for RTBC
Comment #40
alexpottGiven Wim's comment I think we need to improve comment here.
Also I think we have another case to consider. What happens if a user can create bundles but does not has access to the any of the bundles already created?
Comment #41
berdirThat would be a new feature and I'd consider that to be highly irrelevant.
This is modelled after node/add and afaik, nobody ever cared about that scenario in the last 10 years :)
Comment #42
alexpottWith node/add you get access if you have the ability to create bundles regardless of whether you have permissions to create nodes with any of the bundles.
Comment #43
mbovan commentedUpdated the comment and the create bundle access check.
Comment #44
berdirLooks good, but we should expand the tests to also cover this.
Create yet another user, do not give him any permissions to create entity_test_with_bundle entities but give him permissions to create entity_test_bundle entities. He should be allowed to access the page and will not see any bundles but should see the message that he can create them.
Might need a change on EntityController::addPage() too, we need to check access before displaying the empty message.
Comment #45
sanja_m commentedAdded tests mentioned in #44.
Comment #46
berdirGreat, thanks.
Comment #47
bojanz commented#43 and #44 look good. Let's get it in.
Comment #48
alexpottThanks for addressing the odd edge case. This looks great.
Committed 180191f and pushed to 8.1.x and 8.2.x. Thanks!
I removed the gendered pronoun on commit as per our code documentation standards.
Comment #50
jibranQuick followup patch.
Comment #51
alexpott@jibran I think it is best if we don't re-open issues for things like that. Also given the likelihood of re-introducing this codign standard regression I think we should just have a single issue were we re-file patches just to tidy this up so it doesn't become a commit credit thing.
Comment #52
alexpott