diff --git a/core/modules/file/src/FileAccessControlHandler.php b/core/modules/file/src/FileAccessControlHandler.php index 5d41c9f..a310c5f 100644 --- a/core/modules/file/src/FileAccessControlHandler.php +++ b/core/modules/file/src/FileAccessControlHandler.php @@ -22,7 +22,6 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter /** @var \Drupal\file\FileInterface $entity */ if ($operation == 'download' || $operation == 'view') { if (\Drupal::service('file_system')->uriScheme($entity->getFileUri()) === 'public') { - // Always allow access to file in public file system. return AccessResult::allowedIfHasPermissions($account, ['access content', 'administer files'], 'OR'); } elseif ($references = $this->getFileReferences($entity)) { @@ -48,8 +47,9 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter if ($operation == 'delete' || $operation == 'update') { $account = $this->prepareUser($account); $file_uid = $entity->get('uid')->getValue(); - // Only the file owner can delete and update the file entity. - if ($account->id() == $file_uid[0]['target_id']) { + // Only the file owner or user with 'administer files' permission can + // delete and update the file entity. + if ($account->id() === $file_uid[0]['target_id'] || $account->hasPermission('administer files')) { return AccessResult::allowed(); } return AccessResult::forbidden(); diff --git a/core/modules/file/tests/src/Functional/FileManagedAccessTest.php b/core/modules/file/tests/src/Functional/FileManagedAccessTest.php index 01419d4..d5c115d 100644 --- a/core/modules/file/tests/src/Functional/FileManagedAccessTest.php +++ b/core/modules/file/tests/src/Functional/FileManagedAccessTest.php @@ -12,6 +12,11 @@ class FileManagedAccessTest extends FileManagedTestBase { /** + * {@inheritdoc} + */ + public static $modules = ['node']; + + /** * Tests if public file is always accessible. */ public function testFileAccess() { @@ -29,7 +34,7 @@ public function testFileAccess() { $file->save(); // Create authenticated user to check file access. - $account = $this->createUser(['access site reports']); + $account = $this->createUser(['access content']); $this->assertTrue($file->access('view', $account), 'Public file is viewable to authenticated user'); $this->assertTrue($file->access('download', $account), 'Public file is downloadable to authenticated user'); @@ -54,7 +59,7 @@ public function testFileAccess() { $file->save(); // Create authenticated user to check file access. - $account = $this->createUser(['access site reports']); + $account = $this->createUser(['access content']); $this->assertFalse($file->access('view', $account), 'Private file is not viewable to authenticated user'); $this->assertFalse($file->access('download', $account), 'Private file is not downloadable to authenticated user'); diff --git a/core/modules/file/tests/src/Kernel/FileItemTest.php b/core/modules/file/tests/src/Kernel/FileItemTest.php index 8219af1..7a0ddc0 100644 --- a/core/modules/file/tests/src/Kernel/FileItemTest.php +++ b/core/modules/file/tests/src/Kernel/FileItemTest.php @@ -10,6 +10,7 @@ use Drupal\Tests\field\Kernel\FieldKernelTestBase; use Drupal\field\Entity\FieldStorageConfig; use Drupal\file\Entity\File; +use Drupal\user\Entity\Role; /** * Tests using entity fields of the file field type. @@ -42,6 +43,14 @@ class FileItemTest extends FieldKernelTestBase { protected function setUp() { parent::setUp(); + $this->installEntitySchema('user'); + $this->installConfig(['user']); + // Give anonymous users permission to access content, so that we can view + // and download public file. + $anonymous_role = Role::load(Role::ANONYMOUS_ID); + $anonymous_role->grantPermission('access content'); + $anonymous_role->save(); + $this->installEntitySchema('file'); $this->installSchema('file', ['file_usage']); diff --git a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php index ec870e9..2f21be6 100644 --- a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php @@ -64,7 +64,31 @@ public function denormalize($data, $class, $format = NULL, array $context = []) $path = 'temporary://' . drupal_basename($data['uri'][0]['value']); $data['uri'] = file_unmanaged_save_data($file_data, $path); - return $this->entityManager->getStorage('file')->create($data); + $entity = $this->entityManager->getStorage('file')->create($data); + + + // Remove links from data array. + unset($data['_links']); + // Get embedded resources and remove from data array. + $embedded = []; + if (isset($data['_embedded'])) { + $embedded = $data['_embedded']; + unset($data['_embedded']); + } + + // Flatten the embedded values. + foreach ($embedded as $relation => $field) { + $field_ids = $this->linkManager->getRelationInternalIds($relation); + if (!empty($field_ids)) { + $field_name = $field_ids['field_name']; + $data[$field_name] = $field; + } + } + + // Pass the names of the fields whose values can be merged. + // @todo https://www.drupal.org/node/2456257 remove this. + $entity->_restSubmittedFields = array_keys($data); + return $entity; } } 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 be1d67b..0d667a6 100644 --- a/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php @@ -33,24 +33,14 @@ class FileHalJsonAnonTest extends FileResourceTestBase { /** * {@inheritdoc} */ - protected function correctFileOwner() { - parent::correctFileOwner(); - file_copy($this->entity, 'public://drupal.txt', FILE_EXISTS_ERROR); - $this->entity->setFileUri('public://drupal.txt'); - $this->entity->save(); - } - - /** - * {@inheritdoc} - */ protected function getExpectedNormalizedEntity() { $default_normalization = parent::getExpectedNormalizedEntity(); + $normalization = $this->applyHalFieldNormalization($default_normalization); - $author = User::load($this->entity->getOwnerId()); $url = file_create_url($this->entity->getFileUri()); $normalization['uri'][0]['value'] = $url; - $uid = $author->id(); + $uid = $this->author->id(); return $normalization + [ '_embedded' => [ @@ -66,7 +56,7 @@ protected function getExpectedNormalizedEntity() { ], 'uuid' => [ [ - 'value' => $author->uuid(), + 'value' => $this->author->uuid(), ], ], ], @@ -85,51 +75,6 @@ protected function getExpectedNormalizedEntity() { ], ], ], - 'changed' => [ - [ - 'value' => $this->entity->getChangedTime(), - ], - ], - 'created' => [ - [ - 'value' => (int) $this->entity->getCreatedTime(), - ], - ], - 'fid' => [ - [ - 'value' => 1, - ], - ], - 'filemime' => [ - [ - 'value' => 'text/plain', - ], - ], - 'filename' => [ - [ - 'value' => 'drupal.txt', - ], - ], - 'filesize' => [ - [ - 'value' => (int) $this->entity->getSize(), - ], - ], - 'langcode' => [ - [ - 'value' => 'en', - ], - ], - 'status' => [ - [ - 'value' => TRUE, - ], - ], - 'uuid' => [ - [ - 'value' => $this->entity->uuid(), - ], - ], ]; } @@ -154,22 +99,11 @@ protected function getNormalizedPostEntity() { /** * {@inheritdoc} */ - public function testPost() { - $this->markTestSkipped(); - } - - /** - * {@inheritdoc} - */ - public function testPatch() { - $this->markTestSkipped(); - } - - /** - * {@inheritdoc} - */ protected function getExpectedCacheContexts() { - return ['url.site']; + return [ + 'url.site', + 'user.permissions', + ]; } } diff --git a/core/modules/image/tests/src/Kernel/ImageItemTest.php b/core/modules/image/tests/src/Kernel/ImageItemTest.php index f7a1305..497fa17 100644 --- a/core/modules/image/tests/src/Kernel/ImageItemTest.php +++ b/core/modules/image/tests/src/Kernel/ImageItemTest.php @@ -10,6 +10,7 @@ use Drupal\Tests\field\Kernel\FieldKernelTestBase; use Drupal\field\Entity\FieldStorageConfig; use Drupal\file\Entity\File; +use Drupal\user\Entity\Role; /** * Tests using entity fields of the image field type. @@ -40,6 +41,14 @@ class ImageItemTest extends FieldKernelTestBase { protected function setUp() { parent::setUp(); + $this->installEntitySchema('user'); + $this->installConfig(['user']); + // Give anonymous users permission to access content, so that we can view + // and download public file. + $anonymous_role = Role::load(Role::ANONYMOUS_ID); + $anonymous_role->grantPermission('access content'); + $anonymous_role->save(); + $this->installEntitySchema('file'); $this->installSchema('file', ['file_usage']); diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 7d70782..81baf9e 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -404,7 +404,7 @@ public function testGet() { $this->assertFalse($response->hasHeader('X-Drupal-Cache')); } $cache_tags_header_value = $response->getHeader('X-Drupal-Cache-Tags')[0]; - $this->assertEquals($this->getExpectedCacheTags(), empty($cache_tags_header_value) ? [] : explode(' ', $cache_tags_header_value), '', .0, 2, TRUE); + $this->assertEquals($this->getExpectedCacheTags(), empty($cache_tags_header_value) ? [] : explode(' ', $cache_tags_header_value)); $cache_contexts_header_value = $response->getHeader('X-Drupal-Cache-Contexts')[0]; $this->assertEquals($this->getExpectedCacheContexts(), empty($cache_contexts_header_value) ? [] : explode(' ', $cache_contexts_header_value)); // Sort the serialization data first so we can do an identical comparison 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 3754bf3..5302c20 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php @@ -32,6 +32,13 @@ ]; /** + * Additional user to prevent access to file by uid. + * + * @var \Drupal\user\UserInterface + */ + protected $author; + + /** * {@inheritdoc} */ protected function setUpAuthorization($method) { @@ -39,6 +46,7 @@ protected function setUpAuthorization($method) { case 'GET': $this->grantPermissionsToTestedRole(['access content']); break; + case 'POST': case 'PATCH': case 'DELETE': @@ -51,26 +59,22 @@ protected function setUpAuthorization($method) { * {@inheritdoc} */ protected function createEntity() { - $author = User::load(3); - if (!$author) { - $author = User::create([ - 'name' => 'Unknown', - 'status' => 1, - ]); - $author->save(); - } - $uid = $author->id(); - - $file = File::create([ - 'uid' => $uid, - 'filename' => 'drupal.txt', - 'uri' => 'public://drupal.txt', - 'filemime' => 'text/plain', - 'status' => FILE_STATUS_PERMANENT, + $this->author = User::create([ + 'name' => 'Unknown' . $this->randomMachineName(8), + 'status' => 1, ]); - file_put_contents($file->getFileUri(), 'Drupal'); + $this->author->save(); + + $file = File::create(); + $file->setOwnerId($this->author->id()); + $file->setFilename('drupal.txt'); + $file->setMimeType('text/plain'); + $file->setFileUri('public://drupal.txt'); + $file->set('status', FILE_STATUS_PERMANENT); $file->save(); + file_put_contents($file->getFileUri(), 'Drupal'); + return $file; } @@ -78,7 +82,6 @@ protected function createEntity() { * {@inheritdoc} */ protected function getExpectedNormalizedEntity() { - $author = User::load($this->entity->getOwnerId()); return [ 'changed' => [ [ @@ -122,10 +125,10 @@ protected function getExpectedNormalizedEntity() { ], 'uid' => [ [ - 'target_id' => (int) $author->id(), + 'target_id' => (int) $this->author->id(), 'target_type' => 'user', - 'target_uuid' => $author->uuid(), - 'url' => base_path() . 'user/' . $author->id(), + 'target_uuid' => $this->author->uuid(), + 'url' => base_path() . 'user/' . $this->author->id(), ], ], 'uri' => [ @@ -145,11 +148,10 @@ protected function getExpectedNormalizedEntity() { * {@inheritdoc} */ protected function getNormalizedPostEntity() { - $uid = static::$auth ? 2 : 0; return [ 'uid' => [ [ - 'target_id' => $uid, + 'target_id' => $this->author->id(), ], ], 'filename' => [ @@ -173,6 +175,7 @@ protected function getExpectedCacheContexts() { * {@inheritdoc} */ public function testPost() { + // @todo https://www.drupal.org/node/1927648 $this->markTestSkipped(); } @@ -193,10 +196,4 @@ protected function getExpectedUnauthorizedAccessMessage($method) { return parent::getExpectedUnauthorizedAccessMessage($method); } - /** - * {@inheritdoc} - */ - protected function getExpectedCacheTags() { - return array_diff(parent::getExpectedCacheTags(), ['config:user.role.anonymous']); - } } diff --git a/core/modules/text/src/Tests/TextFieldTest.php b/core/modules/text/src/Tests/TextFieldTest.php index d061c00..af0b73e 100644 --- a/core/modules/text/src/Tests/TextFieldTest.php +++ b/core/modules/text/src/Tests/TextFieldTest.php @@ -17,6 +17,11 @@ class TextFieldTest extends StringFieldTest { /** + * {@inheritdoc} + */ + public static $modules = ['node']; + + /** * A user with relevant administrative privileges. * * @var \Drupal\user\UserInterface @@ -26,7 +31,7 @@ class TextFieldTest extends StringFieldTest { protected function setUp() { parent::setUp(); - $this->adminUser = $this->drupalCreateUser(['administer filters']); + $this->adminUser = $this->drupalCreateUser(['administer filters', 'access content']); } // Test fields.