core/lib/Drupal/Core/Field/FieldItemList.php | 7 +++++++ .../src/Plugin/rest/resource/EntityResource.php | 24 ++++++++-------------- .../src/Unit/UserAccessControlHandlerTest.php | 9 ++++++++ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php index 97dd493..55b0174 100644 --- a/core/lib/Drupal/Core/Field/FieldItemList.php +++ b/core/lib/Drupal/Core/Field/FieldItemList.php @@ -169,6 +169,13 @@ public function access($operation = 'view', AccountInterface $account = NULL, $r * {@inheritdoc} */ public function defaultAccess($operation = 'view', AccountInterface $account = NULL) { + if ($operation === 'edit') { + $field_definition = $this->getFieldDefinition(); + $access = $field_definition->isReadOnly() ? AccessResult::forbidden() : AccessResult::allowed(); + $access->addCacheableDependency($field_definition); + return $access; + } + // Grant access per default. return AccessResult::allowed(); } diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php index 55ce64a..447d8de 100644 --- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php @@ -208,23 +208,15 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity foreach ($entity->_restSubmittedFields as $field_name) { $field = $entity->get($field_name); - // Entity key fields need special treatment: together they uniquely - // identify the entity. Therefore it does not make sense to modify any of - // them. However, rather than throwing an error, we just ignore them as - // long as their specified values match their current values. - if (in_array($field_name, $entity_keys, TRUE)) { - // Unchanged values for entity keys don't need access checking. - if ($original_entity->get($field_name)->getValue() === $entity->get($field_name)->getValue()) { - continue; - } - // It is not possible to set the language to NULL as it is automatically - // re-initialized. As it must not be empty, skip it if it is. - elseif (isset($entity_keys['langcode']) && $field_name === $entity_keys['langcode'] && $field->isEmpty()) { - continue; - } + // Allow sending read-only fields, as long as their value is unchanged. + // This should not be necessary, but due to a design flaw in the Entity + // Field API: the validation logic can only use the pre-update values, not + // the post-update values. + // @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess() + if ($field->getFieldDefinition()->isReadOnly() && $original_entity->get($field_name)->getValue() === $entity->get($field_name)->getValue()) { + continue; } - - if (!$original_entity->get($field_name)->access('edit')) { + elseif (!$original_entity->get($field_name)->access('edit')) { throw new AccessDeniedHttpException("Access denied on updating field '$field_name'."); } $original_entity->set($field_name, $field->getValue()); diff --git a/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php b/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php index 75e6ccb..3a16972 100644 --- a/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php +++ b/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php @@ -122,6 +122,15 @@ public function assertFieldAccess($field, $viewer, $target, $view, $edit) { $field_definition->expects($this->any()) ->method('getName') ->will($this->returnValue($field)); + $field_definition->expects($this->any()) + ->method('getCacheContexts') + ->will($this->returnValue([])); + $field_definition->expects($this->any()) + ->method('getCacheTags') + ->will($this->returnValue([])); + $field_definition->expects($this->any()) + ->method('getCacheMaxAge') + ->will($this->returnValue([])); $this->items ->expects($this->any())