EntityReferenceFormatterBase contains the logic about collecting the entities to display, including non-existing entities and autocreated entities.
FileFormatterBase currently duplicates that logic. #2403873: FileFormatterBase does not retain unsaved entities (files) fixed a bug there, but a better fix would be that FileFormatterBase extends EntityReferenceFormatterBase.
Also, this fixes some nasty bugs since currently file/image formatters:
- (major) do not follow the correct translation behavior that was added for ER fields in #2346315: Translated entity references not rendered in the entity display language
- (critical) do no check entity access
Regarding access check, discussion has shown (#32 / #35) that:
- it actually makes no sense and would just add useless overhead to check file access with core's default FileAccessControlHandler, given its internal logic
- but, if a different access handler with different logic was switched in, then not running file access would be a security hole.
Thus, what the patch fixes is the ability for the file access controller to opt-in for "have file formatters run me" if their logic require it, by implementing FileAccessFormatterControlHandlerInterface (empty marker interface)
Comment | File | Size | Author |
---|---|---|---|
#77 | file-formatter-you-stinky-orphan-2405469.77.patch | 28.39 KB | larowlan |
#77 | interdiff.txt | 3.16 KB | larowlan |
#66 | interdiff.txt | 6.92 KB | amateescu |
#66 | 2405469-splobjectstorage.patch | 25.91 KB | amateescu |
#64 | 2405469-FileFormatterBase_ERFormatterBase-64.patch | 25.21 KB | Berdir |
Comments
Comment #1
yched CreditAttribution: yched commentedFor that, EntityReferenceFormatterBase should be in Core rather than in entity_ref module.
Thus, postponed on #2404021: entity_reference formatters should be in Core
Comment #2
yched CreditAttribution: yched commented#2404021: entity_reference formatters should be in Core is in.
Initial attempt - this lets us simplify / unify quite some code down all file / image formatters.
Still @todo - would be really nice if this let us get rid of "the default image has a side effect of modifying the $items in the $entity that gets rendered". Mulling on that.
Comment #3
yched CreditAttribution: yched commentedAlso, this actually fixes a bug: currently file/image formatters do not follow the correct translation behavior that was added for ER fields in #2346315: Translated entity references not rendered in the entity display language
Comment #4
yched CreditAttribution: yched commented#2346315: Translated entity references not rendered in the entity display language was major, so I guess this one is too.
Comment #5
yched CreditAttribution: yched commented#2 will fail on 'default_image'. This should fix it.
Comment #8
yched CreditAttribution: yched commentedHeaddesk of the month.
Comment #10
yched CreditAttribution: yched commentedFails in ToolbarAdminMenuTest look like an unrelated bot fluke, test is green locally
Fails in ImageFieldDefaultImagesTest were because basing on ERFormatterBase means we now check entity access, and that seems to fail for the default image used in that test.
For now, working around this by forcing the "fake" 'access' property in the runtime-added item for the default image. Will need more thought, and #2374019: Cleanup the use of custom item properties in EntityReferenceFormatterBase intends to get rid of that property anyway.
But the fact that file/image formatters currently do not check entity access would make this critical ?
Comment #11
jibranDamn this change will affect DER.
function doc missing.
Comment #12
yched CreditAttribution: yched commented@jibran : Sure, docs still missing, and there are @todos. This is still a WIP :-)
How would DER be affected ?
Comment #13
jibranI was just being helpful please ignore my reviews when you are still working on issues. :)
Doesn't matter this is a note to self. :) I am trying to keep up with iq. We are overriding
EntityReferenceFormatterBase::prepareView()
in DynamicEntityReferenceFormatterTraitComment #14
amateescu CreditAttribution: amateescu commentedWe can override ERFormatterBase::getEntitiesToView() and manually append the fake $item/$file to the returned array, no?
#2374019: Cleanup the use of custom item properties in EntityReferenceFormatterBase will indeed remove 'access' but I think we currently agree that 'originalEntity' needs to stay (even with a different name) and the presence of this property (originalEntity) will also mean that the $item in question was checked for access.. I think.
Comment #15
yched CreditAttribution: yched commentedYup, that would be the idea. I started that in a local branch based on this patch. Not sure yet if I'll include it in the next iteration or keep it for a followup.
IMO it makes a lot of sense for the "fallback image" : the "entities to view" are derived from the $items, but do not modify the $items.
The issue is that getEntitiesToView() is fine for ER formatters, because an ERItem only contains an entity (a target_id).
But File and Image items contain other stuff ('alt', 'title'...), so the formatters use the entity but also the item that references the entity.
In the current patch, they do :
In the case of the "fallback image", we can add it as an entity in getEntitiesToView(), but the formatter will also need the associated "fake item", that we don't want to add in $items.
In my local branch, I worked around that by having ImageFormatterBase::getEntitiesToView() add $entity->_referringItem in all the returned entities. That way the formatters can call getEntitiesToView() and have both the entity and the item. Not completely ideal, but I can't think of a better solution atm.
And then I'm wondering whether it would make sense to move that $entity->_referringItem thing all the way up in ERFormatterBase::getEntitiesToView().
Comment #16
amateescu CreditAttribution: amateescu commentedThat's a very good point. So we actually need something like getItemsToView() if we want to be able to use it for file/image fields too. I think it's valid to do that refactoring in this issue, but maybe postponed on #2374019: Cleanup the use of custom item properties in EntityReferenceFormatterBase?
Comment #17
yched CreditAttribution: yched commentedgetItemsToView() : why not, not too sure yet. I considered that option as well, but:
- I wasn't completely a fan of
:-)
- In relation to #2374019: Cleanup the use of custom item properties in EntityReferenceFormatterBase : if formatters go back to working with $items / $item (those returned by getItemsToView()), we can't expect them to work with $item->originalEntity or whatever other custom property name contains the "right" entity. They should do $this->entity as usual. Hence my last patch in that issue over there, which should make that point a non-issue.
- More importantly : would we also move ERFormatterBase and the ER formatters from getEntitiesToView() to getItemsToView() ?
Feels weird to have ER formatters use getEntitiesToView() and tell field/image formatters "yeah, there is a getEntitiesToView(), but it's not for you, you guys have getItemsToView() instead" ?
Comment #18
amateescu CreditAttribution: amateescu commentedUgh, yes, getItemsToView() might be an unfortunate name.
Definitely, we have to try and get all of them at the same level on semantics.
Comment #19
larowlanShould we be checking $item->hasNewEntity() here too?
Comment #20
yched CreditAttribution: yched commented@larowlan - good question, that puzzled me too. But this is what HEAD does atm, the patch just moves this code around.
Comment #21
larowlanTagging for manual testing in office hours and possibly reviews depending on taker
Comment #22
jibranThis is NW as per #13 #14 #15 and if we can commit #2374019: Cleanup the use of custom item properties in EntityReferenceFormatterBase it would help here that is RTBC. That is not a blocker so I can't change the priority there.
Comment #23
yched CreditAttribution: yched commentedFor now, just a reroll after #2374019: Cleanup the use of custom item properties in EntityReferenceFormatterBase.
Shows that the split between prepareView() and getIdsToLoad() in ERFormatterBase is not ideal. Left a @todo, will address in a later patch.
Comment #25
yched CreditAttribution: yched commentedOf course, like mentioned in #10, this fails now that we can't hack the $entity_ref_item->acces property of the "default image" anymore :-p. Will look into fixing access checks in those tests at last.
Comment #26
catchComment #27
effulgentsia CreditAttribution: effulgentsia commentedConfirmed the criticality of this (due to the access checking part) with the branch maintainers, so tagging.
Comment #28
yched CreditAttribution: yched commentedPuts the iteration back into prepareView(), as mentioned in #23.
This will still fail because of #25 (access check on the fallback image), that's the next target.
Still running the bot to check there were no other regressions.
Comment #30
yched CreditAttribution: yched commentedWork in progress for #15 - #17, here's what I have.
Will post more explanations in a later comment :-)
Comment #32
yched CreditAttribution: yched commentedSo, funny things :
- we cannot check entity access on the fallback image (HEAD currently doesn't, but latest patch does), since core's FileAccessControlHandler grants access based on whether there exists an entity the user can access that referes to the file. But the principle of the fallback image is that it can totally happen that no entity actually refers to it.
- given the above on how FileAccessControlHandler works, it seems a bit absurd (and costly) to have image/file formatters check access on file/image entities before displaying them ? If we are displaying the field, it means the user should have view access to the parent entity, and having FileAccessControlHandler re-do that there exists such an entity is useless (and also, might break on preview I guess, since the entity that refers to the file might not be created in the db yet ?)
- but the reasoning above is only valid for the current FileAccessControlHandler - what if a different access handler is swapped in for files ?
Comment #33
larowlanRight, that is problematic. Something like file_entity would definitely swap something in.
Perhaps we can get the access handler class and see if it is FileAccessControlHandler, and if so skip the access check - if it's not - we can call ->access()?
Comment #34
yched CreditAttribution: yched commentedYes, I was thinking of something like that too.
Drawback is that we'd hardcode behavior on a specific access handler class. If you swap a similar but just slightly different class (say, a subclass that changes a minor behavior, or changes behavior for grants other than 'view'), you're out of luck.
Maybe the file access handlers should have a method for "call me in file/image formatters" ? :-/
Comment #35
larowlanSomething like this?
I think that makes sense
Comment #36
yched CreditAttribution: yched commentedCurious to know what @fago and @Berdir think about it, but this looks fine by me.
Will see how to integrate that behavior in the generic ERFormatterBase::getEntitiesToView()
Comment #37
catchOr just an extra (empty) interface rather than adding the method?
Comment #38
yched CreditAttribution: yched commentedYup, better.
Comment #39
yched CreditAttribution: yched commentedDone as per #33 and #37 :
FileFormatterBase only checks access if the file access control handler implements FileAccessFormatterControlHandlerInterface, which the default file handler does not.
I tried to add understandable explanations in that newly added FileAccessFormatterControlHandlerInterface, suggestions welcome on how to make it less verbose - or how to phrase a first line that makes sense *and* doesn't go over 80 chars :-p
This seems to make the image tests pass again, let's see if this broke something else.
(patch also adjusts a couple vars names, and uses the _referringItem pattern consistenly in all file / image formatters - more on that when we're green)
Comment #40
jibranI know this patch is WIP I am just adding remarks so that you can fix them before next reroll :)
Can we use interface here? Else i have to override this for DER.
{@inheritdoc} missing.
more then 80 chars.
We can use FileInterface::url() here.
Comment #41
googletorp CreditAttribution: googletorp commentedOther than what is mentioned above
Documentation of these methods should be added
Comment #42
googletorp CreditAttribution: googletorp commentedSolved the comment issues, since it was easy pickings.
Comment #43
yched CreditAttribution: yched commented@googletorp: The patch is still in progress, I was waiting for it to settle before filling in the docs. Thanks for stepping in - when uploading a new patch on an issue, it is really recommended to post an interdiff alongside, so that people can check you changes, and the patch author can integrate them in its local branch :-)
Will process @jibran's remarks in a later post.
Comment #44
mikey_p CreditAttribution: mikey_p commentedLatest patch is missing FileAccessFormatterControlHandlerInterface.php
Comment #45
yched CreditAttribution: yched commented@mikey_p: yep, I added it back (manually added @googletorp's changes on top of my own branch)
This attempts to fix ContentEntityBase::__set() messing with TypedData objects added as non-field properties. Will post that as a separate patch if green.
Also adjusts the docs added in #42 a bit.
Comment #46
googletorp CreditAttribution: googletorp commented@yched thanks for the info, I don't do much work on core.
I processed the last remark from jibran using File::url to generate the url, instead of file_create_url (this is what File::url does anyways).
Comment #47
googletorp CreditAttribution: googletorp commentedSomething failed in creating the interdiff, so adding it here.
Comment #48
yched CreditAttribution: yched commented@googletorp, @jibran : That change is correct in itself, but is not related to this task here, this is existing code that the patch just moves around. Plus, ImageFormatter has the same code, and should be updated as well..
--> I'll revert #46 in the next patch I roll, please open a separate issue for this.
@jibran:
Problem is, we don't have an EntityReferenceItemInterface atm. Arguably we should, since EntityReferenceItem adds a specific public hasNewEntity() method. Care to open an issue for that ?
Comment #49
yched CreditAttribution: yched commentedOpened #2426509: ContentEntityBase::__set() messes with values that happen to be TypedData for the bug in ContentEntityBase::__set() that prevents
$entity->_referringItem = $item
.Patch here still contains the fix for now, added a link.
Reverts #46, interdiff is with #45.
Comment #50
Wim LeersOnly nits. This looks ready to me :)
Needs fully qualified classnames.
So much better :)
s/with/when/
s/impleminting/implementing/
Why not
instanceof
?Comment #51
yched CreditAttribution: yched commentedThanks @Wim, will address those asap. Also, I still need to explain a bit why I went with the $entity->_referringItem approach, but it's too late tonight.
Note that this is blocked on #2426509: ContentEntityBase::__set() messes with values that happen to be TypedData though.
Comment #52
yched CreditAttribution: yched commentedre @Wim #50 :
1. Fixed
2. :-)
3. I don't get this point, the sentence looks correct to me, and the proposed replacement doesn't :-)
"[that check] might be needed if a different access control handler with (= that has a) different logic is swapped in", no ?
4. Lol, "implemint" sounds delicious, but well, fixed :-p
5. Because we're checking "is $class_name as subclass of $other_class_name", instanceof doesn't work for that
Comment #53
amateescu CreditAttribution: amateescu commentedI'm very curious about that
_referringItem
stuff :)Comment #54
jibranping @yched #2404331: Can't serialise an entity with an entity reference to an unsaved entity (remove EntityReferenceItem::$NEW_MARKER in favour of a validation constraint) needs your input. :)
Sorry about unrelated comment. :(
Comment #55
Wim LeersPoint 3 = Wimfail. I misread — sorry :)
RTBC as soon as:
_referringItem
stuff is doc'd :)Comment #56
yched CreditAttribution: yched commentedEnhanced docs a bit.
Comment #57
googletorp CreditAttribution: googletorp commentedGreat work yched, all looks good to me.
Comment #58
amateescu CreditAttribution: amateescu commentedI'll try to take a thorough look at this tonight.
Comment #59
yched CreditAttribution: yched commentedSo, about the $entity->_referringtems approach :
Sorry for making this such a mistery, just had a couple busy days with little quality time in front of my keyboard. Well, also took me a little while to actually remember the reason :-p, but it's actually pretty simple :
getEntitiesToView() is about providing an API to ERFormatter::viewElements() implementations for "just give me the entities I should display, *and* the corresponding items that reference them". That API takes care of checking access and putting the entities in the right language, so that it is not left for each formatter to figure out (with large chances of implementing it wrong or not at all).
Now, if that API primarily returned field items instead of entities (i.e if it was getItemsToView() instead of getEntitiesToView()):
- the formatters implementations would access the referenced entity with the regular "$item->entity", which doesn't give you the entity in the correct language (and we don't want to modify $item->entity to contain the entity in the right language, because we don't want to modify the containing entity being rendered - viewing an entity shouldn't modify that entity as a side effect)
- filtering out items for access check means we'd have to clone the FieldItemList (again, we don't want to modify the containing entity being displayed), which would be a slight perf impact.
So, we stick to returning an array of entities for display, and on each entity you can access the item that referenced it - that's $entity->_referringItem :-)
Comment #60
yched CreditAttribution: yched commentedSide note : an alternative could be to use SplObjectStorage to return an "array" with FieldItem object as keys and Entity objects as values, letting consumers do
foreach ($this->getEntitiesToView() as $item => $entity)
Not sure it's worth the extra CPU cost - also, we don't use SplObjectStorage that much, it could look unfamiliar / surprising.
Comment #61
amateescu CreditAttribution: amateescu commentedThanks @yched for the detailed description of this $entity->_referringtems approach. I agree that we can't make it prettier without SplObjectStorage, but I'm not sure there is any extra CPU cost involved to it, after all we're still building an array in
getEntitiesToView()
.So let's get this in to fix the critical and open a followup to investigate the alternative approach :)
One last thing though, should we add a test for the security part of this patch?
Comment #62
yched CreditAttribution: yched commentedReading http://technosophos.com/2009/05/29/set-objects-php-arrays-vs-splobjectst..., it seems there is no perf impact regarding SplObjectStorage - they're even supposed to be faster :-). Given our use case here (most likely under 100 items), it would probably not make a difference anyway.
The difference betwen the two approaches is then:
- exposing SplObjectStorage in a relatively high-level API (entity ref formatters), not really a typical pattern in Drupal so far
- the SplObjectStorage approach loses track of the delta, you need to get it from the $item, using the (totally unintuitive) getName() method from TypedData (which, for FieldItems in a FieldItemList, happens to be the delta) :
This kind of sucks, because not all formatters actually need the $item, but they all need the $delta :-)
So yeah, I can't say I'm married to it, but I'm still inclined towards the _referringItem approach at this point.
Comment #63
jibran#2426509: ContentEntityBase::__set() messes with values that happen to be TypedData is fixed now.
Comment #64
BerdirHere's a reroll without the changes in ContentEntityBase.
Comment #65
jibranIt is RTBC as per #55
/me goes to roll a fix for DER. :)
Comment #66
amateescu CreditAttribution: amateescu commentedIf anyone is curious, this is how the patch might look like with the SplObjectStorage approach. Not saying that we should go with it, I just wanted to try it out.
Comment #68
amateescu CreditAttribution: amateescu commentedApparently it doesn't work very well, so the patch in #64 is RTBC.
Comment #69
alexpottSince there is not an implementation of FileAccessFormatterControlHandlerInterface this code is untested. Plus we're obviously not running access checks on files still then - which the summary claims this fixes.
Comment #70
yched CreditAttribution: yched commented@alexpott:
Yes, this was decided in #32 / #35 :
- it actually makes no sense and would just add useless overhead to check file access with core's default FileAccessControlHandler, given its internal logic
- *but*, if a different access handler with different logic was switched in, then not running file access would be a security hole.
Thus, what the patch fixes is the ability for the file access controller to opt-in for "have file formatters run me" if their logic requires it.
(added that to the IS)
Will need to think of how to test this with a dummy file access handler class.
Comment #71
yched CreditAttribution: yched commentedAlso - we would likely need to move TaxonomyFormatterBase to EntityReferenceFormatterBase as well :-/.
It looks like it currently handles translation and access checks correctly, so that wouldn't be a bug fix, just a maintainability enhancement by avoiding one-off duplicate logic. Opened #2433513: [PP-1] TaxonomyFormatterBase should extend EntityReferenceFormatterBase
Comment #72
yched CreditAttribution: yched commentedComment #73
Wim LeersComment #74
amateescu CreditAttribution: amateescu commentedIsn't the custom access handler responsible for not regressing in security from the default one provided by core?
Comment #75
yched CreditAttribution: yched commented@amateescu : for the custom access handler to be responsible for anything, it needs to be called in the first place ;-)
The logic we're facing is :
- We don't want FileFormatters to run the default file access controller (FileAccessControlHandler), because for the specific logic in FileAccessControlHandler that would super costly and useless anyway
- But we want FileFormatters to be able to call the file access controller if it's *not* FileAccessControlHandler
So:
- It's the responsibility of the file access controller to specify that it wants to be called by FileFormatters (by implementing FileAccessFormatterControlHandlerInterface)
- It's the responsibility of FileFormatters to comply and actually call the access controller if it did the above :-)
Comment #76
larowlan/me starts on a test, will assign back later - sorry for hijacking
Comment #77
larowlanAdded tests
Comment #78
larowlanback to @yched
Comment #79
yched CreditAttribution: yched commentedThanks a lot @larowlan ! Sorry, hard to carve out time in front of my IDE lately :-/
Thus, setting back to RTBC ?
Comment #80
alexpottThis issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed ef0cb6e and pushed to 8.0.x. Thanks!