Problem/Motivation
#2872697: Stop saving an entity when it gets added to or removed from a group Introduced new logic where we directly invalidate an entity's cache tags rather than saving it whenever it gets added to or removed from a Group. For security reasons, this will not do. We also need to invalidate list cache tags, something that used to happen in the old approach where we called for an entity save.
I have filed the core issue to allow this here: #3608207: Make EntityBase::getListCacheTagsToInvalidate() public. But for now we should do the shitty workaround and directly copy the code from core, with an @todo in place saying we really should call the public core method once it becomes public. It's either that or calling postSave with $update set to TRUE manually.
Both options have downsides, but I'd rather wager no-one but me has so far extended EntityBase::getListCacheTagsToInvalidate() rather than trust that the many extensions of EntityBase::postSave() won't break things when called in isolation outside of a real save.
Steps to reproduce
N/A
Proposed resolution
Go with option 2: Copy core code and hope no-one extended it.
Remaining tasks
Carry out the work.
User interface changes
N/A
API changes
N/A
Data model changes
N/A
Issue fork group-3608210
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:
- 3608210-we-also-need
changes, plain diff MR !314
Comments
Comment #2
kristiaanvandeneyndeJust to add some more information, we actually do have this in EntityQueryAlter:
That should theoretically fix all of our problems and make this issue moot, but we still have the following core issue blocking that path: #3516034-43: Add cacheable metadata to SelectInterface and entity QueryInterface objects Especially comment 43 proves that we are far from everyone neatly consuming the query's cacheable metadata on their render objects.
Comment #4
kristiaanvandeneyndeOkay existing tests go green but I'd like to add more tests that prove this workaround fixes things and can also prove that things are still working when we remove the workaround after the core issue is fixed.
Comment #5
kristiaanvandeneyndeOkay I have added ample information to the MR now.
So in theory the following is already true:
Meaning that this issue wasn't such a big problem after all, but we still want to make sure lists built outside of a render context are also secure.
That is what this issue currently fixes until core issue #3516034: Add cacheable metadata to SelectInterface and entity QueryInterface objects lands. The downside is that we may be invalidating list cache tags we do not need to invalidate because they were covered by our group relationship list cache tags. But we cannot know that for sure, so we need to take the necessary precautions and invalidate them anyway, collateral damage or no.
I've left ample @todos in the code to get rid of our workaround as soon as core helps us out here. We should then also rewrite our query alters to no longer use the render context to push our cacheable metadata up as core should take care of that for us at that point.
Comment #6
vensires@kristiaanvandeneynde just for my better understanding:
Comment #7
kristiaanvandeneynde@vensires
Correct. This is a workaround that we can revert at any given time without breaking things.
We would be removing code at that point, as core will take care of most things for us. The only change we'd need is QueryAlterBase and its descendants no longer keeping track of cacheability themselves, but rather pass it to the underlying query.
This would also not be a breaking change, as we could change the internals of ::applyCacheability() to only then pass things to the underlying query. We'll see how the core issue evolves first, though.
The issue and MR here is a pre-emptive security fix for lists built outside of a render context. I am not aware of any such lists, but wouldn't be surprised if modules such as jsonapi, rest, etc. may end up creating such lists. Any lists created via render arrays, through views, etc. have been and will remain to be secure.
Comment #8
vensiresThank you @kristiaanvandeneynde for the very detailed explanation! I am sure it will help for future reference too!
Comment #9
kristiaanvandeneyndeOkay, so after a long internal discussion with @catch I'm going to go ahead and not commit this. The reasoning is as follows (summarized):
My concerns are theoretically valid, but practically not. All modules that generate generic listings with queries that could be altered currently have no proper means to check whether any cacheable metadata was added to said query. Therefore, even if in theory they should not, they all resort to some sort of render context trickery.
More detail below for those who are interested.
Example: jsonapi's
EntityResource::executeQueryInRenderContext()As you can see, they too realized they need to take care of cacheable metadata and linked to a core issue that is related to the one I'm linking to in this issue. And, as mentioned in the MR and the comments above, when there is a render context, Group is already secure regarding list invalidation.
So with that said, the fallout of this MR could be that we invalidate too many lists when we don't need to, all in the name of security, when there are no practical examples of lists being built outside of a render context or without the above render context trickery.
At this point it's a minor and calculated risk to move forward without this MR as the entirety of core and contrib's query handling will need to be rewritten anyway once we have core support for adding cacheable metadata directly onto queries.
So moving forward as such, but glad we at least took the time to thoroughly revisit this before shipping version 4.0.0.