Overview
Founding when researching #3494915: Support entity-level + field-level access checking in auto-save — i.e. in `experience_builder.api.api.(layout.post|auto-save.post)`
How to reproduce
- user 1, has path edit access, user 2 does NOT have path edit access. Both have access to have other XB permissions
- user 1 uses XB with node 1 and sets path to /my-page
- user 2 uses XB with node 1
Right user 2 will get an error.
This because \Drupal\experience_builder\Controller\ApiLayoutController::get() will
return new PreviewEnvelope($this->buildPreviewRenderable($data, $entity, FALSE), $data);
The FALSE here is for $updateAutoSave
buildPreviewRenderable has
$updated_entity_form_fields = $this->converter->convert([
'layout' => $content,
// An empty model needs to be represented as \stdClass so that it is
// correctly json encoded. But we need to convert it to an array before
// we can extract it.
'model' => (array) $model,
'entity_form_fields' => $body['entity_form_fields'],
], $entity, validate: FALSE);
// Store the auto-save entry.
if ($updateAutoSave) {
$this->autoSaveManager->save($entity, [
'layout' => [$content],
// An empty model needs to be represented as \stdClass so that it is
// correctly json encoded. But we need to convert it to an array before
// we can extract it.
'model' => self::extractModelForSubtree($content, (array) $model),
// Store the updated form build ID but leave all other fields as-is.
// This allows us to re-submit the values from auto-save when we finally
// publish the entity. Some field widgets make transformations to the form
// data which cannot be repeated.
// @see \Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsWidgetBase::validateElement
'entity_form_fields' => \array_filter([
'form_build_id' => $updated_entity_form_fields['form_build_id'] ?? NULL,
]) + $body['entity_form_fields'],
]);
convert() will check the field access on but since user 2 doesn't have edit access for path and path is in the auto-save it will throw an error.
But $updated_entity_form_fields is only used inside the if block if $updateAutoSave. But convert() also will update the entity with the new values of the XB field, so this must be done. Most of that logic does live in \Drupal\experience_builder\Controller\ClientServerConversionTrait::convertClientToServer so it could be called directly if we are not auto-saving
Proposed resolution
In \Drupal\experience_builder\Controller\ApiLayoutController::buildPreviewRenderable() when calling \Drupal\experience_builder\ClientDataToEntityConverter::convert() set $client_data['entity_form_fields'] to an empty array if $updateAutoSave is false.
There is not need to update these fields we are not going to be using the return value of the convert() to call \Drupal\experience_builder\AutoSave\AutoSaveManager::save()
User interface changes
Issue fork experience_builder-3519992
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:
- 3519992-donot-convert
changes, plain diff MR !920
- 3519992-only-convert-layout
changes, plain diff MR !921
Comments
Comment #2
tedbowTurns out it is little more complicated because the entity is also update during
convert()so we don't just care about the return valueComment #4
tedbowComment #6
tedbowComment #7
tedbowComment #8
tedbowI added a test on 3519992-only-convert-layout that passes locally but fails on 0.x
Comment #10
wim leersComment #11
tedbowComment #12
tedbowUpdated the summary
Comment #13
tedbowAssigning to @larowlan for review
Comment #14
larowlanLooks good to me - thanks
Comment #15
wim leers🚢
Comment #17
wim leers