diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 6094bdf..8cd767c 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -1108,7 +1108,9 @@ public function createDuplicate() {
 
     $duplicate = clone $this;
     $entity_type = $this->getEntityType();
-    $duplicate->{$entity_type->getKey('id')}->value = NULL;
+    if ($entity_type->hasKey('id')) {
+      $duplicate->{$entity_type->getKey('id')}->value = NULL;
+    }
     $duplicate->enforceIsNew();
 
     // Check if the entity type supports UUIDs and generate a new one if so.
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php b/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php
index c67c2ed..ff8da23 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php
@@ -316,13 +316,17 @@ public function fieldAccess($operation, FieldDefinitionInterface $field_definiti
     $default = $items ? $items->defaultAccess($operation, $account) : AccessResult::allowed();
 
     // Explicitly disallow changing the entity ID and entity UUID.
-    if ($operation === 'edit') {
+    $entity = $items ? $items->getEntity() : NULL;
+    if ($operation === 'edit' && $entity) {
       if ($field_definition->getName() === $this->entityType->getKey('id')) {
-        return $return_as_object ? AccessResult::forbidden('The entity ID cannot be changed') : FALSE;
+        // String IDs can be set when creating the entity.
+        if (!($entity->isNew() && $field_definition->getType() === 'string')) {
+          return $return_as_object ? AccessResult::forbidden('The entity ID cannot be changed')->addCacheableDependency($entity) : FALSE;
+        }
       }
       elseif ($field_definition->getName() === $this->entityType->getKey('uuid')) {
         // UUIDs can be set when creating an entity.
-        if ($items && ($entity = $items->getEntity()) && !$entity->isNew()) {
+        if (!$entity->isNew()) {
           return $return_as_object ? AccessResult::forbidden('The entity UUID cannot be changed')->addCacheableDependency($entity) : FALSE;
         }
       }
diff --git a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php
index 1051ce4..6a2dca2 100644
--- a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php
+++ b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php
@@ -99,8 +99,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       static::$fieldName => [
         [
           'value' => static::$dateString,
diff --git a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php
index ffce48c..729b705 100644
--- a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php
+++ b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php
@@ -99,8 +99,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       static::$fieldName => [
         [
           'value' => static::$dateString,
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php
index d4ee9ab..b2c2998 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php
@@ -53,8 +53,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/block_content/basic',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php b/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
index 3deb0ea..ef4cfcf 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
@@ -98,8 +98,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/comment/comment',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonAnonTest.php
index 06f72bf..38ccb0f 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonAnonTest.php
@@ -78,8 +78,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/entity_test/entity_test',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
index 819a125..0dac2ac 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
@@ -73,8 +73,8 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       'field_test_internal' => [
         [
           'value' => 'This value shall not be internal!',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php
index f95ea5c..7ae2846 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php
@@ -80,8 +80,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/entity_test_label/entity_test_label',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Feed/FeedHalJsonTestBase.php b/core/modules/hal/tests/src/Functional/EntityResource/Feed/FeedHalJsonTestBase.php
index 4318132..a2adf9f 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Feed/FeedHalJsonTestBase.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Feed/FeedHalJsonTestBase.php
@@ -47,8 +47,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/aggregator_feed/aggregator_feed'
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
index f3036a9..14f0b00 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
@@ -85,8 +85,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/file/file',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php b/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php
index 24157e5..a062b9e 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php
@@ -77,8 +77,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/aggregator_item/aggregator_item',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
index c9ee773..d2e992b 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
@@ -154,8 +154,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/media/camelids',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentHalJsonAnonTest.php
index c4c8d94..e0a2dba 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentHalJsonAnonTest.php
@@ -53,8 +53,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/menu_link_content/menu_link_content',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Message/MessageHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Message/MessageHalJsonAnonTest.php
index a9b3ae1..4bb7b6c 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Message/MessageHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Message/MessageHalJsonAnonTest.php
@@ -32,8 +32,8 @@ class MessageHalJsonAnonTest extends MessageResourceTestBase {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/contact_message/camelids',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
index 2de6539..c59be26 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
@@ -98,8 +98,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/node/camelids',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php
index c9e8dc0..b55a68f 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php
@@ -53,8 +53,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/shortcut/default',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
index 73a1549..565eb52 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
@@ -52,8 +52,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/User/UserHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/User/UserHalJsonAnonTest.php
index 7398f28..2e6cbde 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/User/UserHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/User/UserHalJsonAnonTest.php
@@ -52,8 +52,8 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       '_links' => [
         'type' => [
           'href' => $this->baseUrl . '/rest/type/user/user',
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php
index 76aff4b..7f00bcc 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php
@@ -82,7 +82,7 @@ protected function getExpectedCacheContexts() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php
index 1f4b03a..5f8b9e7 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php
@@ -84,7 +84,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php
index 13a4568..f537fb1 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php
@@ -104,7 +104,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
index 5766817..a50f161 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
@@ -146,7 +146,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'type' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/BlockContentType/BlockContentTypeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/BlockContentType/BlockContentTypeResourceTestBase.php
index f96f40e..09f4806 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/BlockContentType/BlockContentTypeResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/BlockContentType/BlockContentTypeResourceTestBase.php
@@ -64,7 +64,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
index d309854..a628160 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
@@ -207,7 +207,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'comment_type' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/CommentType/CommentTypeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/CommentType/CommentTypeResourceTestBase.php
index 9d88211..d6897fe 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/CommentType/CommentTypeResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/CommentType/CommentTypeResourceTestBase.php
@@ -70,7 +70,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ConfigTest/ConfigTestResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ConfigTest/ConfigTestResourceTestBase.php
index 9fe073b..fc814f7 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/ConfigTest/ConfigTestResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ConfigTest/ConfigTestResourceTestBase.php
@@ -66,7 +66,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ConfigurableLanguage/ConfigurableLanguageResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ConfigurableLanguage/ConfigurableLanguageResourceTestBase.php
index cf5be74..a589056 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/ConfigurableLanguage/ConfigurableLanguageResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ConfigurableLanguage/ConfigurableLanguageResourceTestBase.php
@@ -71,7 +71,7 @@ protected function getExpectedCacheContexts() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ContactForm/ContactFormResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ContactForm/ContactFormResourceTestBase.php
index 51e297a..5cf3cb5 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/ContactForm/ContactFormResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ContactForm/ContactFormResourceTestBase.php
@@ -86,7 +86,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ContentLanguageSettings/ContentLanguageSettingsResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ContentLanguageSettings/ContentLanguageSettingsResourceTestBase.php
index a8b3f8c..77c36a4 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/ContentLanguageSettings/ContentLanguageSettingsResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ContentLanguageSettings/ContentLanguageSettingsResourceTestBase.php
@@ -75,7 +75,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/DateFormat/DateFormatResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/DateFormat/DateFormatResourceTestBase.php
index 8fce0fe..80031ee 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/DateFormat/DateFormatResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/DateFormat/DateFormatResourceTestBase.php
@@ -69,7 +69,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php
index 4eb4a6e..24dba9d 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php
@@ -158,7 +158,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
index cc4cf58..a4a91ed 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
@@ -134,7 +134,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormMode/EntityFormModeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormMode/EntityFormModeResourceTestBase.php
index a9035e0..a8bd421 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormMode/EntityFormModeResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormMode/EntityFormModeResourceTestBase.php
@@ -67,7 +67,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 4c7947d..d226b4e 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -126,6 +126,13 @@
   protected $entity;
 
   /**
+   * Another entity of the same type used for testing.
+   *
+   * @var \Drupal\Core\Entity\EntityInterface
+   */
+  protected $anotherEntity;
+
+  /**
    * The entity storage.
    *
    * @var \Drupal\Core\Entity\EntityStorageInterface
@@ -222,6 +229,22 @@ public function setUp() {
   abstract protected function createEntity();
 
   /**
+   * Creates another entity to be tested.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   Another entity based on $this->entity.
+   */
+  protected function createAnotherEntity() {
+    $entity = $this->entity->createDuplicate();
+    $label_key = $entity->getEntityType()->getKey('label');
+    if ($label_key) {
+      $entity->set($label_key, $entity->label() . '_dupe');
+    }
+    $entity->save();
+    return $entity;
+  }
+
+  /**
    * Returns the expected normalization of the entity.
    *
    * @see ::createEntity()
@@ -235,9 +258,15 @@ public function setUp() {
    *
    * @see ::testPost
    *
+   * @param int|null $which
+   *   An integer indicating which POST entity to generate. (Some entity types
+   *   have a required field that must contain a unique value. To test certain
+   *   edge cases, multiple distinct entities need to be created. This integer
+   *   then indicates which set of entity field values to create.)
+   *
    * @return array
    */
-  abstract protected function getNormalizedPostEntity();
+  abstract protected function getNormalizedPostEntity($which = NULL);
 
   /**
    * Returns the normalized PATCH entity.
@@ -712,7 +741,7 @@ public function testPost() {
     $unparseable_request_body = '!{>}<';
     $parseable_valid_request_body   = $this->serializer->encode($this->getNormalizedPostEntity(), static::$format);
     $parseable_valid_request_body_2 = $this->serializer->encode($this->getNormalizedPostEntity(), static::$format);
-    $parseable_invalid_request_body   = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPostEntity()), static::$format);
+    $parseable_invalid_request_body = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPostEntity(), 'label'), static::$format);
     $parseable_invalid_request_body_2 = $this->serializer->encode($this->getNormalizedPostEntity() + ['uuid' => [$this->randomMachineName(129)]], static::$format);
     $parseable_invalid_request_body_3 = $this->serializer->encode($this->getNormalizedPostEntity() + ['field_rest_test' => [['value' => $this->randomString()]]], static::$format);
 
@@ -871,8 +900,9 @@ public function testPost() {
     }
     $response = $this->request('POST', $url, $request_options);
     $this->assertResourceResponse(201, FALSE, $response);
+    $created_entity = $this->entityStorage->load(static::$secondCreatedEntityId);
     if ($has_canonical_url) {
-      $location = $this->entityStorage->load(static::$secondCreatedEntityId)->toUrl('canonical')->setAbsolute(TRUE)->toString();
+      $location = $created_entity->toUrl('canonical')->setAbsolute(TRUE)->toString();
       $this->assertSame([$location], $response->getHeader('Location'));
     }
     else {
@@ -880,6 +910,32 @@ public function testPost() {
     }
     $this->assertFalse($response->hasHeader('X-Drupal-Cache'));
 
+    if ($this->entity->getEntityType()->getStorageClass() !== ContentEntityNullStorage::class && $this->entity->getEntityType()->hasKey('uuid')) {
+      // 500 when creating an entity with a duplicate UUID.
+      $normalized_entity = $this->getNormalizedPostEntity(2);
+      $normalized_entity[$created_entity->getEntityType()->getKey('uuid')] = [['value' => $created_entity->uuid()]];
+      $normalized_entity[$label_field] = [['value' => $this->randomMachineName()]];
+      $request_options[RequestOptions::BODY] = $this->serializer->encode($normalized_entity, static::$format);
+
+      $response = $this->request('POST', $url, $request_options);
+      $this->assertSame(500, $response->getStatusCode());
+      $this->assertContains('Internal Server Error', (string) $response->getBody());
+
+      // 201 when successfully creating an entity with a new UUID.
+      $normalized_entity = $this->getNormalizedPostEntity(2);
+      $new_uuid = \Drupal::service('uuid')->generate();
+      $normalized_entity[$created_entity->getEntityType()->getKey('uuid')] = [['value' => $new_uuid]];
+      $normalized_entity[$label_field] = [['value' => $this->randomMachineName()]];
+      $request_options[RequestOptions::BODY] = $this->serializer->encode($normalized_entity, static::$format);
+
+      $response = $this->request('POST', $url, $request_options);
+      $this->assertResourceResponse(201, FALSE, $response);
+      $entities = $this->entityStorage->loadByProperties([$created_entity->getEntityType()->getKey('uuid') => $new_uuid]);
+      $new_entity = reset($entities);
+      $this->assertNotNull($new_entity);
+      $new_entity->delete();
+    }
+
     // BC: old default POST URLs have their path updated by the inbound path
     // processor \Drupal\rest\PathProcessor\PathProcessorEntityResourceBC to the
     // new URL, which is derived from the 'create' link template if an entity
@@ -902,6 +958,9 @@ public function testPatch() {
       return;
     }
 
+    // Patch testing requires that another entity of the same type exists.
+    $this->anotherEntity = $this->createAnotherEntity();
+
     $this->initAuthentication();
     $has_canonical_url = $this->entity->hasLinkTemplate('canonical');
 
@@ -909,7 +968,7 @@ public function testPatch() {
     $unparseable_request_body = '!{>}<';
     $parseable_valid_request_body   = $this->serializer->encode($this->getNormalizedPatchEntity(), static::$format);
     $parseable_valid_request_body_2 = $this->serializer->encode($this->getNormalizedPatchEntity(), static::$format);
-    $parseable_invalid_request_body   = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPatchEntity()), static::$format);
+    $parseable_invalid_request_body   = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPatchEntity(), 'label'), static::$format);
     $parseable_invalid_request_body_2 = $this->serializer->encode($this->getNormalizedPatchEntity() + ['field_rest_test' => [['value' => $this->randomString()]]], static::$format);
     // The 'field_rest_test' field does not allow 'view' access, so does not end
     // up in the normalization. Even when we explicitly add it the normalization
@@ -1006,6 +1065,18 @@ public function testPatch() {
     $response = $this->request('PATCH', $url, $request_options);
     $this->assertResourceErrorResponse(403, "Access denied on updating field 'field_rest_test'.", $response);
 
+    // DX: 403 when entity trying to update an entity's ID field.
+    $request_options[RequestOptions::BODY] = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPatchEntity(), 'id'), static::$format);;
+    $response = $this->request('PATCH', $url, $request_options);
+    $this->assertResourceErrorResponse(403, "Access denied on updating field '{$this->entity->getEntityType()->getKey('id')}'.", $response);
+
+    if ($this->entity->getEntityType()->hasKey('uuid')) {
+      // DX: 403 when entity trying to update an entity's UUID field.
+      $request_options[RequestOptions::BODY] = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPatchEntity(), 'uuid'), static::$format);;
+      $response = $this->request('PATCH', $url, $request_options);
+      $this->assertResourceErrorResponse(403, "Access denied on updating field '{$this->entity->getEntityType()->getKey('uuid')}'.", $response);
+    }
+
     $request_options[RequestOptions::BODY] = $parseable_invalid_request_body_3;
 
     // DX: 403 when entity contains field without 'edit' nor 'view' access, even
@@ -1308,15 +1379,26 @@ protected static function getModifiedEntityForPatchTesting(EntityInterface $enti
    *
    * @param array $normalization
    *   An entity normalization.
+   * @param string $entity_key
+   *   The entity key whose normalization to make invalid.
    *
    * @return array
    *   The updated entity normalization, now invalid.
    */
-  protected function makeNormalizationInvalid(array $normalization) {
-    // Add a second label to this entity to make it invalid.
-    $label_field = $this->entity->getEntityType()->hasKey('label') ? $this->entity->getEntityType()->getKey('label') : static::$labelFieldName;
-    $normalization[$label_field][1]['value'] = 'Second Title';
-
+  protected function makeNormalizationInvalid(array $normalization, $entity_key) {
+    switch ($entity_key) {
+      case 'label':
+        // Add a second label to this entity to make it invalid.
+        $label_field = $this->entity->getEntityType()->hasKey('label') ? $this->entity->getEntityType()->getKey('label') : static::$labelFieldName;
+        $normalization[$label_field][1]['value'] = 'Second Title';
+        break;
+      case 'id':
+        $normalization[$this->entity->getEntityType()->getKey('id')][0]['value'] = $this->anotherEntity->id();
+        break;
+      case 'uuid':
+        $normalization[$this->entity->getEntityType()->getKey('uuid')][0]['value'] = $this->anotherEntity->uuid();
+        break;
+    }
     return $normalization;
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
index 1944718..2f3d254 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
@@ -75,8 +75,8 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    return parent::getNormalizedPostEntity() + [
+  protected function getNormalizedPostEntity($which = NULL) {
+    return parent::getNormalizedPostEntity($which) + [
       'field_test_internal' => [
         [
           'value' => 'This value shall not be internal!',
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php
index d14ec38..c40b46d 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php
@@ -125,7 +125,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'type' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
index cd8654d..66bc23b 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
@@ -101,8 +101,8 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
-    $post_entity = parent::getNormalizedPostEntity();
+  protected function getNormalizedPostEntity($which = NULL) {
+    $post_entity = parent::getNormalizedPostEntity($which);
     $post_entity['field_test_text'] = [
       [
         'value' => 'Llamas are awesome.',
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleResourceTestBase.php
index f43d877..6ccc0ef 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleResourceTestBase.php
@@ -69,7 +69,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelResourceTestBase.php
index 2257d6c..c5aea4d 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelResourceTestBase.php
@@ -115,7 +115,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'type' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
index ccc3aad..e7963de 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
@@ -91,7 +91,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewMode/EntityViewModeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewMode/EntityViewModeResourceTestBase.php
index 28cb9b0..7e5ad40 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewMode/EntityViewModeResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewMode/EntityViewModeResourceTestBase.php
@@ -67,7 +67,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Feed/FeedResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Feed/FeedResourceTestBase.php
index 1e79395..d0b8b31 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Feed/FeedResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Feed/FeedResourceTestBase.php
@@ -3,10 +3,10 @@
 namespace Drupal\Tests\rest\Functional\EntityResource\Feed;
 
 use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
-use Drupal\Tests\rest\Functional\EntityResource\EntityTest\EntityTestResourceTestBase;
+use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
 use Drupal\aggregator\Entity\Feed;
 
-abstract class FeedResourceTestBase extends EntityTestResourceTestBase {
+abstract class FeedResourceTestBase extends EntityResourceTestBase {
 
   use BcTimestampNormalizerUnixTestTrait;
 
@@ -23,6 +23,11 @@
   /**
    * {@inheritdoc}
    */
+  protected static $patchProtectedFieldNames = [];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUpAuthorization($method) {
     switch ($method) {
       case 'GET':
@@ -134,7 +139,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'title' => [
         [
@@ -143,7 +148,7 @@ protected function getNormalizedPostEntity() {
       ],
       'url' => [
         [
-          'value' => 'http://example.com/feed'
+          'value' => 'http://example.com/feed' . $which
         ]
       ],
       'refresh' => [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php
index 05c05f4..a586a5c 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php
@@ -92,7 +92,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php
index aafc253..2b73c70 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php
@@ -71,7 +71,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
index 1fa5a38..c23377b 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
@@ -169,7 +169,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'uid' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php
index 4d65e00..d48a489 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php
@@ -81,7 +81,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ImageStyle/ImageStyleResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ImageStyle/ImageStyleResourceTestBase.php
index ccd68d9..7bee2e3 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/ImageStyle/ImageStyleResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ImageStyle/ImageStyleResourceTestBase.php
@@ -95,7 +95,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Item/ItemResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Item/ItemResourceTestBase.php
index f217b97..0cdb593 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Item/ItemResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Item/ItemResourceTestBase.php
@@ -81,6 +81,20 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
+  protected function createAnotherEntity() {
+    $entity = $this->entity->createDuplicate();
+    $entity->setLink('https://www.exmaple.org/');
+    $label_key = $entity->getEntityType()->getKey('label');
+    if ($label_key) {
+      $entity->set($label_key, $entity->label() . '_dupe');
+    }
+    $entity->save();
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   protected function getExpectedNormalizedEntity() {
     $feed = Feed::load($this->entity->getFeedId());
 
@@ -125,7 +139,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'fid' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php
index 1875d00..b357873 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php
@@ -218,7 +218,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'bundle' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/MediaType/MediaTypeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/MediaType/MediaTypeResourceTestBase.php
index ac72737..3ca3826 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/MediaType/MediaTypeResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/MediaType/MediaTypeResourceTestBase.php
@@ -71,7 +71,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Menu/MenuResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Menu/MenuResourceTestBase.php
index dd7f552..970df90 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Menu/MenuResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Menu/MenuResourceTestBase.php
@@ -62,7 +62,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentResourceTestBase.php
index 0b1f967..ae3fde8 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentResourceTestBase.php
@@ -71,7 +71,7 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'title' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Message/MessageResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Message/MessageResourceTestBase.php
index 3f1cab9..0d951a2 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Message/MessageResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Message/MessageResourceTestBase.php
@@ -70,7 +70,7 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'subject' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php
index bf0ba7a..7384d10 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php
@@ -187,7 +187,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'type' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/NodeType/NodeTypeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/NodeType/NodeTypeResourceTestBase.php
index c374bfb..7c911b6 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/NodeType/NodeTypeResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/NodeType/NodeTypeResourceTestBase.php
@@ -72,7 +72,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/RdfMapping/RdfMappingResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/RdfMapping/RdfMappingResourceTestBase.php
index c826be0..a872ec9 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/RdfMapping/RdfMappingResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/RdfMapping/RdfMappingResourceTestBase.php
@@ -109,7 +109,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ResponsiveImageStyle/ResponsiveImageStyleResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ResponsiveImageStyle/ResponsiveImageStyleResourceTestBase.php
index 4221314..1165e89 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/ResponsiveImageStyle/ResponsiveImageStyleResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ResponsiveImageStyle/ResponsiveImageStyleResourceTestBase.php
@@ -115,7 +115,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/RestResourceConfig/RestResourceConfigResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/RestResourceConfig/RestResourceConfigResourceTestBase.php
index a19e2ef..712d5e1 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/RestResourceConfig/RestResourceConfigResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/RestResourceConfig/RestResourceConfigResourceTestBase.php
@@ -87,7 +87,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleResourceTestBase.php
index ee719c4..95b2d56 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleResourceTestBase.php
@@ -62,7 +62,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php
index eca9ca8..7bec14d 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/SearchPage/SearchPageResourceTestBase.php
@@ -78,7 +78,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutResourceTestBase.php
index 36be5b5..fdd178f 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutResourceTestBase.php
@@ -120,7 +120,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'title' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/ShortcutSet/ShortcutSetResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/ShortcutSet/ShortcutSetResourceTestBase.php
index 97ae1a6..5b5f9ff 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/ShortcutSet/ShortcutSetResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/ShortcutSet/ShortcutSetResourceTestBase.php
@@ -81,7 +81,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
index 85a4b1a..35b1e4b 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
@@ -146,7 +146,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'vid' => [
         [
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Tour/TourResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Tour/TourResourceTestBase.php
index 294e3ee..b6fce4c 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Tour/TourResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Tour/TourResourceTestBase.php
@@ -96,7 +96,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php
index d25bf2f..59fbe0f 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php
@@ -82,6 +82,17 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
+  protected function createAnotherEntity() {
+    /** @var \Drupal\user\UserInterface $user */
+    $user = $this->entity->createDuplicate();
+    $user->setUsername($user->label() . '_dupe');
+    $user->save();
+    return $user;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   protected function getExpectedNormalizedEntity() {
     return [
       'uid' => [
@@ -117,7 +128,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return [
       'name' => [
         [
@@ -244,6 +255,49 @@ protected function assertRpcLogin($username, $password) {
   }
 
   /**
+   * Tests PATCHing security-sensitive base fields to change other users.
+   */
+  public function testPatchSecurityOtherUser() {
+    // The anonymous user is never allowed to modify other users.
+    if (!static::$auth) {
+      $this->markTestSkipped();
+    }
+
+    $this->initAuthentication();
+    $this->provisionEntityResource();
+
+    /** @var \Drupal\user\UserInterface $user */
+    $user = $this->account;
+    $original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['changed' => TRUE]);
+
+    // Since this test must be performed by the user that is being modified,
+    // we cannot use $this->getUrl().
+    $url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
+    $request_options = [
+      RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
+    ];
+    $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
+
+    $normalization = $original_normalization;
+    $normalization['mail'] = [['value' => 'new-email@example.com']];
+    $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+    // Try changing user 1's email.
+    $user1 = [
+      'mail' => [['value' => 'another_email_address@example.com']],
+      'uid' => [['value' => 1]],
+      'name' => [['value' => 'another_user_name']],
+      'pass' => [['existing' => $this->account->passRaw]],
+      'uuid' => [['value' => '2e9403a4-d8af-4096-a116-624710140be0']],
+    ] + $original_normalization;
+    $request_options[RequestOptions::BODY] = $this->serializer->encode($user1, static::$format);
+    $response = $this->request('PATCH', $url, $request_options);
+    // Ensure the email address has not changed.
+    $this->assertEquals('admin@example.com', $this->entityStorage->loadUnchanged(1)->getEmail());
+    $this->assertResourceErrorResponse(403, "Access denied on updating field 'uid'.", $response);
+  }
+
+  /**
    * {@inheritdoc}
    */
   protected function getExpectedUnauthorizedAccessMessage($method) {
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlBasicAuthTest.php b/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlBasicAuthTest.php
index dbf74b1..2281d33 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlBasicAuthTest.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlBasicAuthTest.php
@@ -41,4 +41,12 @@ public function testPatchDxForSecuritySensitiveBaseFields() {
     $this->markTestSkipped();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function testPatchSecurityOtherUser() {
+    // Deserialization of the XML format is not supported.
+    $this->markTestSkipped();
+  }
+
 }
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlCookieTest.php b/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlCookieTest.php
index d1ef16e..3e01ce0 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlCookieTest.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlCookieTest.php
@@ -36,4 +36,12 @@ public function testPatchDxForSecuritySensitiveBaseFields() {
     $this->markTestSkipped();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function testPatchSecurityOtherUser() {
+    // Deserialization of the XML format is not supported.
+    $this->markTestSkipped();
+  }
+
 }
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php
index 26ec7bb..9ebbc1f 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php
@@ -83,7 +83,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Vocabulary/VocabularyResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Vocabulary/VocabularyResourceTestBase.php
index 5d03512..efb97e1 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Vocabulary/VocabularyResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Vocabulary/VocabularyResourceTestBase.php
@@ -62,7 +62,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/modules/workflows/tests/src/Functional/Rest/WorkflowResourceTestBase.php b/core/modules/workflows/tests/src/Functional/Rest/WorkflowResourceTestBase.php
index ce796a4..bb08021 100644
--- a/core/modules/workflows/tests/src/Functional/Rest/WorkflowResourceTestBase.php
+++ b/core/modules/workflows/tests/src/Functional/Rest/WorkflowResourceTestBase.php
@@ -100,7 +100,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     // @todo Update in https://www.drupal.org/node/2300677.
   }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityAccessControlHandlerTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityAccessControlHandlerTest.php
index b3af27f..8545efe 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityAccessControlHandlerTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityAccessControlHandlerTest.php
@@ -8,6 +8,7 @@
 use Drupal\Core\Entity\EntityAccessControlHandler;
 use Drupal\Core\Session\AnonymousUserSession;
 use Drupal\entity_test\Entity\EntityTest;
+use Drupal\entity_test\Entity\EntityTestStringId;
 use Drupal\entity_test\Entity\EntityTestDefaultAccess;
 use Drupal\entity_test\Entity\EntityTestNoUuid;
 use Drupal\entity_test\Entity\EntityTestLabel;
@@ -18,6 +19,7 @@
 /**
  * Tests the entity access control handler.
  *
+ * @coversDefaultClass \Drupal\Core\Entity\EntityAccessControlHandler
  * @group Entity
  */
 class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
@@ -30,6 +32,7 @@ public function setUp() {
 
     $this->installEntitySchema('entity_test_no_uuid');
     $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_string_id');
   }
 
   /**
@@ -293,4 +296,73 @@ public function testHooks() {
     $this->assertEqual($state->get('entity_test_entity_test_access'), TRUE);
   }
 
+  /**
+   * Tests the default access handling for the ID and UUID fields.
+   *
+   * @covers ::fieldAccess
+   * @dataProvider providerTestFieldAccess
+   */
+  public function testFieldAccess($entity_class, array $entity_create_values, $expected_id_create_access) {
+    // Set up a non-admin user that is allowed to create and update test
+    // entities.
+    \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['administer entity_test content']));
+
+    // Create the entity to test field access with.
+    $entity = $entity_class::create($entity_create_values);
+
+    // On newly-created entities, field access must allow setting the UUID
+    // field.
+    $this->assertTrue($entity->get('uuid')->access('edit'));
+    $this->assertTrue($entity->get('uuid')->access('edit', NULL, TRUE)->isAllowed());
+    // On newly-created entities, field access will not allow setting the ID
+    // field if the ID is of type serial. It will allow access if it is of type
+    // string.
+    $this->assertEquals($expected_id_create_access, $entity->get('id')->access('edit'));
+    $this->assertEquals($expected_id_create_access, $entity->get('id')->access('edit', NULL, TRUE)->isAllowed());
+
+    // Save the entity and check that we can not update the ID or UUID fields
+    // anymore.
+    $entity->save();
+
+    // If the ID has been set as part of the create ensure it has been set
+    // correctly.
+    if (isset($entity_create_values['id'])) {
+      $this->assertSame($entity_create_values['id'], $entity->id());
+    }
+    // The UUID is hard-coded by the data provider.
+    $this->assertSame('60e3a179-79ed-4653-ad52-5e614c8e8fbe', $entity->uuid());
+    $this->assertFalse($entity->get('uuid')->access('edit'));
+    $access_result = $entity->get('uuid')->access('edit', NULL, TRUE);
+    $this->assertTrue($access_result->isForbidden());
+    $this->assertEquals('The entity UUID cannot be changed', $access_result->getReason());
+
+    // Ensure the ID is still not allowed to be edited.
+    $this->assertFalse($entity->get('id')->access('edit'));
+    $access_result = $entity->get('id')->access('edit', NULL, TRUE);
+    $this->assertTrue($access_result->isForbidden());
+    $this->assertEquals('The entity ID cannot be changed', $access_result->getReason());
+  }
+
+  public function providerTestFieldAccess() {
+    return [
+      'serial ID entity' => [
+        EntityTest::class,
+        [
+          'name' => 'A test entity',
+          'uuid' => '60e3a179-79ed-4653-ad52-5e614c8e8fbe',
+        ],
+        FALSE
+      ],
+      'string ID entity' => [
+        EntityTestStringId::class,
+        [
+          'id' => 'a_test_entity',
+          'name' => 'A test entity',
+          'uuid' => '60e3a179-79ed-4653-ad52-5e614c8e8fbe',
+        ],
+        TRUE
+      ],
+    ];
+  }
+
 }
