diff --git a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php index e16ddba453c03ba50ec3a9c000c90852e581bf93..0d5425009c04d2da136a57dfe34253e7a9bbd650 100644 --- a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php +++ b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php @@ -278,15 +278,28 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) { $file->setMimeType($this->mimeTypeGuesser->guess($prepared_filename)); @trigger_error('\Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Implement \Symfony\Component\Mime\MimeTypeGuesserInterface instead. See https://www.drupal.org/node/3133341', E_USER_DEPRECATED); } - $file->setFileUri($file_uri); + $file->setFileUri($temp_file_path); // Set the size. This is done in File::preSave() but we validate the file // before it is saved. $file->setSize(@filesize($temp_file_path)); - // Validate the file entity against entity-level validation and field-level - // validators. - $this->validate($file, $validators); + // Validate the file against field-level validators first while the file is + // still a temporary file. Validation is split up in 2 steps to be the same + // as in _file_save_upload_single(). + // For backwards compatibility this part is copied from ::validate() to + // leave that method behavior unchanged. + // @todo Improve this with a file uploader service in + // https://www.drupal.org/project/drupal/issues/2940383 + $errors = file_validate($file, $validators); + + if (!empty($errors)) { + $message = "Unprocessable Entity: file validation failed.\n"; + $message .= implode("\n", array_map([PlainTextOutput::class, 'renderFromHtml'], $errors)); + + throw new UnprocessableEntityHttpException($message); + } + $file->setFileUri($file_uri); // Move the file to the correct location after validation. Use // FileSystemInterface::EXISTS_ERROR as the file location has already been // determined above in FileSystem::getDestinationFilename(). @@ -297,6 +310,9 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) { throw new HttpException(500, 'Temporary file could not be moved to file location'); } + // Second step of the validation on the file object itself now. + $this->resourceValidate($file); + $file->save(); $this->lock->release($lock_id); @@ -449,6 +465,11 @@ protected function validateAndLoadFieldDefinition($entity_type_id, $bundle, $fie /** * Validates the file. * + * @todo this method is unused in this class because file validation needs to + * be split up in 2 steps in ::post(). Add a deprecation notice as soon as a + * central core file upload service can be used in this class. + * See https://www.drupal.org/project/drupal/issues/2940383 + * * @param \Drupal\file\FileInterface $file * The file entity to validate. * @param array $validators diff --git a/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php index 6682bb3a6f8ef7430dc4e56e60230f8100d9abba..d07b4562ec84168c794f3cf7d7a27d89949b6aa0 100644 --- a/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php +++ b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php @@ -19,6 +19,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Utility\Token; use Drupal\Component\Render\PlainTextOutput; +use Drupal\Core\Entity\EntityConstraintViolationList; use Drupal\file\Entity\File; use Drupal\file\Plugin\Field\FieldType\FileFieldItemList; use Psr\Log\LoggerInterface; @@ -199,18 +200,37 @@ public function handleFileUploadForField(FieldDefinitionInterface $field_definit @trigger_error('\Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Implement \Symfony\Component\Mime\MimeTypeGuesserInterface instead. See https://www.drupal.org/node/3133341', E_USER_DEPRECATED); $file->setMimeType($this->mimeTypeGuesser->guess($prepared_filename)); } - $file->setFileUri($file_uri); + $file->setFileUri($temp_file_path); // Set the size. This is done in File::preSave() but we validate the file // before it is saved. $file->setSize(@filesize($temp_file_path)); - // Validate the file entity against entity-level validation and field-level - // validators. - $violations = $this->validate($file, $validators); - if ($violations->count() > 0) { + // Validate the file against field-level validators first while the file is + // still a temporary file. Validation is split up in 2 steps to be the same + // as in _file_save_upload_single(). + // For backwards compatibility this part is copied from ::validate() to + // leave that method behavior unchanged. + // @todo Improve this with a file uploader service in + // https://www.drupal.org/project/drupal/issues/2940383 + $errors = file_validate($file, $validators); + if (!empty($errors)) { + $violations = new EntityConstraintViolationList($file); + $translator = new DrupalTranslator(); + $entity = EntityAdapter::createFromEntity($file); + foreach ($errors as $error) { + $violation = new ConstraintViolation($translator->trans($error), + $error, + [], + $entity, + '', + NULL + ); + $violations->add($violation); + } return $violations; } + $file->setFileUri($file_uri); // Move the file to the correct location after validation. Use // FileSystemInterface::EXISTS_ERROR as the file location has already been // determined above in FileSystem::getDestinationFilename(). @@ -221,6 +241,16 @@ public function handleFileUploadForField(FieldDefinitionInterface $field_definit throw new HttpException(500, 'Temporary file could not be moved to file location'); } + // Second step of the validation on the file object itself now. + $violations = $file->validate(); + + // Remove violations of inaccessible fields as they cannot stem from our + // changes. + $violations->filterByFieldAccess(); + if ($violations->count() > 0) { + return $violations; + } + $file->save(); $this->lock->release($lock_id); @@ -359,6 +389,11 @@ protected function streamUploadData() { /** * Validates the file. * + * @todo this method is unused in this class because file validation needs to + * be split up in 2 steps in ::handleFileUploadForField(). Add a deprecation + * notice as soon as a central core file upload service can be used in this + * class. See https://www.drupal.org/project/drupal/issues/2940383 + * * @param \Drupal\file\FileInterface $file * The file entity to validate. * @param array $validators