Problem/Motivation
#2958554: Allow creation of file entities from binary data via JSON API requests allows to upload a file to file field on an entity. However, when I create a 'file' base field on an entity, it fails at the below line:
assert(is_null($entity) || $field_definition->getTargetEntityTypeId() === $entity->getEntityTypeId() && $field_definition->getTargetBundle() === $entity->bundle());
and here is my base field definition in an entity:
$fields['user_document'] = BaseFieldDefinition::create('file')
->setLabel(t('Document'))
->setRequired(FALSE)
->setTranslatable(FALSE)
->setSetting('uri_scheme', 'private')
->setSetting('target_type', 'file')
->setCardinality(1)
->setDisplayOptions('form', [
'label' => 'above',
'type' => 'file_generic',
'weight' => 15,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'file_default',
'weight' => 15,
])
->setDisplayConfigurable('view', TRUE);
Proposed resolution
Turns out the issue is with $field_definition->getTargetBundle() as there is no bundle associated. This is handled after for other access check operation, but not in/before `assert()`
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Comments
Comment #2
vijaycs85Comment #3
alexpottThis is happening because the file field is a base field. The docs for \Drupal\Core\Field\FieldDefinitionInterface::getTargetBundle() are clear.
So the question because how can we check create access for files being attached to base fields. I think is we can't determine the bundle then checking the generic create access is all we can do.
Whilst we're at it the docs seem to be wrong here too
It's not
create access to the file will be checked.- it's create access to the entity to which the file field is attached.So I think we can improve the assertion.
Comment #4
alexpottHere's a test. While this whole class is supposed to be removed at some point I think test coverage of this specific method seems like a good idea.
Because we're adding a test with a setup we'll need 9.x and 8.x versions of this patch.
Comment #7
alexpottWhoops test in the wrong namespace.
Comment #9
alexpott#7's patch had more than I bargained for in it. Needed to rebase properly.
Comment #10
vijaycs85Thanks for the patches @alexpott. definitely better solution than
nice comment to say why we do is_null
Minor: probably
The 'no access' user has no access at all?Comment #11
alexpottTidied up the test a bit and addressed #10.2
Comment #12
wim leers#3: nice research! 👏
This is the change, and this comment is critical.
Note that this logic mimics logic from elsewhere in Drupal core because the is no standardized
FileFieldUploaderin core yet.Perhaps those other places also need to be updated to account for this?
(I'm glad we were testing our assumptions! It took over a year for somebody to set up a scenario that proved those assumptions wrong 😳 So while this assertion was perhaps annoying to update and to test for, that seems to have been valuable nonetheless.)
🤔 So … this is using non-file fields to test this particular logic.
Fascinating for sure.
It's probably okay. But the larger question is the one I raised in my previous point.
Comment #15
bbralaAdded probable related issue.
Comment #16
alexpottRe #12.1 No I don't the other instances of similar code have the same problem - for example \Drupal\file\Plugin\rest\resource\FileUploadResource::validateAndLoadFieldDefinition() copes fine when the entity doesn't have bundles and it's a base field.
Re #12.2 It seemed simpler to use available fields.
For sure #2940383: [META] Unify file upload logic of REST and JSON:API will help reduce the bugs in this area by sharing the logic amongst all three similar bits of code.
Comment #17
berdirI'm honestly unsure what exactly the purpose of this assert is. It can't be an actual access check, because assert is not enabled on production.
What if we turn it around and just do $entity->hasField($field_definition->getName())? that works for all kinds of fields.
this doesn't seem right.
That means $bundle will be NULL for a node base field. It should use $entity->bundle() if the entity type supports bundles.
Comment #18
berdirOh wait, that doesn't work because that code is only used when there is no entity. Not sure how we would get it to check on the right bundle then.
Comment #19
kim.pepperCan we split out methods for entity vs no entity? Would make it easier to read the logic.
Comment #21
bbralaThis is not the place for that imo, we could do that in another issue. But this would also mean extra methods in this temporary class. I would like it if we don't touch this too much.
Other than that, reading this thread it seems everything has been adressed, we are expanding the tests to test the
TemporaryJsonapiFileFieldUploadercode and fixing the assertion error.I'm setting this to RTBC.
Comment #22
bbralaAdding credits for reviewers.
Comment #23
larowlanFails on 9.4
Can we also get 10.x version?
Thanks
Comment #24
alexpottPatch applies to all branches and passes on all with minor fix to test data... so setting back to rtbc.
Comment #25
bbralaThanks @alexpott!
Comment #28
larowlansneaky, I like that we commented here because it makes it obvious why we're using title to check a file field access
Committed dce52d1 and pushed to 10.0.x. Thanks!
Backported to 9.4.x and 9.3.x as we have a green run and the risk of disruption here is low.