 core/lib/Drupal/Core/Entity/ContentEntityBase.php  |   4 +-
 .../Core/Entity/EntityAccessControlHandler.php     |  10 ++-
 .../EntityTest/EntityTestDateonlyTest.php          |   2 +-
 .../EntityTest/EntityTestDatetimeTest.php          |   2 +-
 .../BlockContent/BlockContentHalJsonAnonTest.php   |   2 +-
 .../Comment/CommentHalJsonTestBase.php             |   2 +-
 .../EntityTest/EntityTestHalJsonAnonTest.php       |   2 +-
 ...tyTestHalJsonInternalPropertyNormalizerTest.php |   2 +-
 .../EntityTestLabelHalJsonAnonTest.php             |   2 +-
 .../EntityResource/Feed/FeedHalJsonTestBase.php    |   4 +-
 .../EntityResource/File/FileHalJsonAnonTest.php    |   2 +-
 .../EntityResource/Item/ItemHalJsonTestBase.php    |   2 +-
 .../EntityResource/Media/MediaHalJsonAnonTest.php  |   2 +-
 .../MenuLinkContentHalJsonAnonTest.php             |   2 +-
 .../Message/MessageHalJsonAnonTest.php             |   2 +-
 .../EntityResource/Node/NodeHalJsonAnonTest.php    |   2 +-
 .../Shortcut/ShortcutHalJsonAnonTest.php           |   2 +-
 .../EntityResource/Term/TermHalJsonAnonTest.php    |   2 +-
 .../EntityResource/User/UserHalJsonAnonTest.php    |   2 +-
 .../Action/ActionResourceTestBase.php              |   2 +-
 .../BaseFieldOverrideResourceTestBase.php          |   2 +-
 .../EntityResource/Block/BlockResourceTestBase.php |   2 +-
 .../BlockContent/BlockContentResourceTestBase.php  |   2 +-
 .../BlockContentTypeResourceTestBase.php           |   2 +-
 .../Comment/CommentResourceTestBase.php            |   2 +-
 .../CommentType/CommentTypeResourceTestBase.php    |   2 +-
 .../ConfigTest/ConfigTestResourceTestBase.php      |   2 +-
 .../ConfigurableLanguageResourceTestBase.php       |   2 +-
 .../ContactForm/ContactFormResourceTestBase.php    |   2 +-
 .../ContentLanguageSettingsResourceTestBase.php    |   2 +-
 .../DateFormat/DateFormatResourceTestBase.php      |   2 +-
 .../Editor/EditorResourceTestBase.php              |   2 +-
 .../EntityFormDisplayResourceTestBase.php          |   2 +-
 .../EntityFormModeResourceTestBase.php             |   2 +-
 .../EntityResource/EntityResourceTestBase.php      | 100 +++++++++++++++++++--
 ...ntityTestJsonInternalPropertyNormalizerTest.php |   2 +-
 .../EntityTest/EntityTestResourceTestBase.php      |   2 +-
 .../EntityTestBundleResourceTestBase.php           |   2 +-
 .../EntityTestLabelResourceTestBase.php            |   2 +-
 .../EntityViewDisplayResourceTestBase.php          |   2 +-
 .../EntityViewModeResourceTestBase.php             |   2 +-
 .../EntityResource/Feed/FeedResourceTestBase.php   |  13 ++-
 .../FieldConfig/FieldConfigResourceTestBase.php    |   2 +-
 .../FieldStorageConfigResourceTestBase.php         |   2 +-
 .../EntityResource/File/FileResourceTestBase.php   |   2 +-
 .../FilterFormat/FilterFormatResourceTestBase.php  |   2 +-
 .../ImageStyle/ImageStyleResourceTestBase.php      |   2 +-
 .../EntityResource/Item/ItemResourceTestBase.php   |  16 +++-
 .../EntityResource/Media/MediaResourceTestBase.php |   2 +-
 .../MediaType/MediaTypeResourceTestBase.php        |   2 +-
 .../EntityResource/Menu/MenuResourceTestBase.php   |   2 +-
 .../MenuLinkContentResourceTestBase.php            |   2 +-
 .../Message/MessageResourceTestBase.php            |   2 +-
 .../EntityResource/Node/NodeResourceTestBase.php   |   2 +-
 .../NodeType/NodeTypeResourceTestBase.php          |   2 +-
 .../RdfMapping/RdfMappingResourceTestBase.php      |   2 +-
 .../ResponsiveImageStyleResourceTestBase.php       |   2 +-
 .../RestResourceConfigResourceTestBase.php         |   2 +-
 .../EntityResource/Role/RoleResourceTestBase.php   |   2 +-
 .../SearchPage/SearchPageResourceTestBase.php      |   2 +-
 .../Shortcut/ShortcutResourceTestBase.php          |   2 +-
 .../ShortcutSet/ShortcutSetResourceTestBase.php    |   2 +-
 .../EntityResource/Term/TermResourceTestBase.php   |   2 +-
 .../EntityResource/Tour/TourResourceTestBase.php   |   2 +-
 .../EntityResource/User/UserResourceTestBase.php   |  56 +++++++++++-
 .../EntityResource/User/UserXmlBasicAuthTest.php   |   8 ++
 .../EntityResource/User/UserXmlCookieTest.php      |   8 ++
 .../EntityResource/View/ViewResourceTestBase.php   |   2 +-
 .../Vocabulary/VocabularyResourceTestBase.php      |   2 +-
 .../Functional/Rest/WorkflowResourceTestBase.php   |   2 +-
 .../Core/Entity/EntityAccessControlHandlerTest.php |  72 +++++++++++++++
 71 files changed, 331 insertions(+), 82 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 3effe8b..b3d00a0 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -1088,7 +1088,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 ac36411..7eab79d 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..455cdeb 100644
--- a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php
+++ b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php
@@ -99,7 +99,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       static::$fieldName => [
         [
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..d2aa6e9 100644
--- a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php
+++ b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php
@@ -99,7 +99,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       static::$fieldName => [
         [
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..371da68 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php
@@ -53,7 +53,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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 1939e04..60c1635 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Comment/CommentHalJsonTestBase.php
@@ -117,7 +117,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..c224a2a 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonAnonTest.php
@@ -78,7 +78,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..bbc6b38 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
@@ -73,7 +73,7 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       'field_test_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..d457d65 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php
@@ -80,7 +80,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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 ff89f74..975861a 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
@@ -80,7 +80,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..1eaf960 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php
@@ -77,7 +77,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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 c71b54e..742aba9 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
@@ -163,7 +163,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..f2c76960 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/MenuLinkContent/MenuLinkContentHalJsonAnonTest.php
@@ -53,7 +53,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..15f6a6e 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Message/MessageHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Message/MessageHalJsonAnonTest.php
@@ -32,7 +32,7 @@ class MessageHalJsonAnonTest extends MessageResourceTestBase {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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 e218a73..301c90d 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Node/NodeHalJsonAnonTest.php
@@ -111,7 +111,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..31ddb23 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php
@@ -53,7 +53,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..7cf1364 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php
@@ -52,7 +52,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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..fd98c93 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/User/UserHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/User/UserHalJsonAnonTest.php
@@ -52,7 +52,7 @@ protected function getExpectedNormalizedEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       '_links' => [
         'type' => [
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 5d7329f..bdb4c41 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
@@ -144,7 +144,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 60b5930..bcf67f7 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
@@ -205,7 +205,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 2962ef3..aacc151 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -121,6 +121,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
@@ -217,6 +224,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()
@@ -230,9 +253,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.
@@ -702,7 +731,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);
 
@@ -861,8 +890,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 {
@@ -870,6 +900,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
@@ -892,6 +948,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');
 
@@ -899,7 +958,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 URL and Guzzle request options that will be used in this test. The
@@ -992,6 +1051,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);
+    }
+
     // DX: 403 when sending PATCH request with read-only fields.
     // First send all fields (the "maximum normalization"). Assert the expected
     // error message for the first PATCH-protected field. Remove that field from
@@ -1234,15 +1305,26 @@ protected function getEntityResourcePostUrl() {
    *
    * @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..be475dd 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
@@ -75,7 +75,7 @@ protected function createEntity() {
   /**
    * {@inheritdoc}
    */
-  protected function getNormalizedPostEntity() {
+  protected function getNormalizedPostEntity($which = NULL) {
     return parent::getNormalizedPostEntity() + [
       'field_test_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/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 c63853e..4149a43 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
@@ -168,7 +168,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 492ff64..27a2183 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 e0a1581..a501ee3 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
@@ -143,7 +143,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
+      ],
+    ];
+  }
+
 }
