Problem/Motivation
#2958241: Impossible to reply to comments: commented entity considered unreferencable because CommentSelection::entityQueryAlter() joins on {node_field_data} table causes a regression when you have a comment reference on a non-comment entity type. Our use case is a contact form allows to report a comment and references that comment. This is a fatal now on 9.5 and worked on 9.4, so I think this is major if not critical, only reason it's not is that it might be a bit an obscure use case.
Steps to reproduce
1. Add a comment field anywhere, for example on a node type
2. create such an entity, reference a comment, save.
Proposed resolution
Check that the context entity really is a comment. That's more a workaround though, I'm not sure what should happen in such a case, I didn't look into what exactly the purpose of this check even is.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #23 | comment-reference-3332546-23-test-only-interdiff.txt | 1.15 KB | berdir |
| #23 | comment-reference-3332546-23-test-only.patch | 4.6 KB | berdir |
Issue fork drupal-3332546
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
berdirAdditional problem: the code hardcodes the data table, but that is not given to exist, it needs to fall back to the base table.
Comment #3
djsagar commentedFixed CCF for #2.
Comment #4
fjgarlin commentedI worked on the previous issue.
Why don’t we just change
if ($comment) {toif ($comment && ($comment instanceof CommentInterface)) {and leave the rest unchanged? If we don't even have a comment in the "CommentSelection" plugin, we shouldn't go ahead with the rest of the code.Also, the issue with hardcoding "node" is assuming that the entity will be a node, and that even the "node" module will be installed. That’s why the previous issue (and related) were initially reported. ie: #3272023: Do not check node_access if node module is not installed
Comment #5
andypostComment #6
fjgarlin commentedThis patch is more generic and doesn't fall back on the hardcoded "node". It's still a workaround and it still needs tests, so I'll mark it as "Need work".
Help with the tests specially would be appreciated but in any case, I'll try to give it a go.
Comment #7
fjgarlin commentedI've been doing some good debugging and I am able to reproduce it on a gitpod instance and I think I have identified all the parts of the code that might need changing. I will try to get a test as well as the code. I am assigning this to myself but if somebody else wants to try it feel free to ping me.
Comment #9
fjgarlin commentedAs the MRs testing is failing, I attach it in patch format. Attaching both a test-only and a full patch.
Comment #11
larowlanSo prior to #2958241: Impossible to reply to comments: commented entity considered unreferencable because CommentSelection::entityQueryAlter() joins on {node_field_data} table we joined against node for access checking.
That appears to be lost in the current patch?
Comment #12
fjgarlin commentedIt was mostly to optimize the query to have a smaller subset of records. The access checking happens on the "getReferenceableEntities" method that was introduced in #2958241: Impossible to reply to comments: commented entity considered unreferencable because CommentSelection::entityQueryAlter() joins on {node_field_data} table. This part of the code is run on validation, but also when querying available comments.
The issue is that in the past comments were always linked to nodes, but that's not the case anymore, as they can be linked/referenced to/by any entity, and even the same comment type could be linked to many entities, so you cannot just join those tables anymore.
However, 99% of the use cases will be comment replies (where the reply has a reference to the parent comment) and in that case, when we have the entity and it's a comment, we do the join if the entity that the comment is attached to does have the necessary tables to join (that logic was implemented in the previous issue).
So all we are doing is making sure that the 1% doesn't go through the "if", by checking the type. If it's not a comment, then don't do the joins as we have no way of knowing which comment it is (because $comment is actually the entity that has the comment reference, not the comment). It's kind of complex to explain, so I hope that it makes sense.
I was discussing these cases and options with @berdir and @catch, in case you need further context.
Comment #13
bobooon commented+1 RTBC
Works as expected and the type checking makes sense.
Comment #14
smustgrave commentedThis issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.
Removing the tests tag as they were added in #9.
Yes there was a bug with the MR composer build a few weeks ago that has been resolved.
I can confirm the bug still exists on D10.1 and that the patch in #9 does solve the issue.
Comment #18
berdirFWIW, this addresses the original case that I reported, but IMHO it doesn't fully address the additional case we discussed when referencing a comment of a node that is unpublished and we don't have test coverage for that yet.
Comment #19
fjgarlin commentedThe above "if" only runs when saving the data.
When a node is unpublished, then any other entity trying to reference the comment won't even get it offered thanks to this: https://git.drupalcode.org/project/drupal/-/blob/10.1.x/core/modules/com...
If the comment was already saved previously to unpublishing the node, the reference is still kept but the comment won't show in front-end due to a similar check on the comment entity. I think this is the expected and right behaviour.
I'll try to work on a test case for it anyway.
Comment #20
fjgarlin commented@Berdir - I've added two more tests:
* As a user with "bypass node access" permission, I can choose the value of the comment of an unpublished node and view it, but users without that permission can't view it.
* As a user without "bypass node access" permission, I cannot even set the value of a comment of an unpublished node.
Please review this. Those were the only additions. I am using the MR instead of the patch (which I'm hiding).
Note that the error reported in #17 was a false negative. The issue was marked as RTBC, but after @Berdir feedback, I guess the logical step is to mark it as "Needs review" again.
Comment #21
fjgarlin commentedHiding files which aren't relevant anymore.
Comment #22
fjgarlin commentedComment #23
berdir> * As a user without "bypass node access" permission, I cannot even set the value of a comment of an unpublished node.
Yes, but that is that assuming that you are using the UI, and you changed the test to use an options list, at this point you're not testing entity validation, you're testing that the select doesn't initially contain that option.
But entity validation must work on its own as well, for example when using REST/JSON:API.
I adjusted your test to verify the validation API directly instead of using the UI, just for that method. I think it would probably make more sense for this to be a kernel test and always use the API, but I just made the minimal required changes for now. This incorrectly doesn't cause any validations in 9.5 and I expect it to do so in 9.4.
Comment #25
andypostLooks ready, NR as failure from test-only patch
Comment #26
berdirI should have uploaded a complete patch for 9.5, it is a test only patch, but the point is that it's still failing now with that change on 9.5 and passes on 9.4.
I did outline a possible solution in our slack discussions, that involves looking at the selection handler configuration, picking the target bundles and then using their configuration and target type to do the correct join on the right host entity type. That means we'll also need another test like the current one but where the comments are attached to entity_test or so and not a node.
And as a fallback if we don't have a target bundles, we'd need to explicitly check access on the comments being validated. Despite the other method already doing that as well, we still need the query join as there might only be a few accessible comments out of many.
Comment #27
fjgarlin commented@Berdir - thanks for the small code change to the test and the explanation after. I'll try to work on that approach next.
Just as a reminder (to myself?) about the conversation we had:
We can only make the joins if the comments can be attached just to one bundle.
We do not have the comment being validated in the function, only the host entity. So not sure about this fallback part.
Comment #28
berdir> We can only make the joins if the comments can be attached just to one bundle.
Fair enough, to be very correct, it could be multiple bundles as long as they share the same host entity type, that's what we can only support one of.
> We do not have the comment being validated in the function, only the host entity. So not sure about this fallback part.
Not in this function, but we have them in validateReferenceableEntities(). But loading them and checking access is slow, so I think we should only do that if we have to (like getReferenceableEntities() could also be optimized to do so).
Comment #29
fjgarlin commented@Berdir - turns out that we do not need to load entities on "validateReferenceableEntities", so I mirrored the logic found in the other functions.
"buildEntityQuery", "validateReferenceableNewEntities", and now too "validateReferenceableEntities" filter out the unpublished nodes from the results.
Also, "entityQueryAlter" checks the type of the variable before making any additional joins.
I turned the Functional test into a Kernel one as well.
I think this covers all cases of UI and also validation API, so I'm not sure if we need to implement the additional logic related to the selection handler configuration in the "entityQueryAlter" method.
Please review and let me know if there are any additional cases that we need to cover or further things to cater for.
Comment #30
smustgrave commentedConfirmed this issue on D10.1
Using standard profile, added a reference field on the Article content type, referencing comments
Created an Article, added a few comments
Created a 2nd Article tried referencing one of the comments from Article A and got the fatal error.
Applied the MR and the issue is resolved.
#23 shows the tests are properly covering the change.
Comment #31
larowlanLeft one minor request for the test
Can we also get a version of this for 11.x?
Thanks
Comment #33
fjgarlin commentedAddressed the minor request and created an MR for 11.x: https://git.drupalcode.org/project/drupal/-/merge_requests/4072
As the change was minor and the issue was RTBC I will set it back to RTBC
but if that's not the process please let me know or just change it to Needs reviewas agreed with @larowlan via slack.Comment #34
larowlanComment #35
larowlanLeft a comment on the MR, I think something's not right in the test
Feel free to ping me when this is back at RTBC
Comment #36
fjgarlin commentedAgain, addressing really minor feedback, tests are green again, so back to RTBC, and will ping @larowlan via slack about this.
Comment #37
socialnicheguru commentedWhich MR is for Drupal 9.5?
I think they both apply.
Comment #38
larowlanLeft another comment about the test, same as before - fine to ping/self RTBC
Comment #39
fjgarlin commented@SocialNicheGuru, re #37: https://git.drupalcode.org/project/drupal/-/merge_requests/3233 is the one against 9.5.x
Comment #40
fjgarlin commentedAs agreed via slack, marking it again RTBC and pinging @larowlan.
Comment #43
larowlanCommitted 13955d6 and pushed to 11.x. Thanks!
Backported to 10.1.x
9.5.x and 10.0.x are effectively in security only support now, so not backporting any further.
Thanks folks, especially @fjgarlin for the persistence here.