diff --git a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php index e4e44b6152..0b7214c7b3 100644 --- a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php +++ b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php @@ -172,22 +172,31 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) { // Create the file. $file_uri = "{$destination}/{$filename}"; - $this->streamUploadData($file_uri); + $temp_file_path = $this->streamUploadData($file_uri); // Begin building file entity. $values = [ 'uid' => $this->currentUser->id(), 'filename' => $filename, + 'filemime' => $this->mimeTypeGuesser->guess($filename), 'uri' => $file_uri, // Set the size. This is done in File::preSave() but we validate the file // before it is saved. - 'filesize' => @filesize($file_uri), + 'filesize' => @filesize($temp_file_path), ]; - $values['filemime'] = $this->mimeTypeGuesser->guess($values['filename']); $file = File::create($values); + // Validate the file entity against entiy level validation and field level + // validators. $this->validate($file, $field_definition); + // Move the file to the correct location after validation. + // Move the file to the correct location based on the file entity, + // replacing any existing file. + if (!file_unmanaged_move($temp_file_path, $file_uri)) { + throw new HttpException(500, 'Temporary file could not be moved to file location'); + } + $file->save(); // 201 Created responses return the newly created entity in the response @@ -204,35 +213,32 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) { /** * Streams file upload data to temporary file and moves to file destination. * - * @param string $destination_uri + * @return string + * The temp file path. * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ - protected function streamUploadData($destination_uri) { + protected function streamUploadData() { // 'rb' is needed so reading works correctly on windows environments too. $file_data = fopen('php://input','rb'); - $temp_file_name = $this->fileSystem->tempnam('temporary://', 'file'); + $temp_file_path = $this->fileSystem->tempnam('temporary://', 'file'); - if ($temp_file = fopen($temp_file_name, 'wb')) { + if ($temp_file = fopen($temp_file_path, 'wb')) { while (!feof($file_data)) { fwrite($temp_file, fread($file_data, 8192)); } fclose($temp_file); - - // Move the file to the correct location based on the file entity, - // replacing any existing file. - if (!file_unmanaged_move($temp_file_name, $destination_uri)) { - throw new HttpException(500, 'Temporary file could not be moved to file location'); - } } else { - $this->getLogger('file system')->error('Temporary file "%path" could not be opened for file upload', ['%path' => $temp_file_name]); + $this->getLogger('file system')->error('Temporary file "%path" could not be opened for file upload', ['%path' => $temp_file_path]); throw new HttpException(500, 'Temporary file could not be opened'); } fclose($file_data); + + return $temp_file_path; } /** diff --git a/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php b/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php index 42a956ffcd..1ec01d3eda 100644 --- a/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php @@ -9,6 +9,7 @@ use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\file\Entity\File; +use Drupal\Tests\RandomGeneratorTrait; use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait; use Drupal\Tests\rest\Functional\ResourceTestBase; use Drupal\user\Entity\User; @@ -20,7 +21,7 @@ */ abstract class FileUploadResourceTestBase extends ResourceTestBase { - use BcTimestampNormalizerUnixTestTrait; + use RandomGeneratorTrait, BcTimestampNormalizerUnixTestTrait; /** * {@inheritdoc} @@ -46,6 +47,20 @@ */ protected $testFileData = 'Hares sit on chairs, and mules sit on stools.'; + /** + * The test field storage config. + * + * @var \Drupal\field\Entity\FieldStorageConfig + */ + protected $fieldStorage; + + /** + * The field config. + * + * @var \Drupal\field\Entity\FieldConfig + */ + protected $field; + /** * The parent entity. * @@ -74,7 +89,7 @@ public function setUp() { parent::setUp(); // Add a file field. - FieldStorageConfig::create([ + $this->fieldStorage = FieldStorageConfig::create([ 'entity_type' => 'entity_test', 'field_name' => 'field_rest_file_test', 'type' => 'file', @@ -82,10 +97,10 @@ public function setUp() { 'uri_scheme' => 'public', ], ]) - ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) - ->save(); + ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); + $this->fieldStorage->save(); - FieldConfig::create([ + $this->field = FieldConfig::create([ 'entity_type' => 'entity_test', 'field_name' => 'field_rest_file_test', 'bundle' => 'entity_test', @@ -96,8 +111,8 @@ public function setUp() { ], ]) ->setLabel('Test file field') - ->setTranslatable(FALSE) - ->save(); + ->setTranslatable(FALSE); + $this->field->save(); // Create an entity that a file can be attached to. $this->entity = EntityTest::create([ @@ -208,6 +223,33 @@ public function testFileUploadInvalidFileType() { $this->assertFalse(file_exists('public://foobar/example.txt')); } + /** + * Tests using the file upload route with a file size larger than allowed. + */ + public function testFileUploadLargerFileSize() { + // Set a limit of 50 bytes. + $this->field->setSetting('max_filesize', 50) + ->save(); + $this->refreshTestStateAfterRestConfigChange(); + + $this->initAuthentication(); + + $this->provisionResource([static::$format], static::$auth ? [static::$auth] : [], ['POST']); + + $this->setUpAuthorization('POST'); + + $uri = Url::fromUri('base:' . static::$postUri); + + // Generate a string larger than the 50 byte limit set. + $response = $this->fileRequest($uri, $this->randomString(100)); + + $this->assertSame(422, $response->getStatusCode()); + + // Make sure that no file was saved. + $this->assertEmpty(File::load(1)); + $this->assertFalse(file_exists('public://foobar/example.txt')); + } + /** * {@inheritdoc} */