summaryrefslogtreecommitdiffstats
path: root/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
diff options
context:
space:
mode:
authorxjm2017-09-03 06:10:11 -0500
committerxjm2017-09-03 06:10:11 -0500
commit84bbc5730a26e6e7f128b2290d306c2a38c0d26b (patch)
tree2f9ce0a0adbb372103f18cafd129d16fd03d54e5 /core/modules/rest/src/Plugin/rest/resource/EntityResource.php
parentfe7caba729f7b038893b3730fc832614f7f65a17 (diff)
Revert "Issue #2824851 by Wim Leers, arshadcn, amateescu, tedbow, timmillwood, Berdir, tstoeckler: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior"
This reverts commit 3b3daa353f4c5ebd47d241aa1ac7e9af755f56f7.
Diffstat (limited to 'core/modules/rest/src/Plugin/rest/resource/EntityResource.php')
-rw-r--r--core/modules/rest/src/Plugin/rest/resource/EntityResource.php76
1 files changed, 61 insertions, 15 deletions
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 1087468..5d9849d 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -11,6 +11,8 @@ use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\TypedData\PrimitiveInterface;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Psr\Log\LoggerInterface;
@@ -200,6 +202,41 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
}
/**
+ * Gets the values from the field item list casted to the correct type.
+ *
+ * Values are casted to the correct type so we can determine whether or not
+ * something has changed. REST formats such as JSON support typed data but
+ * Drupal's database API will return values as strings. Currently, only
+ * primitive data types know how to cast their values to the correct type.
+ *
+ * @param \Drupal\Core\Field\FieldItemListInterface $field_item_list
+ * The field item list to retrieve its data from.
+ *
+ * @return mixed[][]
+ * The values from the field item list casted to the correct type. The array
+ * of values returned is a multidimensional array keyed by delta and the
+ * property name.
+ */
+ protected function getCastedValueFromFieldItemList(FieldItemListInterface $field_item_list) {
+ $value = $field_item_list->getValue();
+
+ foreach ($value as $delta => $field_item_value) {
+ /** @var \Drupal\Core\Field\FieldItemInterface $field_item */
+ $field_item = $field_item_list->get($delta);
+ $properties = $field_item->getProperties(TRUE);
+ // Foreach field value we check whether we know the underlying property.
+ // If we exists we try to cast the value.
+ foreach ($field_item_value as $property_name => $property_value) {
+ if (isset($properties[$property_name]) && ($property = $field_item->get($property_name)) && $property instanceof PrimitiveInterface) {
+ $value[$delta][$property_name] = $property->getCastedValue();
+ }
+ }
+ }
+
+ return $value;
+ }
+
+ /**
* Responds to entity PATCH requests.
*
* @param \Drupal\Core\Entity\EntityInterface $original_entity
@@ -225,26 +262,35 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'update'));
}
- // Overwrite the received fields.
+ // Overwrite the received properties.
+ $entity_keys = $entity->getEntityType()->getKeys();
foreach ($entity->_restSubmittedFields as $field_name) {
$field = $entity->get($field_name);
- $original_field = $original_entity->get($field_name);
-
- // If the user has access to view the field, we need to check update
- // access regardless of the field value to avoid information disclosure.
- // (Otherwise the user may try PATCHing with value after value, until they
- // send the current value for the field, and then they won't get a 403
- // response anymore, which indicates that the value they sent in the PATCH
- // request body matches the current value.)
- if (!$original_field->access('view')) {
- if (!$original_field->access('edit')) {
+
+ // 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)) {
+ // @todo Work around the wrong assumption that entity keys need special
+ // treatment, when only read-only fields need it.
+ // This will be fixed in https://www.drupal.org/node/2824851.
+ if ($entity->getEntityTypeId() == 'comment' && $field_name == 'status' && !$original_entity->get($field_name)->access('edit')) {
throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
}
+
+ // Unchanged values for entity keys don't need access checking.
+ if ($this->getCastedValueFromFieldItemList($original_entity->get($field_name)) === $this->getCastedValueFromFieldItemList($entity->get($field_name))) {
+ 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;
+ }
}
- // Check access for all received fields, but only if they are being
- // changed. The bundle of an entity, for example, must be provided for
- // denormalization to succeed, but it may not be changed.
- elseif (!$original_field->equals($field) && !$original_field->access('edit')) {
+
+ if (!$original_entity->get($field_name)->access('edit')) {
throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
}
$original_entity->set($field_name, $field->getValue());