Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ff8aa195b | ||
|
|
f176d12a1e | ||
|
|
f974b15f78 | ||
|
|
91fad09367 | ||
|
|
e8fe1feda5 | ||
|
|
23fd78e5c8 | ||
|
|
6cdede2997 | ||
|
|
ace02b14d8 | ||
|
|
58e916d314 | ||
|
|
4f6d85f4da | ||
|
|
cd45590a72 | ||
|
|
4d42da201c | ||
|
|
e310cb626a | ||
|
|
c04c71d755 | ||
|
|
9fc40a6629 | ||
|
|
6362e7f2f0 | ||
|
|
50dfc10d31 | ||
|
|
24077e483f | ||
|
|
6585c6bfef | ||
|
|
f180046283 | ||
|
|
b64d0ce5f0 | ||
|
|
bab8460f80 | ||
|
|
a092217201 | ||
|
|
e286d7f913 | ||
|
|
e148a39902 | ||
|
|
b7d5a79c3a | ||
|
|
9f8a86b4b0 |
@@ -25,7 +25,7 @@
|
||||
"phpstan/phpdoc-parser": "^2.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||
"phan/phan": "^5.4",
|
||||
"egrajp/smarty-extended": "^4.3",
|
||||
"egrajp/smarty-extended": "^5.4",
|
||||
"gullevek/dotenv": "dev-master",
|
||||
"phpunit/phpunit": "^9"
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
9.24.1
|
||||
9.26.7
|
||||
|
||||
@@ -1418,6 +1418,7 @@ class Login
|
||||
'additional_acl' => Json::jsonConvertToArray($res['additional_acl']),
|
||||
'data' => $ea_data
|
||||
];
|
||||
// LEGACY LOOKUP
|
||||
$unit_access_eaid[$res['edit_access_id']] = [
|
||||
'cuid' => $res['cuid'],
|
||||
];
|
||||
@@ -1477,6 +1478,8 @@ class Login
|
||||
// username (login), group name
|
||||
$this->acl['user_name'] = $_SESSION['LOGIN_USER_NAME'];
|
||||
$this->acl['group_name'] = $_SESSION['LOGIN_GROUP_NAME'];
|
||||
// DEPRECATED
|
||||
$this->acl['euid'] = $_SESSION['LOGIN_EUID'];
|
||||
// edit user cuid
|
||||
$this->acl['eucuid'] = $_SESSION['LOGIN_EUCUID'];
|
||||
$this->acl['eucuuid'] = $_SESSION['LOGIN_EUCUUID'];
|
||||
@@ -1529,7 +1532,7 @@ class Login
|
||||
$this->acl['page'] = $_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name];
|
||||
}
|
||||
|
||||
$this->acl['unit_id'] = null;
|
||||
$this->acl['unit_cuid'] = null;
|
||||
$this->acl['unit_name'] = null;
|
||||
$this->acl['unit_uid'] = null;
|
||||
$this->acl['unit'] = [];
|
||||
@@ -1552,8 +1555,10 @@ class Login
|
||||
$this->acl['unit_legacy'][$unit['id']] = $this->acl['unit'][$ea_cuid];
|
||||
// detail name/level set
|
||||
$this->acl['unit_detail'][$ea_cuid] = [
|
||||
'id' => $unit['id'],
|
||||
'name' => $unit['name'],
|
||||
'uid' => $unit['uid'],
|
||||
'cuuid' => $unit['cuuid'],
|
||||
'level' => $this->default_acl_list[$this->acl['unit'][$ea_cuid]]['name'] ?? -1,
|
||||
'default' => $unit['default'],
|
||||
'data' => $unit['data'],
|
||||
@@ -3212,7 +3217,7 @@ HTML;
|
||||
* @return int|null same edit access id if ok
|
||||
* or the default edit access id
|
||||
* if given one is not valid
|
||||
* @deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessValidCuid()
|
||||
* @#deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessValidCuid()
|
||||
*/
|
||||
public function loginCheckEditAccessId(?int $edit_access_id): ?int
|
||||
{
|
||||
@@ -3277,6 +3282,34 @@ HTML;
|
||||
return (int)$_SESSION['LOGIN_UNIT_CUID'][$uid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy lookup for edit access id to cuid
|
||||
*
|
||||
* @param int $id edit access id PK
|
||||
* @return string|false edit access cuid or false if not found
|
||||
*/
|
||||
public function loginGetEditAccessCuidFromId(int $id): string|false
|
||||
{
|
||||
if (!isset($_SESSION['LOGIN_UNIT_LEGACY'][$id])) {
|
||||
return false;
|
||||
}
|
||||
return (string)$_SESSION['LOGIN_UNIT_LEGACY'][$id]['cuid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a Legacy lookup from the edit access id to cuid for further lookups in the normal list
|
||||
*
|
||||
* @param string $cuid edit access cuid
|
||||
* @return int|false false on not found or edit access id PK
|
||||
*/
|
||||
public function loginGetEditAccessIdFromCuid(string $cuid): int|false
|
||||
{
|
||||
if (!isset($_SESSION['LOGIN_UNIT'][$cuid])) {
|
||||
return false;
|
||||
}
|
||||
return $_SESSION['LOGIN_UNIT'][$cuid]['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if admin flag is set
|
||||
*
|
||||
|
||||
@@ -289,7 +289,7 @@ class Backend
|
||||
* JSON, STRING/SERIEAL, BINARY/BZIP or ZLIB
|
||||
* @param string|null $db_schema [default=null] override target schema
|
||||
* @return void
|
||||
* @deprecated Use $login->writeLog() and set action_set from ->adbGetActionSet()
|
||||
* @deprecated Use $login->writeLog($event, $data, action_set:$cms->adbGetActionSet(), write_type:$write_type)
|
||||
*/
|
||||
public function adbEditLog(
|
||||
string $event = '',
|
||||
|
||||
@@ -14,9 +14,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Admin;
|
||||
|
||||
use Exception;
|
||||
use SmartyException;
|
||||
|
||||
class EditBase
|
||||
{
|
||||
/** @var array<mixed> */
|
||||
@@ -63,6 +60,7 @@ class EditBase
|
||||
// smarty template engine (extended Translation version)
|
||||
$this->smarty = new \CoreLibs\Template\SmartyExtend(
|
||||
$l10n,
|
||||
$log,
|
||||
$options['cache_id'] ?? '',
|
||||
$options['compile_id'] ?? '',
|
||||
);
|
||||
@@ -538,8 +536,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,
|
||||
|
||||
@@ -363,11 +363,12 @@ class Session
|
||||
* set the auto write close flag
|
||||
*
|
||||
* @param bool $flag
|
||||
* @return void
|
||||
* @return Session
|
||||
*/
|
||||
public function setAutoWriteClose(bool $flag): void
|
||||
public function setAutoWriteClose(bool $flag): Session
|
||||
{
|
||||
$this->auto_write_close = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -513,14 +514,15 @@ class Session
|
||||
*
|
||||
* @param string $name array name in _SESSION
|
||||
* @param mixed $value value to set (can be anything)
|
||||
* @return void
|
||||
* @return Session
|
||||
*/
|
||||
public function set(string $name, mixed $value): void
|
||||
public function set(string $name, mixed $value): Session
|
||||
{
|
||||
$this->checkValidSessionEntryKey($name);
|
||||
$this->restartSession();
|
||||
$_SESSION[$name] = $value;
|
||||
$this->closeSessionCall();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -577,16 +579,17 @@ class Session
|
||||
* unset one _SESSION entry 'name' if exists
|
||||
*
|
||||
* @param string $name _SESSION key name to remove
|
||||
* @return void
|
||||
* @return Session
|
||||
*/
|
||||
public function unset(string $name): void
|
||||
public function unset(string $name): Session
|
||||
{
|
||||
if (!isset($_SESSION[$name])) {
|
||||
return;
|
||||
return $this;
|
||||
}
|
||||
$this->restartSession();
|
||||
unset($_SESSION[$name]);
|
||||
$this->closeSessionCall();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3141,6 +3141,7 @@ class IO
|
||||
'pk_name' => '',
|
||||
'count' => 0,
|
||||
'query' => '',
|
||||
'query_raw' => $query,
|
||||
'result' => null,
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
@@ -3237,11 +3238,12 @@ class IO
|
||||
}
|
||||
} else {
|
||||
// if we try to use the same statement name for a differnt query, error abort
|
||||
if ($this->prepare_cursor[$stm_name]['query'] != $query) {
|
||||
if ($this->prepare_cursor[$stm_name]['query_raw'] != $query) {
|
||||
// thrown error
|
||||
$this->__dbError(26, false, context: [
|
||||
'statement_name' => $stm_name,
|
||||
'prepared_query' => $this->prepare_cursor[$stm_name]['query'],
|
||||
'prepared_query_raw' => $this->prepare_cursor[$stm_name]['query_raw'],
|
||||
'query' => $query,
|
||||
'pk_name' => $pk_name,
|
||||
]);
|
||||
@@ -4364,6 +4366,37 @@ class IO
|
||||
return $this->prepare_cursor[$stm_name][$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a prepared query eixsts
|
||||
*
|
||||
* @param string $stm_name Statement to check
|
||||
* @param string $query [default=''] If set then query must also match
|
||||
* @return false|int<0,2> False on missing stm_name
|
||||
* 0: ok, 1: stm_name matchin, 2: stm_name and query matching
|
||||
*/
|
||||
public function dbPreparedCursorStatus(string $stm_name, string $query = ''): false|int
|
||||
{
|
||||
if (empty($stm_name)) {
|
||||
$this->__dbError(
|
||||
101,
|
||||
false,
|
||||
'No statement name given'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// does not exist
|
||||
$return_value = 0;
|
||||
if (!empty($this->prepare_cursor[$stm_name]['query_raw'])) {
|
||||
// statement name eixts
|
||||
$return_value = 1;
|
||||
if ($this->prepare_cursor[$stm_name]['query_raw'] == $query) {
|
||||
// query also matches
|
||||
$return_value = 2;
|
||||
}
|
||||
}
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
// ***************************
|
||||
// ERROR AND WARNING DATA
|
||||
// ***************************
|
||||
|
||||
@@ -26,7 +26,9 @@ class ConvertPlaceholder
|
||||
. '&&|' // array overlap
|
||||
. '\-\|\-|' // range overlap for array
|
||||
. '[^-]-{1}|' // single -, used in JSON too
|
||||
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-'; //JSON searches, Array searchs, etc
|
||||
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-|' // JSON searches, Array searchs, etc
|
||||
. 'THEN|ELSE' // command parts (CASE)
|
||||
;
|
||||
/** @var string the main regex including the pattern query split */
|
||||
private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:' . self::PATTERN_QUERY_SPLIT . ')\s*';
|
||||
/** @var string comment regex
|
||||
|
||||
95
src/DeprecatedHelper/Deprecated84.php
Normal file
95
src/DeprecatedHelper/Deprecated84.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2025/1/17
|
||||
* DESCRIPTION:
|
||||
* Deprecated helper for fputcsv
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DeprecatedHelper;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Deprecated84
|
||||
{
|
||||
/**
|
||||
* This is a wrapper for fputcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.fputcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param mixed $stream
|
||||
* @param array<mixed> $fields
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @param string $eol
|
||||
* @return int|false
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fputcsv(
|
||||
mixed $stream,
|
||||
array $fields,
|
||||
string $separator = ",",
|
||||
string $enclosure = '"',
|
||||
string $escape = '', // set to empty for future compatible
|
||||
string $eol = PHP_EOL
|
||||
): int | false {
|
||||
if (!is_resource($stream)) {
|
||||
throw new \InvalidArgumentException("fputcsv stream parameter must be a resrouce");
|
||||
}
|
||||
return fputcsv($stream, $fields, $separator, $enclosure, $escape, $eol);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper for fgetcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.fgetcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param mixed $stream
|
||||
* @param null|int<0,max> $length
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @return array<mixed>|false
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fgetcsv(
|
||||
mixed $stream,
|
||||
?int $length = null,
|
||||
string $separator = ',',
|
||||
string $enclosure = '"',
|
||||
string $escape = '' // set to empty for future compatible
|
||||
): array | false {
|
||||
if (!is_resource($stream)) {
|
||||
throw new \InvalidArgumentException("fgetcsv stream parameter must be a resrouce");
|
||||
}
|
||||
return fgetcsv($stream, $length, $separator, $enclosure, $escape);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper for str_getcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.str-getcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @return array<mixed>
|
||||
*/
|
||||
// phpcs:disable PSR1.Methods.CamelCapsMethodName
|
||||
public static function str_getcsv(
|
||||
string $string,
|
||||
string $separator = ",",
|
||||
string $enclosure = '"',
|
||||
string $escape = '' // set to empty for future compatible
|
||||
): array {
|
||||
return str_getcsv($string, $separator, $enclosure, $escape);
|
||||
}
|
||||
// phpcs:enable PSR1.Methods.CamelCapsMethodName
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -30,6 +30,10 @@ class Logging
|
||||
{
|
||||
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
|
||||
public const MIN_LOG_MAX_FILESIZE = 10 * 1024;
|
||||
/** @var string log file extension, not changeable */
|
||||
private const LOG_FILE_NAME_EXT = "log";
|
||||
/** @var string log file block separator, not changeable */
|
||||
private const LOG_FILE_BLOCK_SEPARATOR = '.';
|
||||
|
||||
// NOTE: the second party array{} hs some errors
|
||||
/** @var array<string,array<string,string|bool|Level>>|array{string:array{type:string,type_info?:string,mandatory:true,alias?:string,default:string|bool|Level,deprecated:bool,use?:string}} */
|
||||
@@ -104,8 +108,6 @@ class Logging
|
||||
private string $log_folder = '';
|
||||
/** @var string a alphanumeric name that has to be set as global definition */
|
||||
private string $log_file_id = '';
|
||||
/** @var string log file name extension */
|
||||
private string $log_file_name_ext = 'log';
|
||||
/** @var string log file name with folder, for actual writing */
|
||||
private string $log_file_name = '';
|
||||
/** @var int set in bytes */
|
||||
@@ -431,7 +433,7 @@ class Logging
|
||||
private function buildLogFileName(Level $level, string $group_id = ''): string
|
||||
{
|
||||
// init base file path
|
||||
$fn = $this->log_print_file . '.' . $this->log_file_name_ext;
|
||||
$fn = $this->log_print_file . '.' . self::LOG_FILE_NAME_EXT;
|
||||
// log ID prefix settings, if not valid, replace with empty
|
||||
if (!empty($this->log_file_id)) {
|
||||
$rpl_string = $this->log_file_id;
|
||||
@@ -440,14 +442,15 @@ class Logging
|
||||
}
|
||||
$fn = str_replace('{LOGID}', $rpl_string, $fn); // log id (like a log file prefix)
|
||||
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_level) ? '' :
|
||||
'_' . $level->getName();
|
||||
$rpl_string = $this->getLogFlag(Flag::per_level) ?
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . $level->getName() :
|
||||
'';
|
||||
$fn = str_replace('{LEVEL}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// write per level
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_group) ? '' :
|
||||
$rpl_string = $this->getLogFlag(Flag::per_group) ?
|
||||
// normalize level, replace all non alphanumeric characters with -
|
||||
'_' . (
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . (
|
||||
// if return is only - then set error string
|
||||
preg_match(
|
||||
"/^-+$/",
|
||||
@@ -455,25 +458,29 @@ class Logging
|
||||
) ?
|
||||
'INVALID-LEVEL-STRING' :
|
||||
$level_string
|
||||
);
|
||||
) :
|
||||
'';
|
||||
$fn = str_replace('{GROUP}', $rpl_string, $fn); // create output filename
|
||||
// set per class, but don't use get_class as we will only get self
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_class) ? '' : '_'
|
||||
// set sub class settings
|
||||
. str_replace('\\', '-', Support::getCallerTopLevelClass());
|
||||
$rpl_string = $this->getLogFlag(Flag::per_class) ?
|
||||
// set sub class settings
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . str_replace('\\', '-', Support::getCallerTopLevelClass()) :
|
||||
'';
|
||||
$fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// if request to write to one file
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_page) ?
|
||||
'' :
|
||||
'_' . System::getPageName(System::NO_EXTENSION);
|
||||
$rpl_string = $this->getLogFlag(Flag::per_page) ?
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . System::getPageName(System::NO_EXTENSION) :
|
||||
'';
|
||||
$fn = str_replace('{PAGENAME}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// if run id, we auto add ymd, so we ignore the log file date
|
||||
if ($this->getLogFlag(Flag::per_run)) {
|
||||
$rpl_string = '_' . $this->getLogUniqueId(); // add 8 char unique string
|
||||
// add 8 char unique string and date block with time
|
||||
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogUniqueId();
|
||||
} elseif ($this->getLogFlag(Flag::per_date)) {
|
||||
$rpl_string = '_' . $this->getLogDate(); // add date to file
|
||||
// add date to file
|
||||
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogDate();
|
||||
} else {
|
||||
$rpl_string = '';
|
||||
}
|
||||
@@ -739,7 +746,10 @@ class Logging
|
||||
{
|
||||
if (empty($this->log_file_unique_id) || $override == true) {
|
||||
$this->log_file_unique_id =
|
||||
date('Y-m-d_His') . '_U_'
|
||||
date('Y-m-d_His')
|
||||
. self::LOG_FILE_BLOCK_SEPARATOR
|
||||
. 'U_'
|
||||
// this doesn't have to be unique for everything, just for this logging purpose
|
||||
. substr(hash(
|
||||
'sha1',
|
||||
random_bytes(63)
|
||||
|
||||
@@ -115,7 +115,7 @@ class AsymmetricAnonymousEncryption
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException key pair empty
|
||||
* @throws \UnexpectedValueException invalid hex key pair
|
||||
* @throws \UnexpectedValueException key pair not correct size
|
||||
* @throws \RangeException key pair not correct size
|
||||
*/
|
||||
private function createKeyPair(
|
||||
#[\SensitiveParameter]
|
||||
@@ -147,7 +147,7 @@ class AsymmetricAnonymousEncryption
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException public key empty
|
||||
* @throws \UnexpectedValueException invalid hex key
|
||||
* @throws \UnexpectedValueException invalid key length
|
||||
* @throws \RangeException invalid key length
|
||||
*/
|
||||
private function createPublicKey(?string $public_key): string
|
||||
{
|
||||
@@ -256,13 +256,13 @@ class AsymmetricAnonymousEncryption
|
||||
* sets the private key for encryption
|
||||
*
|
||||
* @param string $key_pair Key pair in hex
|
||||
* @return void
|
||||
* @return AsymmetricAnonymousEncryption
|
||||
* @throws \UnexpectedValueException key pair empty
|
||||
*/
|
||||
public function setKeyPair(
|
||||
#[\SensitiveParameter]
|
||||
string $key_pair
|
||||
) {
|
||||
): AsymmetricAnonymousEncryption {
|
||||
if (empty($key_pair)) {
|
||||
throw new \UnexpectedValueException('Key pair cannot be empty');
|
||||
}
|
||||
@@ -277,6 +277,7 @@ class AsymmetricAnonymousEncryption
|
||||
// check if valid
|
||||
$this->createPublicKey($this->public_key);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,10 +309,10 @@ class AsymmetricAnonymousEncryption
|
||||
* extract the public key from the key pair
|
||||
*
|
||||
* @param string $public_key Public Key in hex
|
||||
* @return void
|
||||
* @return AsymmetricAnonymousEncryption
|
||||
* @throws \UnexpectedValueException public key empty
|
||||
*/
|
||||
public function setPublicKey(string $public_key)
|
||||
public function setPublicKey(string $public_key): AsymmetricAnonymousEncryption
|
||||
{
|
||||
if (empty($public_key)) {
|
||||
throw new \UnexpectedValueException('Public key cannot be empty');
|
||||
@@ -320,6 +321,7 @@ class AsymmetricAnonymousEncryption
|
||||
$this->createPublicKey($public_key);
|
||||
$this->public_key = $public_key;
|
||||
sodium_memzero($public_key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,13 +209,13 @@ class SymmetricEncryption
|
||||
* set a new key for encryption
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
* @return SymmetricEncryption
|
||||
* @throws \UnexpectedValueException key cannot be empty
|
||||
*/
|
||||
public function setKey(
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
) {
|
||||
): SymmetricEncryption {
|
||||
if (empty($key)) {
|
||||
throw new \UnexpectedValueException('Key cannot be empty');
|
||||
}
|
||||
@@ -224,6 +224,7 @@ class SymmetricEncryption
|
||||
// set key
|
||||
$this->key = $key;
|
||||
sodium_memzero($key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,12 +19,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Template;
|
||||
|
||||
// leading slash if this is in lib\Smarty
|
||||
class SmartyExtend extends \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
|
||||
* 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<string,mixed> $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
|
||||
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
|
||||
$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
|
||||
_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,77 @@ class SmartyExtend extends \Smarty
|
||||
// set internal settings
|
||||
$this->CACHE_ID = $cache_id;
|
||||
$this->COMPILE_ID = $compile_id;
|
||||
// set options
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* set options
|
||||
*
|
||||
* @param array<string,mixed> $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 {
|
||||
/** @phan-suppress-next-line PhanNoopNew */
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1531,6 +1531,12 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_uid']),
|
||||
'Assert check access uid to cuid valid'
|
||||
);
|
||||
// - loginGetEditAccessCuidFromId
|
||||
$this->assertEquals(
|
||||
$expected['check_access_cuid'],
|
||||
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_id']),
|
||||
'Assert check access id to cuid valid'
|
||||
);
|
||||
// Deprecated
|
||||
// - loginCheckEditAccess
|
||||
$this->assertEquals(
|
||||
|
||||
@@ -3692,7 +3692,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function preparedProviderValue(): array
|
||||
public function providerDbGetPrepareCursorValue(): array
|
||||
{
|
||||
// 1: query (can be empty for do not set)
|
||||
// 2: stm name
|
||||
@@ -3736,7 +3736,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
* test return prepare cursor errors
|
||||
*
|
||||
* @covers ::dbGetPrepareCursorValue
|
||||
* @dataProvider preparedProviderValue
|
||||
* @dataProvider providerDbGetPrepareCursorValue
|
||||
* @testdox prepared query $stm_name with $key expect error id $error_id [$_dataName]
|
||||
*
|
||||
* @param string $query
|
||||
@@ -3769,6 +3769,94 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerDbPreparedCursorStatus(): array
|
||||
{
|
||||
return [
|
||||
'empty statement pararm' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_a',
|
||||
'check_stm_name' => '',
|
||||
'check_query' => '',
|
||||
'expected' => false
|
||||
],
|
||||
'different stm_name' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_b',
|
||||
'check_stm_name' => 'other_name',
|
||||
'check_query' => '',
|
||||
'expected' => 0
|
||||
],
|
||||
'same stm_name' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_c',
|
||||
'check_stm_name' => 'test_stm_c',
|
||||
'check_query' => '',
|
||||
'expected' => 1
|
||||
],
|
||||
'same stm_name and query' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_d',
|
||||
'check_stm_name' => 'test_stm_d',
|
||||
'check_query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'expected' => 2
|
||||
],
|
||||
'same stm_name and different query' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_e',
|
||||
'check_stm_name' => 'test_stm_e',
|
||||
'check_query' => 'SELECT row_int, uid, row_int FROM table_with_primary_key',
|
||||
'expected' => 1
|
||||
],
|
||||
'insert query test' => [
|
||||
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
|
||||
'stm_name' => 'test_stm_f',
|
||||
'check_stm_name' => 'test_stm_f',
|
||||
'check_query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
|
||||
'expected' => 2
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test cursor status for prepared statement
|
||||
*
|
||||
* @covers ::dbPreparedCursorStatus
|
||||
* @dataProvider providerDbPreparedCursorStatus
|
||||
* @testdox Check prepared $stm_name ($check_stm_name) status is $expected [$_dataName]
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $stm_name
|
||||
* @param string $check_stm_name
|
||||
* @param string $check_query
|
||||
* @param bool|int $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testDbPreparedCursorStatus(
|
||||
string $query,
|
||||
string $stm_name,
|
||||
string $check_stm_name,
|
||||
string $check_query,
|
||||
bool|int $expected
|
||||
): void {
|
||||
$db = new \CoreLibs\DB\IO(
|
||||
self::$db_config['valid'],
|
||||
self::$log
|
||||
);
|
||||
$db->dbPrepare($stm_name, $query);
|
||||
// $db->dbExecute($stm_name);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$db->dbPreparedCursorStatus($check_stm_name, $check_query),
|
||||
'check prepared stement cursor status'
|
||||
);
|
||||
unset($db);
|
||||
}
|
||||
|
||||
// - schema set/get tests
|
||||
// dbGetSchema, dbSetSchema
|
||||
|
||||
@@ -5196,6 +5284,27 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
SQL,
|
||||
'count' => 1,
|
||||
'convert' => false,
|
||||
],
|
||||
'update with case' => [
|
||||
'query' => <<<SQL
|
||||
UPDATE table_with_primary_key SET
|
||||
row_int = $1::INT,
|
||||
row_varchar = CASE WHEN row_int = 1 THEN $2 ELSE 'bar'::VARCHAR END
|
||||
WHERE
|
||||
row_varchar = $3
|
||||
SQL,
|
||||
'count' => 3,
|
||||
'convert' => false,
|
||||
],
|
||||
'select with case' => [
|
||||
'query' => <<<SQL
|
||||
SELECT row_int
|
||||
FROM table_with_primary_key
|
||||
WHERE
|
||||
row_varchar = CASE WHEN row_int = 1 THEN $1 ELSE $2 END
|
||||
SQL,
|
||||
'count' => 2,
|
||||
'convert' => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
}
|
||||
$per_run_id = $log->getLogUniqueId();
|
||||
$this->assertMatchesRegularExpression(
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
|
||||
$per_run_id,
|
||||
'assert per log run id 1st'
|
||||
);
|
||||
@@ -403,7 +403,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$log->setLogUniqueId(true);
|
||||
$per_run_id_2nd = $log->getLogUniqueId();
|
||||
$this->assertMatchesRegularExpression(
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
|
||||
$per_run_id_2nd,
|
||||
'assert per log run id 2nd'
|
||||
);
|
||||
@@ -824,13 +824,13 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
|
||||
$this->assertEquals(
|
||||
$log->getLogFile(),
|
||||
$log->getLogFileId() . '_DEBUG.log'
|
||||
$log->getLogFileId() . '.DEBUG.log'
|
||||
);
|
||||
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
|
||||
$this->assertTrue($log_ok, 'assert ::log (info) OK');
|
||||
$this->assertEquals(
|
||||
$log->getLogFile(),
|
||||
$log->getLogFileId() . '_INFO.log'
|
||||
$log->getLogFileId() . '.INFO.log'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user