Problem/Motivation

Drupal\file\Plugin\Field\FieldType\FileItem::getUploadValidators() always sets the maximum file size for uploading based on post_max_size and upload_max_filesize PHP settings.

That prevents us from avoiding max file size validation for some use-cases. A typical use case where this would be helpful is when files are not stored on the same server as Drupal (e.g. they are stored in a remote file system, S3).

Proposed resolution

Let file size validator use max_filesize setting if it is explicitly configured (not empty) only.
Otherwise, we should either skip file_validate_size() completely or do the call and indicate that no file size limit should be enforced.

Remaining tasks

User interface changes

API changes

Data model changes

Comments

mbovan created an issue. See original summary.

mbovan’s picture

Issue summary: View changes
Issue tags: +Needs tests
StatusFileSize
new2 KB

The first patch that implements the idea from the issue summary.

mbovan’s picture

Status: Active » Needs review
berdir’s picture

Category: Task » Bug report
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -302,12 +302,9 @@ public function getUploadValidators() {
     $max_filesize = Bytes::toInt(file_upload_max_size());
     if (!empty($settings['max_filesize'])) {
-      $max_filesize = min($max_filesize, Bytes::toInt($settings['max_filesize']));
+      $validators['file_validate_size'] = min($max_filesize, Bytes::toInt($settings['max_filesize']));
     }
 
-    // There is always a file size limit due to the PHP server limit.
-    $validators['file_validate_size'] = [$max_filesize];
-

this is also used for the actual upload validation.

I think we should instead just alter it in the constraint only, so we don't do this validation for existing files.

Also, this is IMHO a bug/regression because 7.x didn't do anything like this.

berdir’s picture

That said, I don't really understand why we do need that in there, as you wouldn't actually ever get that far if it's over the system limit?

But that's for another issue to change/discuss.

Status: Needs review » Needs work

The last submitted patch, 2: file_size_validator-2867336-2.patch, failed testing.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new2.41 KB
new2.11 KB

Updates as suggested in #4.

mbovan’s picture

StatusFileSize
new2.69 KB
new1.23 KB

And conversions...

mbovan’s picture

StatusFileSize
new2.63 KB
new786 bytes

Ahh.. Removed duplicated comment.

mbovan’s picture

StatusFileSize
new2.65 KB

Not sure in which version this should go... Here is the #9 with unchanged old array syntax (applies to 8.2.7).

The last submitted patch, 7: file_size_validator-2867336-5.patch, failed testing.

berdir’s picture

Status: Needs review » Needs work

Looks good to me, I don't really know how we could test this nor how we need to convince to RTBC this. Possibly crosspost in the S3 CORS upload issue, as I'm sure people there will run into this problem.

Maybe we can improve the comments a bit and clearly state that files that are already uploaded and/or might even be stored remotly to not need to respect the max upload size. Needs work for that.

berdir’s picture

+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -177,7 +177,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
       '#default_value' => $settings['max_filesize'],
-      '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))),
+      '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes could be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))),
       '#size' => 10,
       '#element_validate' => array(array(get_class($this), 'validateMaxFilesize')),

could reads strange, maybe we can change the test to specifically mention *Uploading files to the server will...* or so.

berdir’s picture

Idea how we could have *some* test coverage:

Move the logic for getting the validators into a protected helper method, write a unit test that uses mocking and a subclass to call that helper directly and get the validators, with different settings.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new2.62 KB

Reroll.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

gabesullice’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
    @@ -179,7 +179,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
    +      '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. IUPloading files will only be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', ['%limit' => format_size(Environment::getUploadMaxSize())]),
    

    IUPloading files will only <- I think this got messed up in the reroll.

  2. +++ b/core/modules/file/src/Plugin/Validation/Constraint/FileValidationConstraintValidator.php
    @@ -23,6 +24,16 @@ public function validate($value, Constraint $constraint) {
    +    // Always respect the configured maximum file size.
    +    if ($max_filesize = Bytes::toInt($value->getFieldDefinition()->getSetting('max_filesize'))) {
    +      $validators['file_validate_size'] = [$max_filesize];
    

    This makes it impossible to set max_filesize to 0.

    I think that's acceptable because one should disable the field or make it inaccessible. However, it'd make the code more pleasing if it precisely matched the comment. Something like:

    // Always respect the configured maximum file size.
    $field_definition = $value->getFieldDefinition();
    if (array_key_exists('max_filesize', $field_definition->getSettings())) {
      $validators['file_validate_size'] = [Bytes::toInt($field_definition->getSetting('max_filesize'))];
    }
    
gabesullice’s picture

That prevents us from avoiding max file size validation for some use-cases. A typical use case where this would be helpful is when files are not stored on the same server as Drupal (e.g. they are stored in a remote file system, S3).

Maybe I'm misunderstanding this because I'm not very familiar with file system stuff, but couldn't one argue that this is an upstream bug/missing feature in PHP? E.g. shouldn't there be stream wrapper specific versions of upload_max_filesize so that Drupal doesn't have to ignore its environment's configuration?

berdir’s picture

upload_max_filesize is a PHP ini setting that PHP automatically enforces, this is not related to stream wrappers. The file could absolutely live in the regular sites directory, maybe you've built a custom feature that imports files from a file share or whatever.

This is about validating a file that already exists on the server in regards to whether or not you would be able to upload it to that server. Why would you do that?

If you move to a new server that has a lower upload limit, does that make all your existing files suddenly invalid?

It makes sense to check the setting so that we can tell the user that this limit applies, but that's all it should be used for.

gabesullice’s picture

Thanks for helping me understand @Berdir. I misinterpreted the issue to be about uploading to the filesystem or to alternative storage like S3 and made the assumption that the problem was that upload_max_filesize shouldn't/doesn't apply when forward-streaming the upload to an S3 bucket.

kim.pepper’s picture

Can we add a test for this?

berdir’s picture

I tried, but no, not easily. upload_max_filesize can't be changed/set at runtime. It doesn't just not use the value, it actively ignores an attempt to set it.

And without the ability to change it, I doubt that we want to have a test that creates a file that is bigger than upload_max_filesize, whatever that value might be.

If that would work, then doing something like \Drupal\Tests\file\Kernel\FileItemValidationTest::testFileValidationConstraint() and setting that instead of the explicit setting would work.

I suppose we could write a unit test for FileValidationConstraintValidator and mock getUploadValidators() and everything else going on, including the function call to file_validate(), but that seems like a lot work with a lot of hardcoded assumptions about the exact implementation of the code.

If your patch to make them separate constraints would move that logic out of \Drupal\Tests\file\Kernel\FileItemValidationTest::testFileValidationConstraint() and into whatever logic creates dynamic constraints with configuration then maybe we could check that?

hchonov’s picture

StatusFileSize
new2.62 KB

reroll.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jcnventura’s picture

Bytes::toInt() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0

Also, please take into account the comment in #24 where we should check if the setting exists instead of checking if it is not 0.

One personal nitpick: I really dislike the code readability of if (a = b) {} instead of a = b; if (a) {}. There's coding standards for writing secure code that specifically forbid this kind of thing because of how easy it is to overlook what is happening here.

jcnventura’s picture

Status: Needs work » Needs review
StatusFileSize
new1.05 KB
new2.68 KB

Status: Needs review » Needs work

The last submitted patch, 33: 2867336-33.patch, failed testing. View results

jcnventura’s picture

Status: Needs work » Needs review
StatusFileSize
new1.3 KB
new2.69 KB

Needed another further fix.

jonathanshaw’s picture

Status: Needs review » Reviewed & tested by the community

FileFieldValidateTest already tests that the max filesize field setting is respected on both upload and other validation.

Per #29 there is no good way to test that the php environment setting is respected on upload but correctly ignored with other validation.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 35: 2867336-35.patch, failed testing. View results

berdir’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs tests

Random JS test fail, looks very unrelated?



1) Drupal\Tests\layout_builder\FunctionalJavascript\ContentPreviewToggleTest::testContentPreviewToggle
Behat\Mink\Exception\ElementHtmlException: Element exists on the page.

/var/www/html/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php:509
/var/www/html/core/modules/layout_builder/tests/src/FunctionalJavascript/ContentPreviewToggleTest.php:138
/var/www/html/core/modules/layout_builder/tests/src/FunctionalJavascript/ContentPreviewToggleTest.php:94
/var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php:726

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 35: 2867336-35.patch, failed testing. View results

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new2.71 KB

Reroll for 9.5+.

Status: Needs review » Needs work

The last submitted patch, 42: 2867336-42.patch, failed testing. View results

ravi.shankar’s picture

Status: Needs work » Needs review

It's green so changing status to needs review.

jonathanshaw’s picture

Status: Needs review » Reviewed & tested by the community

rtbc per #38

  • catch committed 6c93f38 on 10.0.x
    Issue #2867336 by mbovan, jcnventura, Berdir, hchonov, gabesullice,...
  • catch committed c647ef4 on 10.1.x
    Issue #2867336 by mbovan, jcnventura, Berdir, hchonov, gabesullice,...
  • catch committed 5839749 on 9.5.x
    Issue #2867336 by mbovan, jcnventura, Berdir, hchonov, gabesullice,...
catch’s picture

Version: 10.1.x-dev » 9.5.x-dev
Status: Reviewed & tested by the community » Fixed

This looks fine and I also can't think of way to test it. Committed/pushed to 10.1.x, cherry-picked to 10.0.x and 9.5.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

omd’s picture

Couldn't this be tested this with the plupload api module and the plupload widget module?