diff --git a/VERSIONS_HOTLINE/73509 b/VERSIONS_HOTLINE/73509 new file mode 100644 index 0000000000000000000000000000000000000000..c226d6759cccaad0949cf6d72597d4a6e0de9523 --- /dev/null +++ b/VERSIONS_HOTLINE/73509 @@ -0,0 +1 @@ + - ticket #73509 : Explorateur de fichiers : correction de la validation de la taille de fichier, l'explorateur s'appuie désormais sur la configuration courante de PHP (post_max_size et upload_max_filesize) \ No newline at end of file diff --git a/library/Class/Php/Config.php b/library/Class/Php/Config.php new file mode 100644 index 0000000000000000000000000000000000000000..8685008a7fbfe7cad2db1f2cb11d9edda1947c84 --- /dev/null +++ b/library/Class/Php/Config.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright (c) 2012-2017, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_Php_Config { + use Trait_Singleton; + + public function getMaxFileSize() { + $ini_post_max_size = ini_get('post_max_size'); + $post_max_size = $this->_sizeToBytes($ini_post_max_size); + + $ini_upload_max_filesize = ini_get('upload_max_filesize'); + $upload_max = $this->_sizeToBytes($ini_upload_max_filesize); + + if (!$upload_max && !$post_max_size) + return; + + return $upload_max < $post_max_size + ? $ini_upload_max_filesize + : $ini_post_max_size; + } + + + protected function _sizeToBytes($size) { + $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); + $size = preg_replace('/[^0-9\.]/', '', $size); + + return $unit + ? round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))) + : round($size); + } +} diff --git a/library/ZendAfi/Form/Admin/FileManager/Import.php b/library/ZendAfi/Form/Admin/FileManager/Import.php index b3ecc5c5c6bdd8fc9c4d2e1091aaa7eeb58d6755..9a46e554925f20cf939c070f96bbe063a965a38a 100644 --- a/library/ZendAfi/Form/Admin/FileManager/Import.php +++ b/library/ZendAfi/Form/Admin/FileManager/Import.php @@ -41,22 +41,33 @@ class ZendAfi_Form_Admin_FileManager_Import extends ZendAfi_Form { return $element; $extensions = Class_AdminVar::get('ALLOWED_FILES_EXTENSIONS_FOR_IMPORT'); + $not_found_message = [$form->_('Un problème est survenu lors du transfert.')]; + $php_config = Class_Php_Config::getInstance(); + if ($max_file_size = $php_config->getMaxFileSize()) + $not_found_message[] = $form->_('Veuillez vérifier que la taille du fichier ne dépasse pas le maximum autorisé de %s', + $max_file_size); + + $file_extension_validator = (new ZendAfi_Validate_FileExtension(explode(';', $extensions))) + ->setMessage($form->_('Les extensions possibles sont : %s', $extensions), + ZendAfi_Validate_FileExtension::FALSE_EXTENSION) + + ->setMessage(implode(' ', $not_found_message), + ZendAfi_Validate_FileExtension::NOT_FOUND); + + $element + ->addValidator($file_extension_validator, true) - return $element ->addValidator((new ZendAfi_Validate_FileName(Class_FileManager::REGEX_NAME)) ->setMessage($form->_('Le nom doit contenir uniquement des lettres, des chiffres et les caratères "_", "-", ".". Exemple : "mon_fichier.jpg"'), ZendAfi_Validate_FileName::NOT_MATCH)) ->addValidator((new Zend_Validate_File_Count(1)) - ->setMessage($form->_('Un seul fichier doit être transféré.'))) + ->setMessage($form->_('Un seul fichier doit être transféré.'))); + + - ->addValidator((new Zend_Validate_File_Size('1', '10000000', true)) - ->setMessage($form->_('La taille du fichier doit être supérieur à "%min%"'), Zend_Validate_File_Size::TOO_SMALL) - ->setMessage($form->_('La taille du fichier doit être inférieur à "%max%"'), Zend_Validate_File_Size::TOO_BIG) - ->setMessage($form->_('Le fichier n\'est pas lisible'), Zend_Validate_File_Size::NOT_FOUND)) - ->addValidator((new Zend_Validate_File_Extension(explode(';', $extensions))) - ->setMessage($form->_('Les extensions possibles sont : %s', $extensions))); + return $element; } diff --git a/library/ZendAfi/Validate/FileExtension.php b/library/ZendAfi/Validate/FileExtension.php new file mode 100644 index 0000000000000000000000000000000000000000..1f374bcc0bc0c8d6fccab84b6e84de4f8066c66b --- /dev/null +++ b/library/ZendAfi/Validate/FileExtension.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright (c) 2012-2017, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_Validate_FileExtension extends Zend_Validate_File_Extension { + use Trait_StaticFileSystem; + + /** + * Testable version of parent method + * + * Returns true if and only if the fileextension of $value is included in the + * set extension list + * + * @param string $value Real file to check for extension + * @param array $file File data from Zend_File_Transfer + * @return boolean + */ + public function isValid($value, $file = null) + { + $filesystem = static::getFileSystem(); + + // Is file readable ? + if (!@$filesystem->is_readable($value)) { + $this->_throw($file, self::NOT_FOUND); + return false; + } + + if ($file !== null) { + $info['extension'] = substr($file['name'], strrpos($file['name'], '.') + 1); + } else { + $info = @$filesystem->pathinfo($value); + } + + $extensions = $this->getExtension(true); + + if ($this->_case and (in_array($info['extension'], $extensions))) + return true; + + if (!$this->_case) { + foreach ($extensions as $extension) { + if (strtolower($extension) == strtolower($info['extension'])) { + return true; + } + } + } + + $this->_throw($file, self::FALSE_EXTENSION); + return false; + } +} diff --git a/tests/application/modules/admin/controllers/FileManagerControllerTest.php b/tests/application/modules/admin/controllers/FileManagerControllerTest.php index b82ea59064f7ee823b7145273d34ef046ef2efe7..26ef5578a5f71c24e99e9132b46306ff615088f9 100644 --- a/tests/application/modules/admin/controllers/FileManagerControllerTest.php +++ b/tests/application/modules/admin/controllers/FileManagerControllerTest.php @@ -105,6 +105,10 @@ abstract class FileManagerControllerTestCase extends Admin_AbstractControllerTes ['id' => 'RESIZABLE_EXTENSIONS', 'valeur' => 'jpg;jpeg;png']); + $this->fixture('Class_AdminVar', + ['id' => 'ALLOWED_FILES_EXTENSIONS_FOR_IMPORT', + 'valeur' => 'css;jpg;jpeg;png']); + $this->_userfiles_dir = (new Class_FileManager) ->setId('userfiles') ->setDir(true) @@ -712,8 +716,15 @@ class FileManagerControllerImportCssDispatchTest extends FileManagerControllerTe 'size' => 126976, 'tmp_name' => 'xxxxx/php8iuxxajl']]; - ZendAfi_Form_Admin_FileManager_Import::setTransferAdapter((new FileManagerController_Mock_TransferAdapter())->setFiles($files)); - ZendAfi_Form_Admin_FileManager_Import::setValidators(null); + $adapter = (new FileManagerController_Mock_TransferAdapter())->setFiles($files); + ZendAfi_Form_Admin_FileManager_Import::setTransferAdapter($adapter); + + $filesystem = $this + ->mock() + ->whenCalled('is_readable')->with('xxxxx/php8iuxxajl')->answers(true) + ->whenCalled('filesize')->with('xxxxx/php8iuxxajl')->answers(126976); + + ZendAfi_Validate_FileExtension::setFileSystem($filesystem); $imported_file = (new Class_FileManager) ->setId('userfiles/stylesheet.css') @@ -738,6 +749,14 @@ class FileManagerControllerImportCssDispatchTest extends FileManagerControllerTe } + public function tearDown() { + ZendAfi_Validate_FileExtension::setFileSystem(null); + Class_Php_Config::setInstance(null); + + parent::tearDown(); + } + + /** @test */ public function dispatchFormShouldContainsInputFile() { $this->dispatch('/admin/file-manager/import?into=userfiles', true); @@ -754,7 +773,8 @@ class FileManagerControllerImportCssDispatchTest extends FileManagerControllerTe /** @test */ public function postDispatchImportShouldRedirectWithSucces() { - $this->postDispatch('/admin/file-manager/import?into=userfiles', ['file' => 'stylesheet.css']); + $this->postDispatch('/admin/file-manager/import?into=userfiles', + ['file' => 'stylesheet.css']); $this->assertFlashMessengerContentContains('Fichier "userfiles/stylesheet.css" téléversé.'); $this->assertRedirectTo('/admin/file-manager/index?browser=userfiles%2Fstylesheet.css'); } @@ -767,6 +787,20 @@ class FileManagerControllerImportCssDispatchTest extends FileManagerControllerTe $this->assertFlashMessengerContentContains('Fichier "userfiles/stylesheet.css" téléversé.'); $this->assertRedirectTo('/admin/file-manager/index?browser=userfiles%2Fstylesheet.css&selectable_extensions=css'); } + + + /** @test */ + public function postDispatchImportFileSizeTooBigShouldDisplayErrorWithMaxFileSize() { + ZendAfi_Validate_FileExtension::setFileSystem($this->mock() + ->whenCalled('is_readable') + ->answers(false)); + + Class_Php_Config::setInstance($this->mock() + ->whenCalled('getMaxFileSize')->answers('10M')); + $this->postDispatch('/admin/file-manager/import?into=userfiles&selectable_extensions=css', + ['file' => 'stylesheet.css']); + $this->assertXPathContentContains('//ul[@class="errors"]/li', '10M'); + } }