From dce52d1ff1e181870d6984e9fd9c09396517e609 Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Thu, 24 Feb 2022 10:56:52 +1000
Subject: [PATCH] Issue #3154962 by alexpott, vijaycs85, bbrala, Berdir, Wim
 Leers: TemporaryJsonapiFileFieldUploader::checkFileUploadAccess() checks for
 bundle

---
 .../TemporaryJsonapiFileFieldUploader.php     |   8 +-
 .../TemporaryJsonapiFileFieldUploaderTest.php | 179 ++++++++++++++++++
 2 files changed, 185 insertions(+), 2 deletions(-)
 create mode 100644 core/modules/jsonapi/tests/src/Kernel/Controller/TemporaryJsonapiFileFieldUploaderTest.php

diff --git a/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php
index 62e96a57129b..0551812fa848 100644
--- a/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php
+++ b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php
@@ -303,13 +303,17 @@ public function validateAndParseContentDispositionHeader(Request $request) {
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   (optional) The entity to which the file is to be uploaded, if it exists.
    *   If the entity does not exist and it is not given, create access to the
-   *   file will be checked.
+   *   entity the file is attached to will be checked.
    *
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The file upload access result.
    */
   public static function checkFileUploadAccess(AccountInterface $account, FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
-    assert(is_null($entity) || $field_definition->getTargetEntityTypeId() === $entity->getEntityTypeId() && $field_definition->getTargetBundle() === $entity->bundle());
+    assert(is_null($entity) ||
+      $field_definition->getTargetEntityTypeId() === $entity->getEntityTypeId() &&
+      // Base fields do not have target bundles.
+      (is_null($field_definition->getTargetBundle()) || $field_definition->getTargetBundle() === $entity->bundle())
+    );
     $entity_type_manager = \Drupal::entityTypeManager();
     $entity_access_control_handler = $entity_type_manager->getAccessControlHandler($field_definition->getTargetEntityTypeId());
     $bundle = $entity_type_manager->getDefinition($field_definition->getTargetEntityTypeId())->hasKey('bundle') ? $field_definition->getTargetBundle() : NULL;
diff --git a/core/modules/jsonapi/tests/src/Kernel/Controller/TemporaryJsonapiFileFieldUploaderTest.php b/core/modules/jsonapi/tests/src/Kernel/Controller/TemporaryJsonapiFileFieldUploaderTest.php
new file mode 100644
index 000000000000..38ed1552855e
--- /dev/null
+++ b/core/modules/jsonapi/tests/src/Kernel/Controller/TemporaryJsonapiFileFieldUploaderTest.php
@@ -0,0 +1,179 @@
+<?php
+
+namespace Drupal\Tests\jsonapi\Kernel\Controller;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\jsonapi\Controller\TemporaryJsonapiFileFieldUploader;
+use Drupal\node\Entity\Node;
+use Drupal\node\Entity\NodeType;
+use Drupal\Tests\jsonapi\Kernel\JsonapiKernelTestBase;
+use Drupal\user\Entity\Role;
+use Drupal\user\Entity\User;
+
+/**
+ * @coversDefaultClass \Drupal\jsonapi\Controller\TemporaryJsonapiFileFieldUploader
+ * @group jsonapi
+ */
+class TemporaryJsonapiFileFieldUploaderTest extends JsonapiKernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'node',
+    'field',
+    'jsonapi',
+    'serialization',
+    'system',
+    'user',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    // Add the entity schemas.
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('user');
+    // Add the additional table schemas.
+    $this->installSchema('system', ['sequences']);
+    $this->installSchema('node', ['node_access']);
+    $this->installSchema('user', ['users_data']);
+    NodeType::create([
+      'type' => 'lorem',
+    ])->save();
+    $type = NodeType::create([
+      'type' => 'article',
+    ]);
+    $type->save();
+    $type = NodeType::create([
+      'type' => 'page',
+    ]);
+    $type->save();
+    $this->createEntityReferenceField('node', 'article', 'field_relationships', 'Relationship', 'node', 'default', ['target_bundles' => ['article']], FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+
+    Role::create([
+      'id' => 'article editor',
+      'label' => 'article editor',
+      'permissions' => [
+        'access content',
+        'create article content',
+        'edit any article content',
+      ],
+    ])->save();
+
+    Role::create([
+      'id' => 'page editor',
+      'label' => 'page editor',
+      'permissions' => [
+        'access content',
+        'create page content',
+        'edit any page content',
+      ],
+    ])->save();
+
+    Role::create([
+      'id' => 'editor',
+      'label' => 'editor',
+      'permissions' => [
+        'bypass node access',
+      ],
+    ])->save();
+  }
+
+  /**
+   * @covers ::checkFileUploadAccess
+   */
+  public function testCheckFileUploadAccessWithBaseField() {
+    // Create a set of users for access testing.
+    $article_editor = User::create([
+      'name' => 'article editor',
+      'mail' => 'article@localhost',
+      'status' => 1,
+      // Do not use UID 1 as that has access to everything.
+      'uid' => 2,
+      'roles' => ['article editor'],
+    ]);
+    $page_editor = User::create([
+      'name' => 'page editor',
+      'mail' => 'page@localhost',
+      'status' => 1,
+      'uid' => 3,
+      'roles' => ['page editor'],
+    ]);
+    $editor = User::create([
+      'name' => 'editor',
+      'mail' => 'editor@localhost',
+      'status' => 1,
+      'uid' => 3,
+      'roles' => ['editor'],
+    ]);
+    $no_access_user = User::create([
+      'name' => 'no access',
+      'mail' => 'user@localhost',
+      'status' => 1,
+      'uid' => 4,
+    ]);
+
+    // Create an entity to test access against.
+    $node = Node::create([
+      'title' => 'dummy_title',
+      'type' => 'article',
+      'uid' => 1,
+    ]);
+
+    // While the method is only used to check file fields it should work without
+    // error for any field whether it is a base field or a bundle field.
+    $base_field_definition = $this->container->get('entity_field.manager')->getBaseFieldDefinitions('node')['title'];
+    $bundle_field_definition = $this->container->get('entity_field.manager')->getFieldDefinitions('node', 'article')['field_relationships'];
+
+    // Tests the expected access result for each user.
+    // The $article_editor account can edit any article.
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($article_editor, $base_field_definition, $node);
+    $this->assertTrue($result->isAllowed());
+    // The article editor cannot create a node of undetermined type.
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($article_editor, $base_field_definition);
+    $this->assertFalse($result->isAllowed());
+    // The article editor can edit any article.
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($article_editor, $bundle_field_definition, $node);
+    $this->assertTrue($result->isAllowed());
+    // The article editor can create an article. The type can be determined
+    // because the field is a bundle field.
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($article_editor, $bundle_field_definition);
+    $this->assertTrue($result->isAllowed());
+
+    // The $editor account has the bypass node access permissions and can edit
+    // and create all node types.
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($editor, $base_field_definition, $node);
+    $this->assertTrue($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($editor, $base_field_definition);
+    $this->assertTrue($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($editor, $bundle_field_definition, $node);
+    $this->assertTrue($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($editor, $bundle_field_definition);
+    $this->assertTrue($result->isAllowed());
+
+    // The $page_editor account can only edit and create pages therefore has no
+    // access.
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($page_editor, $base_field_definition, $node);
+    $this->assertFalse($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($page_editor, $base_field_definition);
+    $this->assertFalse($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($page_editor, $bundle_field_definition, $node);
+    $this->assertFalse($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($page_editor, $bundle_field_definition);
+    $this->assertFalse($result->isAllowed());
+
+    // The $no_access_user account has no access at all.
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($no_access_user, $base_field_definition, $node);
+    $this->assertFalse($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($no_access_user, $base_field_definition);
+    $this->assertFalse($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($no_access_user, $bundle_field_definition, $node);
+    $this->assertFalse($result->isAllowed());
+    $result = TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($no_access_user, $bundle_field_definition);
+    $this->assertFalse($result->isAllowed());
+  }
+
+}
-- 
GitLab