From 9d65f5d7c152e29ca12bd1f0ddbd12d8998ef6d2 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 27 Dec 2024 14:00:12 +0900 Subject: [PATCH] phpunit script update, SmartyExtended allow load of plugins - phpunit has better options set for testdox/php version - SmartyExtended has logger class as option (argument 2) and options - SmartyExtneded can via option set html escape and load of plugins - plugin array is set of - file: path to plugin file - type: what type this is - tag: tag name - callable: the callable for the tag name - will throw exceptions on plugin load - for all other things will set warning only and skip read - fix the Smarty call with the logger option - fix password test for PHP 8.4 password hash change *IMPORTANT* SmartyExtended($l10n, $logger, $cache_id, $compile_id) The second argument is now the Logger class, this MUST be updated for all calls --- 4dev/checking/phan.sh | 4 +- 4dev/checking/phpstan.sh | 4 +- 4dev/checking/phpunit.sh | 53 ++++---- .../Security/CoreLibsSecurityPasswordTest.php | 14 ++ www/admin/class_test.password.php | 5 + www/admin/class_test.smarty.php | 1 + www/includes/admin_header.php | 2 +- www/lib/CoreLibs/Admin/EditBase.php | 3 +- www/lib/CoreLibs/Template/SmartyExtend.php | 120 ++++++++++++++++-- 9 files changed, 157 insertions(+), 49 deletions(-) diff --git a/4dev/checking/phan.sh b/4dev/checking/phan.sh index 5c788c16..856a20c3 100755 --- a/4dev/checking/phan.sh +++ b/4dev/checking/phan.sh @@ -1,5 +1,5 @@ base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/"; # must be run in ${base} -cd $base; +cd $base || exit; ${base}tools/phan --progress-bar -C --analyze-twice; -cd ~; +cd ~ || exit; diff --git a/4dev/checking/phpstan.sh b/4dev/checking/phpstan.sh index 079bbf6c..00481ad8 100755 --- a/4dev/checking/phpstan.sh +++ b/4dev/checking/phpstan.sh @@ -1,5 +1,5 @@ base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/"; # must be run in ${base} -cd $base; +cd $base || exit; ${base}tools/phpstan; -cd ~; +cd ~ || exit; diff --git a/4dev/checking/phpunit.sh b/4dev/checking/phpunit.sh index 0fbf93d8..b0295408 100755 --- a/4dev/checking/phpunit.sh +++ b/4dev/checking/phpunit.sh @@ -3,47 +3,44 @@ base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/"; # -c phpunit.xml # --testdox -# call with "t" to give verbose testdox output +# call with "-tt" to give verbose testdox output # SUPPORTED: https://www.php.net/supported-versions.php -# call with php version number to force a certain php version +# call with -p to force a certain php version opt_testdox=""; -if [ "${1}" = "t" ] || [ "${2}" = "t" ]; then - opt_testdox="--testdox"; -fi; php_bin=""; -if [ -n "${1}" ]; then +while [ -n "${1-}" ]; do case "${1}" in - # "7.3") php_bin="/usr/bin/php7.3 "; ;; - # "7.4") php_bin="/usr/bin/php7.4 "; ;; - # "8.0") php_bin="/usr/bin/php8.0 "; ;; - # "8.1") php_bin="/usr/bin/php8.1 "; ;; - "8.2") php_bin="/usr/bin/php8.2 "; ;; - "8.3") php_bin="/usr/bin/php8.4 "; ;; - *) echo "Not support PHP: ${1}"; exit; ;; - esac; -fi; -if [ -n "${2}" ] && [ -z "${php_bin}" ]; then - case "${2}" in - # "7.3") php_bin="/usr/bin/php7.3 "; ;; - # "7.4") php_bin="/usr/bin/php7.4 "; ;; - # "8.0") php_bin="/usr/bin/php8.0 "; ;; - # "8.1") php_bin="/usr/bin/php8.1 "; ;; - "8.2") php_bin="/usr/bin/php8.2 "; ;; - "8.3") php_bin="/usr/bin/php8.3 "; ;; - *) echo "Not support PHP: ${1}"; exit; ;; - esac; + -t | --testdox) + opt_testdox="--testdox"; + ;; + -p | --php) + php_bin="/usr/bin/php${2-}"; + shift + ;; + # invalid option + -?*) + error "[!] Unknown option: '$1'." + ;; + esac + shift; +done; + +if [ ! -f "${php_bin}" ]; then + echo "Set php ${php_bin} does not exist"; + exit; fi; +php_bin="${php_bin} "; # Note 4dev/tests/bootstrap.php has to be set as bootstrap file in phpunit.xml phpunit_call="${php_bin}${base}vendor/bin/phpunit ${opt_testdox} -c ${base}phpunit.xml ${base}4dev/tests/"; ${phpunit_call}; -if [ ! -z "${php_bin}" ]; then - echo "CALLED WITH PHP: ${php_bin}"$(${php_bin} --version); +if [ -n "${php_bin}" ]; then + echo "CALLED WITH PHP: ${php_bin}$(${php_bin} --version)"; else - echo "Default PHP used: "$(php --version); + echo "Default PHP used: $(php --version)"; fi; # __END__ diff --git a/4dev/tests/Security/CoreLibsSecurityPasswordTest.php b/4dev/tests/Security/CoreLibsSecurityPasswordTest.php index 80adec64..7904997a 100644 --- a/4dev/tests/Security/CoreLibsSecurityPasswordTest.php +++ b/4dev/tests/Security/CoreLibsSecurityPasswordTest.php @@ -13,6 +13,11 @@ use PHPUnit\Framework\TestCase; */ final class CoreLibsSecurityPasswordTest extends TestCase { + /** + * Undocumented function + * + * @return array + */ public function passwordProvider(): array { return [ @@ -21,6 +26,11 @@ final class CoreLibsSecurityPasswordTest extends TestCase ]; } + /** + * Note: we need different hash types for PHP versions + * + * @return array + */ public function passwordRehashProvider(): array { return [ @@ -63,6 +73,10 @@ final class CoreLibsSecurityPasswordTest extends TestCase */ public function testPasswordRehashCheck(string $input, bool $expected): void { + // in PHP 8.4 the length is $12 + if (PHP_VERSION_ID > 80400) { + $input = str_replace('$2y$10$', '$2y$12$', $input); + } $this->assertEquals( $expected, \CoreLibs\Security\Password::passwordRehashCheck($input) diff --git a/www/admin/class_test.password.php b/www/admin/class_test.password.php index b1aa9de1..7a9a4556 100644 --- a/www/admin/class_test.password.php +++ b/www/admin/class_test.password.php @@ -51,6 +51,11 @@ print "PASSWORD REHASH: " . (string)$password_class::passwordRehashCheck($enc_pa // direct static print "S::PASSWORD VERFIY: " . (string)PwdChk::passwordVerify($password, $enc_password) . "
"; +$rehash_test = '$2y$10$EgWJ2WE73DWi.hIyFRCdpejLXTvHbmTK3LEOclO1tAvXAXUNuUS4W'; +if (PwdChk::passwordRehashCheck($rehash_test)) { + print "Bad password
"; +} + print ""; // __END__ diff --git a/www/admin/class_test.smarty.php b/www/admin/class_test.smarty.php index 72996954..49e95ae9 100644 --- a/www/admin/class_test.smarty.php +++ b/www/admin/class_test.smarty.php @@ -35,6 +35,7 @@ $l10n = new \CoreLibs\Language\L10n( ); $smarty = new CoreLibs\Template\SmartyExtend( $l10n, + $log, CACHE_ID, COMPILE_ID, ); diff --git a/www/includes/admin_header.php b/www/includes/admin_header.php index d6176102..9def1ebe 100644 --- a/www/includes/admin_header.php +++ b/www/includes/admin_header.php @@ -91,7 +91,7 @@ $l10n = new \CoreLibs\Language\L10n( ); // create smarty object -$smarty = new \CoreLibs\Template\SmartyExtend($l10n, CACHE_ID, COMPILE_ID); +$smarty = new \CoreLibs\Template\SmartyExtend($l10n, $log, CACHE_ID, COMPILE_ID); // create new Backend class with db and loger attached $cms = new \CoreLibs\Admin\Backend($db, $log, $session, $l10n, DEFAULT_ACL_LEVEL); // the menu show flag (what menu to show) diff --git a/www/lib/CoreLibs/Admin/EditBase.php b/www/lib/CoreLibs/Admin/EditBase.php index 27d4a162..93d589d7 100644 --- a/www/lib/CoreLibs/Admin/EditBase.php +++ b/www/lib/CoreLibs/Admin/EditBase.php @@ -538,8 +538,7 @@ class EditBase * builds the smarty content and runs smarty display output * * @return void - * @throws Exception - * @throws SmartyException + * @throws \Smarty\Exception */ public function editBaseRun( ?string $template_dir = null, diff --git a/www/lib/CoreLibs/Template/SmartyExtend.php b/www/lib/CoreLibs/Template/SmartyExtend.php index 5b5bfd5d..54b024f6 100644 --- a/www/lib/CoreLibs/Template/SmartyExtend.php +++ b/www/lib/CoreLibs/Template/SmartyExtend.php @@ -19,12 +19,13 @@ declare(strict_types=1); namespace CoreLibs\Template; -// leading slash if this is in lib\Smarty class SmartyExtend extends \Smarty\Smarty { // internal translation engine - /** @var \CoreLibs\Language\L10n */ + /** @var \CoreLibs\Language\L10n language class */ public \CoreLibs\Language\L10n $l10n; + /** @var \CoreLibs\Logging\Logging $log logging class */ + public \CoreLibs\Logging\Logging $log; // lang & encoding /** @var string */ @@ -157,14 +158,18 @@ class SmartyExtend extends \Smarty\Smarty * calls L10 for pass on internaly in smarty * also registers the getvar caller plugin * - * @param \CoreLibs\Language\L10n $l10n l10n language class - * @param string|null $cache_id - * @param string|null $compile_id + * @param \CoreLibs\Language\L10n $l10n l10n language class + * @param \CoreLibs\Logging\Logging $log Logger class + * @param string|null $cache_id [default=null] + * @param string|null $compile_id [default=null] + * @param array $options [default=[]] */ public function __construct( \CoreLibs\Language\L10n $l10n, + \CoreLibs\Logging\Logging $log, ?string $cache_id = null, - ?string $compile_id = null + ?string $compile_id = null, + array $options = [] ) { // trigger deprecation if ( @@ -177,14 +182,33 @@ class SmartyExtend extends \Smarty\Smarty E_USER_DEPRECATED ); } - // set variables (to be deprecated) - $cache_id = $cache_id ?? - (defined('CACHE_ID') ? CACHE_ID : ''); - $compile_id = $compile_id ?? - (defined('COMPILE_ID') ? COMPILE_ID : ''); + // set variables from global constants (deprecated) + if ($cache_id === null && defined('CACHE_ID')) { + trigger_error( + 'SmartyExtended: No cache_id set and CACHE_ID constant set, this is deprecated', + E_USER_DEPRECATED + ); + $cache_id = CACHE_ID; + } + if ($compile_id === null && defined('COMPILE_ID')) { + trigger_error( + 'SmartyExtended: No compile_id set and COMPILE_ID constant set, this is deprecated', + E_USER_DEPRECATED + ); + $compile_id = COMPILE_ID; + } + if (empty($cache_id)) { + throw new \BadMethodCallException('cache_id parameter is not set'); + } + if (empty($compile_id)) { + throw new \BadMethodCallException('compile_id parameter is not set'); + } + // call basic smarty - // or Smarty::__construct(); parent::__construct(); + + $this->log = $log; + // init lang $this->l10n = $l10n; // parse and read, legacy stuff @@ -194,7 +218,6 @@ class SmartyExtend extends \Smarty\Smarty $this->lang_short = $locale['lang_short']; $this->domain = $locale['domain']; $this->lang_dir = $locale['path']; - // opt load functions so we can use legacy init for smarty run perhaps \CoreLibs\Language\L10n::loadFunctions(); _setlocale(LC_MESSAGES, $locale['locale']); @@ -203,7 +226,6 @@ class SmartyExtend extends \Smarty\Smarty _bind_textdomain_codeset($this->domain, $this->encoding); // register smarty variable - // $this->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']); $this->registerPlugin(self::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']); $this->page_name = \CoreLibs\Get\System::getPageName(); @@ -211,6 +233,76 @@ class SmartyExtend extends \Smarty\Smarty // set internal settings $this->CACHE_ID = $cache_id; $this->COMPILE_ID = $compile_id; + // set options + $this->setOptions($options); + } + + /** + * set options + * + * @param array $options + * @return void + */ + private function setOptions(array $options): void + { + // set escape html if option is set + if (!empty($options['escape_html'])) { + $this->setEscapeHtml(true); + } + // load plugins + // plugin array: + // 'file': string, path to plugin content to load + // 'type': a valid smarty type see Smarty PLUGIN_ constants for correct names + // 'tag': the smarty tag + // 'callback': the function to call in 'file' + if (!empty($options['plugins'])) { + foreach ($options['plugins'] as $plugin) { + // file is readable + if ( + empty($plugin['file']) || + !is_file($plugin['file']) || + !is_readable($plugin['file']) + ) { + $this->log->warning('SmartyExtended plugin load failed, file not accessable', [ + 'plugin' => $plugin, + ]); + continue; + } + // tag is alphanumeric + if (!preg_match("/^\w+$/", $plugin['tag'] ?? '')) { + $this->log->warning('SmartyExtended plugin load failed, invalid tag', [ + 'plugin' => $plugin, + ]); + continue; + } + // callback is alphanumeric + if (!preg_match("/^\w+$/", $plugin['callback'] ?? '')) { + $this->log->warning('SmartyExtended plugin load failed, invalid callback', [ + 'plugin' => $plugin, + ]); + continue; + } + try { + new \ReflectionClassConstant($this, $plugin['type']); + } catch (\ReflectionException $e) { + $this->log->error('SmartyExtended plugin load failed, type is not valid', [ + 'message' => $e->getMessage(), + 'plugin' => $plugin, + ]); + continue; + } + try { + require $plugin['file']; + $this->registerPlugin($plugin['type'], $plugin['tag'], $plugin['callback']); + } catch (\Smarty\Exception $e) { + $this->log->error('SmartyExtended plugin load failed with exception', [ + 'message' => $e->getMessage(), + 'plugin' => $plugin, + ]); + continue; + } + } + } } /**