Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a310fab3ee | ||
|
|
9914815285 | ||
|
|
969467fa15 | ||
|
|
f4dd78fff2 | ||
|
|
ba5e78e839 | ||
|
|
1a5ee2e16d | ||
|
|
e1d9985ec8 | ||
|
|
2316c151ac | ||
|
|
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 | ||
|
|
50e593789e | ||
|
|
4ee141f8df |
@@ -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.0
|
||||
9.28.0
|
||||
|
||||
@@ -423,14 +423,9 @@ class Login
|
||||
|
||||
// LOGOUT TARGET
|
||||
if (!isset($options['logout_target'])) {
|
||||
if (defined('LOGOUT_TARGET')) {
|
||||
trigger_error(
|
||||
'loginMainCall: LOGOUT_TARGET should not be used',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$options['logout_target'] = LOGOUT_TARGET;
|
||||
$this->logout_target = $options['logout_target'];
|
||||
}
|
||||
// defaults to ''
|
||||
$options['logout_target'] = '';
|
||||
$this->logout_target = $options['logout_target'];
|
||||
}
|
||||
|
||||
// *** PASSWORD SETTINGS
|
||||
@@ -1418,6 +1413,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 +1473,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 +1527,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,9 +1550,12 @@ 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,
|
||||
'level_number' => $this->acl['unit'][$ea_cuid],
|
||||
'default' => $unit['default'],
|
||||
'data' => $unit['data'],
|
||||
'additional_acl' => $unit['additional_acl']
|
||||
@@ -2138,10 +2139,10 @@ body {
|
||||
text-align: right;
|
||||
}
|
||||
input.login-input-text {
|
||||
font-size: 1.5em;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
button.login-button {
|
||||
font-size: 1.5em;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.login-visible {
|
||||
visibility: visible;
|
||||
@@ -2533,7 +2534,7 @@ HTML;
|
||||
$this->login_user_id,
|
||||
-1,
|
||||
$login_user_id_changed
|
||||
);
|
||||
) ?? '';
|
||||
// flag unclean input data
|
||||
if ($login_user_id_changed > 0) {
|
||||
$this->login_user_id_unclear = true;
|
||||
@@ -3212,7 +3213,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 +3278,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'] ?? '',
|
||||
);
|
||||
@@ -78,7 +76,7 @@ class EditBase
|
||||
);
|
||||
if ($this->form->mobile_phone) {
|
||||
echo "I am sorry, but this page cannot be viewed by a mobile phone";
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
// $this->log->debug('POST', $this->log->prAr($_POST));
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -103,11 +103,7 @@ class Basic
|
||||
'VIDEOS', 'DOCUMENTS', 'PDFS', 'BINARIES', 'ICONS', 'UPLOADS', 'CSV', 'JS',
|
||||
'CSS', 'TABLE_ARRAYS', 'SMARTY', 'LANG', 'CACHE', 'TMP', 'LOG', 'TEMPLATES',
|
||||
'TEMPLATES_C', 'DEFAULT_LANG', 'DEFAULT_ENCODING', 'DEFAULT_HASH',
|
||||
'DEFAULT_ACL_LEVEL', 'LOGOUT_TARGET', 'PASSWORD_CHANGE', 'AJAX_REQUEST_TYPE',
|
||||
'USE_PROTOTYPE', 'USE_SCRIPTACULOUS', 'USE_JQUERY', 'PAGE_WIDTH',
|
||||
'MASTER_TEMPLATE_NAME', 'PUBLIC_SCHEMA', 'TEST_SCHEMA', 'DEV_SCHEMA',
|
||||
'LIVE_SCHEMA', 'DB_CONFIG_NAME', 'DB_CONFIG', 'TARGET', 'DEBUG',
|
||||
'SHOW_ALL_ERRORS'
|
||||
'DB_CONFIG_NAME', 'DB_CONFIG', 'TARGET'
|
||||
] as $constant
|
||||
) {
|
||||
if (!defined($constant)) {
|
||||
|
||||
@@ -639,16 +639,26 @@ class DateTime
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $return_named return array type, false (default), true for named
|
||||
* @return array<mixed> 0/overall, 1/weekday, 2/weekend
|
||||
* @param bool $return_named [default=false] return array type, false (default), true for named
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{0:int,1:int,2:int,3:bool}|array{overall:int,weekday:int,weekend:int,reverse:bool}
|
||||
* 0/overall, 1/weekday, 2/weekend, 3/reverse
|
||||
*/
|
||||
public static function calcDaysInterval(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $return_named = false
|
||||
bool $return_named = false,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
// pos 0 all, pos 1 weekday, pos 2 weekend
|
||||
$days = [];
|
||||
$days = [
|
||||
0 => 0,
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => false,
|
||||
];
|
||||
// if anything invalid, return 0,0,0
|
||||
try {
|
||||
$start = new \DateTime($start_date);
|
||||
@@ -659,19 +669,30 @@ class DateTime
|
||||
'overall' => 0,
|
||||
'weekday' => 0,
|
||||
'weekend' => 0,
|
||||
'reverse' => false
|
||||
];
|
||||
} else {
|
||||
return [0, 0, 0];
|
||||
return $days;
|
||||
}
|
||||
}
|
||||
// so we include the last day too, we need to add +1 second in the time
|
||||
$end->setTime(0, 0, 1);
|
||||
// if end date before start date, only this will be filled
|
||||
$days[0] = $end->diff($start)->days;
|
||||
$days[1] = 0;
|
||||
$days[2] = 0;
|
||||
// if start is before end, switch dates and flag
|
||||
$days[3] = false;
|
||||
if ($start > $end) {
|
||||
$new_start = $end;
|
||||
$end = $start;
|
||||
$start = $new_start;
|
||||
$days[3] = true;
|
||||
}
|
||||
// get period for weekends/weekdays
|
||||
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end);
|
||||
$options = 0;
|
||||
if ($include_end_date) {
|
||||
$options |= \DatePeriod::INCLUDE_END_DATE;
|
||||
}
|
||||
if ($exclude_start_date) {
|
||||
$options |= \DatePeriod::EXCLUDE_START_DATE;
|
||||
}
|
||||
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end, $options);
|
||||
foreach ($period as $dt) {
|
||||
$curr = $dt->format('D');
|
||||
if ($curr == 'Sat' || $curr == 'Sun') {
|
||||
@@ -679,18 +700,80 @@ class DateTime
|
||||
} else {
|
||||
$days[1]++;
|
||||
}
|
||||
$days[0]++;
|
||||
}
|
||||
if ($return_named === true) {
|
||||
return [
|
||||
'overall' => $days[0],
|
||||
'weekday' => $days[1],
|
||||
'weekend' => $days[2],
|
||||
'reverse' => $days[3],
|
||||
];
|
||||
} else {
|
||||
return $days;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for calcDaysInterval with numeric return only
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{0:int,1:int,2:int,3:bool}
|
||||
*/
|
||||
public static function calcDaysIntervalNumIndex(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
$values = self::calcDaysInterval(
|
||||
$start_date,
|
||||
$end_date,
|
||||
false,
|
||||
$include_end_date,
|
||||
$exclude_start_date
|
||||
);
|
||||
return [
|
||||
$values[0] ?? 0,
|
||||
$values[1] ?? 0,
|
||||
$values[2] ?? 0,
|
||||
$values[3] ?? false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for calcDaysInterval with named return only
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{overall:int,weekday:int,weekend:int,reverse:bool}
|
||||
*/
|
||||
public static function calcDaysIntervalNamedIndex(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
$values = self::calcDaysInterval(
|
||||
$start_date,
|
||||
$end_date,
|
||||
true,
|
||||
$include_end_date,
|
||||
$exclude_start_date
|
||||
);
|
||||
return [
|
||||
'overall' => $values['overall'] ?? 0,
|
||||
'weekday' => $values['weekday'] ?? 0,
|
||||
'weekend' => $values['weekend'] ?? 0,
|
||||
'reverse' => $values['reverse'] ?? false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a weekend day (sat/sun) is in the given date range
|
||||
* Can have time too, but is not needed
|
||||
@@ -705,6 +788,13 @@ class DateTime
|
||||
): bool {
|
||||
$dd_start = new \DateTime($start_date);
|
||||
$dd_end = new \DateTime($end_date);
|
||||
// flip if start is after end
|
||||
if ($dd_start > $dd_end) {
|
||||
$new_start = $dd_end;
|
||||
$dd_end = $dd_start;
|
||||
$dd_start = $new_start;
|
||||
}
|
||||
// if start > end, flip
|
||||
if (
|
||||
// starts with a weekend
|
||||
$dd_start->format('N') >= 6 ||
|
||||
|
||||
@@ -10,9 +10,16 @@ namespace CoreLibs\Convert;
|
||||
|
||||
class Html
|
||||
{
|
||||
/** @var int */
|
||||
public const SELECTED = 0;
|
||||
/** @var int */
|
||||
public const CHECKED = 1;
|
||||
|
||||
// TODO: check for not valid htmlentites encoding
|
||||
// as of PHP 8.4: https://www.php.net/manual/en/function.htmlentities.php
|
||||
/** @#var array<string> */
|
||||
// public const VALID_HTMLENT_ENCODINGS = [];
|
||||
|
||||
/**
|
||||
* full wrapper for html entities
|
||||
*
|
||||
@@ -22,14 +29,19 @@ class Html
|
||||
* encodes in UTF-8
|
||||
* does not double encode
|
||||
*
|
||||
* @param mixed $string string to html encode
|
||||
* @param int $flags [default: ENT_QUOTES | ENT_HTML5]
|
||||
* @param mixed $string string to html encode
|
||||
* @param int $flags [default=ENT_QUOTES | ENT_HTML5]
|
||||
* @param string $encoding [default=UTF-8]
|
||||
* @return mixed if string, encoded, else as is (eg null)
|
||||
*/
|
||||
public static function htmlent(mixed $string, int $flags = ENT_QUOTES | ENT_HTML5): mixed
|
||||
{
|
||||
public static function htmlent(
|
||||
mixed $string,
|
||||
int $flags = ENT_QUOTES | ENT_HTML5,
|
||||
string $encoding = 'UTF-8'
|
||||
): mixed {
|
||||
if (is_string($string)) {
|
||||
return htmlentities($string, $flags, 'UTF-8', false);
|
||||
// if not a valid encoding this will throw a warning and use UTF-8
|
||||
return htmlentities($string, $flags, $encoding, false);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
@@ -37,7 +49,7 @@ class Html
|
||||
/**
|
||||
* strips out all line breaks or replaced with given string
|
||||
* @param string $string string
|
||||
* @param string $replace replace character, default ' '
|
||||
* @param string $replace [default=' '] replace character
|
||||
* @return string cleaned string without any line breaks
|
||||
*/
|
||||
public static function removeLB(string $string, string $replace = ' '): string
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -500,7 +500,7 @@ class IO
|
||||
die('<!-- Cannot load db functions class for: ' . $this->db_type . ' -->');
|
||||
}
|
||||
// write to internal one, once OK
|
||||
$this->db_functions = $db_functions;
|
||||
$this->db_functions = $db_functions; /** @phan-suppress-current-line PhanPossiblyNullTypeMismatchProperty */
|
||||
|
||||
// connect to DB
|
||||
if (!$this->__connectToDB()) {
|
||||
@@ -1413,10 +1413,7 @@ class IO
|
||||
$this->pk_name_table[$table] ?
|
||||
$this->pk_name_table[$table] : 'NULL';
|
||||
}
|
||||
if (
|
||||
!preg_match(self::REGEX_RETURNING, $this->query) &&
|
||||
$this->pk_name && $this->pk_name != 'NULL'
|
||||
) {
|
||||
if (!preg_match(self::REGEX_RETURNING, $this->query) && $this->pk_name != 'NULL') {
|
||||
// check if this query has a ; at the end and remove it
|
||||
$__query = preg_replace("/(;\s*)$/", '', $this->query);
|
||||
// must be query, if preg replace failed, use query as before
|
||||
@@ -1426,7 +1423,7 @@ class IO
|
||||
} elseif (
|
||||
preg_match(self::REGEX_RETURNING, $this->query, $matches)
|
||||
) {
|
||||
if ($this->pk_name && $this->pk_name != 'NULL') {
|
||||
if ($this->pk_name != 'NULL') {
|
||||
// add the primary key if it is not in the returning set
|
||||
if (!preg_match("/$this->pk_name/", $matches[1])) {
|
||||
$this->query .= " , " . $this->pk_name;
|
||||
@@ -2946,7 +2943,7 @@ class IO
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
// clears cache for this query
|
||||
if (empty($this->cursor_ext[$query_hash]['query'])) {
|
||||
$this->__dbError(18, context: [
|
||||
$this->__dbWarning(18, context: [
|
||||
'query' => $query,
|
||||
'params' => $params,
|
||||
'hash' => $query_hash,
|
||||
@@ -3141,6 +3138,7 @@ class IO
|
||||
'pk_name' => '',
|
||||
'count' => 0,
|
||||
'query' => '',
|
||||
'query_raw' => $query,
|
||||
'result' => null,
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
@@ -3237,11 +3235,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 +4363,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__
|
||||
@@ -50,7 +50,6 @@ class GetLocale
|
||||
$locale = defined('SITE_LOCALE') && !empty(SITE_LOCALE) ?
|
||||
SITE_LOCALE :
|
||||
// else parse from default, if not 'en'
|
||||
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
|
||||
(defined('DEFAULT_LOCALE') && !empty(DEFAULT_LOCALE) ?
|
||||
DEFAULT_LOCALE : 'en');
|
||||
}
|
||||
@@ -97,8 +96,7 @@ class GetLocale
|
||||
$encoding = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
|
||||
SITE_ENCODING :
|
||||
// or default encoding, if not 'UTF-8'
|
||||
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
|
||||
(defined('DEFAULT_ENCODING') && !empty(DEFAULT_ENCODING) ?
|
||||
(defined('DEFAULT_ENCODING') ?
|
||||
DEFAULT_ENCODING : 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -418,9 +418,7 @@ class ProgressBar
|
||||
// if this is percent, we ignore anything, it is auto positioned
|
||||
if ($this->label[$name]['type'] != 'percent') {
|
||||
foreach (['top', 'left', 'width', 'height'] as $pos_name) {
|
||||
if ($$pos_name !== false) {
|
||||
$this->label[$name][$pos_name] = intval($$pos_name);
|
||||
}
|
||||
$this->label[$name][$pos_name] = intval($$pos_name);
|
||||
}
|
||||
|
||||
if ($align != '') {
|
||||
|
||||
408
src/Security/AsymmetricAnonymousEncryption.php
Normal file
408
src/Security/AsymmetricAnonymousEncryption.php
Normal file
@@ -0,0 +1,408 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* very simple asymmetric encryption
|
||||
* Better use:
|
||||
* https://paragonie.com/project/halite
|
||||
* https://github.com/paragonie/halite
|
||||
*
|
||||
* current code is just to encrypt and decrypt
|
||||
*
|
||||
* must use a valid encryption key created with
|
||||
* Secruty\CreateKey class
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Security;
|
||||
|
||||
use CoreLibs\Security\CreateKey;
|
||||
use SodiumException;
|
||||
|
||||
class AsymmetricAnonymousEncryption
|
||||
{
|
||||
/** @var AsymmetricAnonymousEncryption self instance */
|
||||
private static AsymmetricAnonymousEncryption $instance;
|
||||
|
||||
/** @var ?string key pair which holds secret and public key, needed for encryption */
|
||||
private ?string $key_pair = null;
|
||||
/** @var ?string public key, needed for decryption
|
||||
* if not set but key_pair set, this will be extracted from key pair */
|
||||
private ?string $public_key = null;
|
||||
|
||||
/**
|
||||
* init class
|
||||
* if key not passed, key must be set with createKey
|
||||
*
|
||||
* @param string|null $key_pair
|
||||
* @param string|null $public_key
|
||||
*/
|
||||
public function __construct(
|
||||
#[\SensitiveParameter]
|
||||
string|null $key_pair = null,
|
||||
string|null $public_key = null
|
||||
) {
|
||||
if ($public_key !== null) {
|
||||
$this->setPublicKey($public_key);
|
||||
}
|
||||
if ($key_pair !== null) {
|
||||
$this->setKeyPair($key_pair);
|
||||
if (empty($public_key)) {
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$this->setPublicKey($public_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton self object.
|
||||
* For function wrapper use
|
||||
*
|
||||
* @param string|null $key_pair
|
||||
* @param string|null $public_key
|
||||
* @return AsymmetricAnonymousEncryption object
|
||||
*/
|
||||
public static function getInstance(
|
||||
#[\SensitiveParameter]
|
||||
string|null $key_pair = null,
|
||||
string|null $public_key = null
|
||||
): self {
|
||||
// new if no instsance or key is different
|
||||
if (
|
||||
empty(self::$instance) ||
|
||||
self::$instance->key_pair != $key_pair ||
|
||||
self::$instance->public_key != $public_key
|
||||
) {
|
||||
self::$instance = new self($key_pair, $public_key);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (empty($this->key_pair)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// would set it to null, but we we do not want to make key null
|
||||
sodium_memzero($this->key_pair);
|
||||
return;
|
||||
} catch (SodiumException) {
|
||||
// empty catch
|
||||
}
|
||||
if (is_null($this->key_pair)) {
|
||||
return;
|
||||
}
|
||||
$zero = str_repeat("\0", mb_strlen($this->key_pair, '8bit'));
|
||||
$this->key_pair = $this->key_pair ^ (
|
||||
$zero ^ $this->key_pair
|
||||
);
|
||||
unset($zero);
|
||||
unset($this->key_pair); /** @phan-suppress-current-line PhanTypeObjectUnsetDeclaredProperty */
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* MARK: PRIVATE
|
||||
* *************************************************************************/
|
||||
|
||||
/**
|
||||
* Create the internal key pair in binary
|
||||
*
|
||||
* @param ?string $key_pair
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException key pair empty
|
||||
* @throws \UnexpectedValueException invalid hex key pair
|
||||
* @throws \RangeException key pair not correct size
|
||||
*/
|
||||
private function createKeyPair(
|
||||
#[\SensitiveParameter]
|
||||
?string $key_pair
|
||||
): string {
|
||||
if (empty($key_pair)) {
|
||||
throw new \UnexpectedValueException('Key pair cannot be empty');
|
||||
}
|
||||
try {
|
||||
$key_pair = CreateKey::hex2bin($key_pair);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($key_pair);
|
||||
throw new \UnexpectedValueException('Invalid hex key pair: ' . $e->getMessage());
|
||||
}
|
||||
if (mb_strlen($key_pair, '8bit') !== SODIUM_CRYPTO_BOX_KEYPAIRBYTES) {
|
||||
sodium_memzero($key_pair);
|
||||
throw new \RangeException(
|
||||
'Key pair is not the correct size (must be '
|
||||
. SODIUM_CRYPTO_BOX_KEYPAIRBYTES . ' bytes long).'
|
||||
);
|
||||
}
|
||||
return $key_pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the internal public key in binary
|
||||
*
|
||||
* @param ?string $public_key
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException public key empty
|
||||
* @throws \UnexpectedValueException invalid hex key
|
||||
* @throws \RangeException invalid key length
|
||||
*/
|
||||
private function createPublicKey(?string $public_key): string
|
||||
{
|
||||
if (empty($public_key)) {
|
||||
throw new \UnexpectedValueException('Public key cannot be empty');
|
||||
}
|
||||
try {
|
||||
$public_key = CreateKey::hex2bin($public_key);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($public_key);
|
||||
throw new \UnexpectedValueException('Invalid hex public key: ' . $e->getMessage());
|
||||
}
|
||||
if (mb_strlen($public_key, '8bit') !== SODIUM_CRYPTO_BOX_PUBLICKEYBYTES) {
|
||||
sodium_memzero($public_key);
|
||||
throw new \RangeException(
|
||||
'Public key is not the correct size (must be '
|
||||
. SODIUM_CRYPTO_BOX_PUBLICKEYBYTES . ' bytes long).'
|
||||
);
|
||||
}
|
||||
return $public_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypt a message asymmetric with a bpulic key
|
||||
*
|
||||
* @param string $message
|
||||
* @param ?string $public_key
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException create encryption failed
|
||||
* @throws \UnexpectedValueException convert to base64 failed
|
||||
*/
|
||||
private function asymmetricEncryption(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
?string $public_key
|
||||
): string {
|
||||
$public_key = $this->createPublicKey($public_key);
|
||||
try {
|
||||
$encrypted = sodium_crypto_box_seal($message, $public_key);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($message);
|
||||
try {
|
||||
$result = sodium_bin2base64($encrypted, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($encrypted);
|
||||
throw new \UnexpectedValueException("bin2base64 failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($encrypted);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt a message that is asymmetric encrypted with a key pair
|
||||
*
|
||||
* @param string $message
|
||||
* @param ?string $key_pair
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException message string empty
|
||||
* @throws \UnexpectedValueException base64 decoding failed
|
||||
* @throws \UnexpectedValueException decryption failed
|
||||
* @throws \UnexpectedValueException could not decrypt message
|
||||
*/
|
||||
private function asymmetricDecryption(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
?string $key_pair
|
||||
): string {
|
||||
if (empty($message)) {
|
||||
throw new \UnexpectedValueException('Encrypted string cannot be empty');
|
||||
}
|
||||
$key_pair = $this->createKeyPair($key_pair);
|
||||
try {
|
||||
$result = sodium_base642bin($message, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
sodium_memzero($key_pair);
|
||||
throw new \UnexpectedValueException("base642bin failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($message);
|
||||
$plaintext = false;
|
||||
try {
|
||||
$plaintext = sodium_crypto_box_seal_open($result, $key_pair);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
sodium_memzero($key_pair);
|
||||
sodium_memzero($result);
|
||||
throw new \UnexpectedValueException("Decrypting message failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($key_pair);
|
||||
sodium_memzero($result);
|
||||
if (!is_string($plaintext)) {
|
||||
throw new \UnexpectedValueException('Invalid key pair');
|
||||
}
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* MARK: PUBLIC
|
||||
* *************************************************************************/
|
||||
|
||||
/**
|
||||
* sets the private key for encryption
|
||||
*
|
||||
* @param string $key_pair Key pair in hex
|
||||
* @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');
|
||||
}
|
||||
// check if valid;
|
||||
$this->createKeyPair($key_pair);
|
||||
// set new key pair
|
||||
$this->key_pair = $key_pair;
|
||||
sodium_memzero($key_pair);
|
||||
// set public key if not set
|
||||
if (empty($this->public_key)) {
|
||||
$this->public_key = CreateKey::getPublicKey($this->key_pair);
|
||||
// check if valid
|
||||
$this->createPublicKey($this->public_key);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if set key pair matches given one
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @return bool
|
||||
*/
|
||||
public function compareKeyPair(
|
||||
#[\SensitiveParameter]
|
||||
string $key_pair
|
||||
): bool {
|
||||
return $this->key_pair === $key_pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the current set key pair, null if not set
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getKeyPair(): ?string
|
||||
{
|
||||
return $this->key_pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the public key for decryption
|
||||
* if only key pair exists Security\Create::getPublicKey() can be used to
|
||||
* extract the public key from the key pair
|
||||
*
|
||||
* @param string $public_key Public Key in hex
|
||||
* @return AsymmetricAnonymousEncryption
|
||||
* @throws \UnexpectedValueException public key empty
|
||||
*/
|
||||
public function setPublicKey(string $public_key): AsymmetricAnonymousEncryption
|
||||
{
|
||||
if (empty($public_key)) {
|
||||
throw new \UnexpectedValueException('Public key cannot be empty');
|
||||
}
|
||||
// check if valid
|
||||
$this->createPublicKey($public_key);
|
||||
$this->public_key = $public_key;
|
||||
sodium_memzero($public_key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the set public key matches the given one
|
||||
*
|
||||
* @param string $public_key
|
||||
* @return bool
|
||||
*/
|
||||
public function comparePublicKey(string $public_key): bool
|
||||
{
|
||||
return $this->public_key === $public_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the current set public key, null if not set
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPublicKey(): ?string
|
||||
{
|
||||
return $this->public_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a message with a public key
|
||||
* static version
|
||||
*
|
||||
* @param string $message Message to encrypt
|
||||
* @param string $public_key Public key in hex to encrypt message with
|
||||
* @return string Encrypted message as hex string
|
||||
*/
|
||||
public static function encryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
string $public_key
|
||||
): string {
|
||||
return self::getInstance()->asymmetricEncryption($message, $public_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a message
|
||||
*
|
||||
* @param string $message Message to ecnrypt
|
||||
* @return string Encrypted message as hex string
|
||||
*/
|
||||
public function encrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $message
|
||||
): string {
|
||||
return $this->asymmetricEncryption($message, $this->public_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt a message with a key pair
|
||||
* static version
|
||||
*
|
||||
* @param string $message Message to decrypt in hex
|
||||
* @param string $key_pair Key pair in hex to decrypt the message with
|
||||
* @return string Decrypted message
|
||||
*/
|
||||
public static function decryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
string $key_pair
|
||||
): string {
|
||||
return self::getInstance()->asymmetricDecryption($message, $key_pair);
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt a message
|
||||
*
|
||||
* @param string $message Message to decrypt in hex
|
||||
* @return string Decrypted message
|
||||
*/
|
||||
public function decrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $message
|
||||
): string {
|
||||
return $this->asymmetricDecryption($message, $this->key_pair);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -35,14 +35,39 @@ class CreateKey
|
||||
return random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a sodium cyptobox keypair as hex string
|
||||
*
|
||||
* @return string hex string for the keypair
|
||||
*/
|
||||
public static function createKeyPair(): string
|
||||
{
|
||||
return self::bin2hex(sodium_crypto_box_keypair());
|
||||
}
|
||||
|
||||
/**
|
||||
* extracts the public key and returns it as hex string from the hex keypari
|
||||
*
|
||||
* @param string $hex_keypair hex encoded keypair
|
||||
* @return string hex encoded public key
|
||||
*/
|
||||
public static function getPublicKey(
|
||||
#[\SensitiveParameter]
|
||||
string $hex_keypair
|
||||
): string {
|
||||
return self::bin2hex(sodium_crypto_box_publickey(self::hex2bin($hex_keypair)));
|
||||
}
|
||||
|
||||
/**
|
||||
* convert binary key to hex string
|
||||
*
|
||||
* @param string $hex_key Convert binary key string to hex
|
||||
* @return string
|
||||
*/
|
||||
public static function bin2hex(string $hex_key): string
|
||||
{
|
||||
public static function bin2hex(
|
||||
#[\SensitiveParameter]
|
||||
string $hex_key
|
||||
): string {
|
||||
return sodium_bin2hex($hex_key);
|
||||
}
|
||||
|
||||
@@ -52,8 +77,10 @@ class CreateKey
|
||||
* @param string $string_key Convery hex key string to binary
|
||||
* @return string
|
||||
*/
|
||||
public static function hex2bin(string $string_key): string
|
||||
{
|
||||
public static function hex2bin(
|
||||
#[\SensitiveParameter]
|
||||
string $string_key
|
||||
): string {
|
||||
return sodium_hex2bin($string_key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ class Password
|
||||
* @param string $password password
|
||||
* @return string hashed password
|
||||
*/
|
||||
public static function passwordSet(string $password): string
|
||||
{
|
||||
public static function passwordSet(
|
||||
#[\SensitiveParameter]
|
||||
string $password
|
||||
): string {
|
||||
// always use the PHP default for the password
|
||||
// password options ca be set in the password init,
|
||||
// but should be kept as default
|
||||
@@ -31,8 +33,11 @@ class Password
|
||||
* @param string $hash password hash
|
||||
* @return bool true or false
|
||||
*/
|
||||
public static function passwordVerify(string $password, string $hash): bool
|
||||
{
|
||||
public static function passwordVerify(
|
||||
#[\SensitiveParameter]
|
||||
string $password,
|
||||
string $hash
|
||||
): bool {
|
||||
if (password_verify($password, $hash)) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -24,19 +24,19 @@ class SymmetricEncryption
|
||||
/** @var SymmetricEncryption self instance */
|
||||
private static SymmetricEncryption $instance;
|
||||
|
||||
/** @var string bin hex key */
|
||||
private string $key = '';
|
||||
/** @var ?string bin hex key */
|
||||
private ?string $key = null;
|
||||
|
||||
/**
|
||||
* init class
|
||||
* if key not passed, key must be set with createKey
|
||||
*
|
||||
* @param string|null|null $key
|
||||
* @param string|null $key encryption key
|
||||
*/
|
||||
public function __construct(
|
||||
string|null $key = null
|
||||
?string $key = null
|
||||
) {
|
||||
if ($key != null) {
|
||||
if ($key !== null) {
|
||||
$this->setKey($key);
|
||||
}
|
||||
}
|
||||
@@ -45,9 +45,10 @@ class SymmetricEncryption
|
||||
* Returns the singleton self object.
|
||||
* For function wrapper use
|
||||
*
|
||||
* @param string|null $key encryption key
|
||||
* @return SymmetricEncryption object
|
||||
*/
|
||||
public static function getInstance(string|null $key = null): self
|
||||
public static function getInstance(?string $key = null): self
|
||||
{
|
||||
// new if no instsance or key is different
|
||||
if (
|
||||
@@ -59,6 +60,34 @@ class SymmetricEncryption
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __deconstruct()
|
||||
{
|
||||
if (empty($this->key)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// would set it to null, but we we do not want to make key null
|
||||
sodium_memzero($this->key);
|
||||
return;
|
||||
} catch (SodiumException) {
|
||||
// empty catch
|
||||
}
|
||||
if (is_null($this->key)) {
|
||||
return;
|
||||
}
|
||||
$zero = str_repeat("\0", mb_strlen($this->key, '8bit'));
|
||||
$this->key = $this->key ^ (
|
||||
$zero ^ $this->key
|
||||
);
|
||||
unset($zero);
|
||||
unset($this->key); /** @phan-suppress-current-line PhanTypeObjectUnsetDeclaredProperty */
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* MARK: PRIVATE
|
||||
* *************************************************************************/
|
||||
@@ -66,11 +95,19 @@ class SymmetricEncryption
|
||||
/**
|
||||
* create key and check validity
|
||||
*
|
||||
* @param string $key The key from which the binary key will be created
|
||||
* @return string Binary key string
|
||||
* @param ?string $key The key from which the binary key will be created
|
||||
* @return string Binary key string
|
||||
* @throws \UnexpectedValueException empty key
|
||||
* @throws \UnexpectedValueException invalid hex key
|
||||
* @throws \RangeException invalid length
|
||||
*/
|
||||
private function createKey(string $key): string
|
||||
{
|
||||
private function createKey(
|
||||
#[\SensitiveParameter]
|
||||
?string $key
|
||||
): string {
|
||||
if (empty($key)) {
|
||||
throw new \UnexpectedValueException('Key cannot be empty');
|
||||
}
|
||||
try {
|
||||
$key = CreateKey::hex2bin($key);
|
||||
} catch (SodiumException $e) {
|
||||
@@ -91,36 +128,42 @@ class SymmetricEncryption
|
||||
* @param string $encrypted Text to decrypt
|
||||
* @param ?string $key Mandatory encryption key, will throw exception if empty
|
||||
* @return string Plain text
|
||||
* @throws \RangeException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException key cannot be empty
|
||||
* @throws \UnexpectedValueException decipher message failed
|
||||
* @throws \UnexpectedValueException invalid key
|
||||
*/
|
||||
private function decryptData(string $encrypted, ?string $key): string
|
||||
{
|
||||
if (empty($key)) {
|
||||
throw new \UnexpectedValueException('Key not set');
|
||||
private function decryptData(
|
||||
#[\SensitiveParameter]
|
||||
string $encrypted,
|
||||
#[\SensitiveParameter]
|
||||
?string $key
|
||||
): string {
|
||||
if (empty($encrypted)) {
|
||||
throw new \UnexpectedValueException('Encrypted string cannot be empty');
|
||||
}
|
||||
$key = $this->createKey($key);
|
||||
$decoded = base64_decode($encrypted);
|
||||
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
|
||||
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
|
||||
|
||||
$plain = false;
|
||||
$plaintext = false;
|
||||
try {
|
||||
$plain = sodium_crypto_secretbox_open(
|
||||
$plaintext = sodium_crypto_secretbox_open(
|
||||
$ciphertext,
|
||||
$nonce,
|
||||
$key
|
||||
);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($ciphertext);
|
||||
sodium_memzero($key);
|
||||
throw new \UnexpectedValueException('Decipher message failed: ' . $e->getMessage());
|
||||
}
|
||||
if (!is_string($plain)) {
|
||||
throw new \UnexpectedValueException('Invalid Key');
|
||||
}
|
||||
sodium_memzero($ciphertext);
|
||||
sodium_memzero($key);
|
||||
return $plain;
|
||||
if (!is_string($plaintext)) {
|
||||
throw new \UnexpectedValueException('Invalid Key');
|
||||
}
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,15 +171,15 @@ class SymmetricEncryption
|
||||
*
|
||||
* @param string $message Message to encrypt
|
||||
* @param ?string $key Mandatory encryption key, will throw exception if empty
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
* @return string Ciphered text
|
||||
* @throws \UnexpectedValueException create message failed
|
||||
*/
|
||||
private function encryptData(string $message, ?string $key): string
|
||||
{
|
||||
if ($key === null) {
|
||||
throw new \UnexpectedValueException('Key not set');
|
||||
}
|
||||
private function encryptData(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
?string $key
|
||||
): string {
|
||||
$key = $this->createKey($key);
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
try {
|
||||
@@ -149,6 +192,8 @@ class SymmetricEncryption
|
||||
)
|
||||
);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
sodium_memzero($key);
|
||||
throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($message);
|
||||
@@ -160,19 +205,49 @@ class SymmetricEncryption
|
||||
* MARK: PUBLIC
|
||||
* *************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* set a new key for encryption
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
* @return SymmetricEncryption
|
||||
* @throws \UnexpectedValueException key cannot be empty
|
||||
*/
|
||||
public function setKey(string $key)
|
||||
{
|
||||
public function setKey(
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): SymmetricEncryption {
|
||||
if (empty($key)) {
|
||||
throw new \UnexpectedValueException('Key cannot be empty');
|
||||
}
|
||||
// check that this is a valid key
|
||||
$this->createKey($key);
|
||||
// set key
|
||||
$this->key = $key;
|
||||
sodium_memzero($key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if set key is equal to parameter key
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function compareKey(
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): bool {
|
||||
return $key === $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the current set key, null if not set
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getKey(): ?string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,13 +257,13 @@ class SymmetricEncryption
|
||||
* @param string $encrypted Message encrypted with safeEncrypt()
|
||||
* @param string $key Encryption key (as hex string)
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public static function decryptKey(string $encrypted, string $key): string
|
||||
{
|
||||
public static function decryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $encrypted,
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): string {
|
||||
return self::getInstance()->decryptData($encrypted, $key);
|
||||
}
|
||||
|
||||
@@ -197,12 +272,11 @@ class SymmetricEncryption
|
||||
*
|
||||
* @param string $encrypted Message encrypted with safeEncrypt()
|
||||
* @return string
|
||||
* @throws \RangeException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function decrypt(string $encrypted): string
|
||||
{
|
||||
public function decrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $encrypted
|
||||
): string {
|
||||
return $this->decryptData($encrypted, $this->key);
|
||||
}
|
||||
|
||||
@@ -213,11 +287,13 @@ class SymmetricEncryption
|
||||
* @param string $message Message to encrypt
|
||||
* @param string $key Encryption key (as hex string)
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
*/
|
||||
public static function encryptKey(string $message, string $key): string
|
||||
{
|
||||
public static function encryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): string {
|
||||
return self::getInstance()->encryptData($message, $key);
|
||||
}
|
||||
|
||||
@@ -226,11 +302,11 @@ class SymmetricEncryption
|
||||
*
|
||||
* @param string $message Message to encrypt
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
*/
|
||||
public function encrypt(string $message): string
|
||||
{
|
||||
public function encrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $message
|
||||
): string {
|
||||
return $this->encryptData($message, $this->key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,7 +48,7 @@ header("Content-Type: application/json; charset=UTF-8");
|
||||
if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) {
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// if server request type is get set file_get to null -> no body
|
||||
@@ -57,7 +57,7 @@ if ($_SERVER['REQUEST_METHOD'] == "GET") {
|
||||
} elseif (($file_get = file_get_contents('php://input')) === false) {
|
||||
header("HTTP/1.1 404 Not Found");
|
||||
print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
print buildContent($http_headers, $file_get);
|
||||
|
||||
@@ -152,7 +152,6 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
// TARGET
|
||||
define('TARGET', 'test');
|
||||
// LOGIN DB SCHEMA
|
||||
// define('LOGIN_DB_SCHEMA', '');
|
||||
|
||||
// SHOULD SET
|
||||
// DEFAULT_ACL_LEVEL (d80)
|
||||
@@ -1531,6 +1530,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(
|
||||
|
||||
@@ -926,48 +926,114 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
public function daysIntervalProvider(): array
|
||||
{
|
||||
return [
|
||||
'valid interval /, not named array' => [
|
||||
'2020/1/1',
|
||||
'2020/1/30',
|
||||
false,
|
||||
[29, 22, 8],
|
||||
// normal and format tests
|
||||
'valid interval / not named array' => [
|
||||
'input_a' => '2020/1/1',
|
||||
'input_b' => '2020/1/30',
|
||||
'return_named' => false, // return_named
|
||||
'include_end_date' => true, // include_end_date
|
||||
'exclude_start_date' => false, // exclude_start_date
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
'valid interval /, named array' => [
|
||||
'2020/1/1',
|
||||
'2020/1/30',
|
||||
true,
|
||||
['overall' => 29, 'weekday' => 22, 'weekend' => 8],
|
||||
'valid interval / named array' => [
|
||||
'input_a' => '2020/1/1',
|
||||
'input_b' => '2020/1/30',
|
||||
'return_named' => true,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => ['overall' => 30, 'weekday' => 22, 'weekend' => 8, 'reverse' => false],
|
||||
],
|
||||
'valid interval -' => [
|
||||
'2020-1-1',
|
||||
'2020-1-30',
|
||||
false,
|
||||
[29, 22, 8],
|
||||
],
|
||||
'valid interval switched' => [
|
||||
'2020/1/30',
|
||||
'2020/1/1',
|
||||
false,
|
||||
[28, 0, 0],
|
||||
'valid interval with "-"' => [
|
||||
'input_a' => '2020-1-1',
|
||||
'input_b' => '2020-1-30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
'valid interval with time' => [
|
||||
'2020/1/1 12:12:12',
|
||||
'2020/1/30 13:13:13',
|
||||
false,
|
||||
[28, 21, 8],
|
||||
'input_a' => '2020/1/1 12:12:12',
|
||||
'input_b' => '2020/1/30 13:13:13',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
// invalid
|
||||
'invalid dates' => [
|
||||
'abc',
|
||||
'xyz',
|
||||
false,
|
||||
[0, 0, 0]
|
||||
'input_a' => 'abc',
|
||||
'input_b' => 'xyz',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [0, 0, 0, false]
|
||||
],
|
||||
// this test will take a long imte
|
||||
// this test will take a long time
|
||||
'out of bound dates' => [
|
||||
'1900-1-1',
|
||||
'9999-12-31',
|
||||
false,
|
||||
[2958463,2113189,845274],
|
||||
'input_a' => '1900-1-1',
|
||||
'input_b' => '9999-12-31',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [2958463, 2113189, 845274, false],
|
||||
],
|
||||
// tests for include/exclude
|
||||
'exclude end date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [29, 21, 8, false],
|
||||
],
|
||||
'exclude start date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [29, 21, 8, false],
|
||||
],
|
||||
'exclude start and end date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [28, 20, 8, false],
|
||||
],
|
||||
// reverse
|
||||
'reverse: valid interval' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, true],
|
||||
],
|
||||
'reverse: exclude end date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [29, 21, 8, true],
|
||||
],
|
||||
'reverse: exclude start date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [29, 21, 8, true],
|
||||
],
|
||||
'reverse: exclude start and end date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [28, 20, 8, true],
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -982,20 +1048,52 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
*
|
||||
* @param string $input_a
|
||||
* @param string $input_b
|
||||
* @param bool $flag
|
||||
* @param array $expected
|
||||
* @param bool $return_named
|
||||
* @param array $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testCalcDaysInterval(
|
||||
string $input_a,
|
||||
string $input_b,
|
||||
bool $flag,
|
||||
bool $return_named,
|
||||
bool $include_end_date,
|
||||
bool $exclude_start_date,
|
||||
$expected
|
||||
): void {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysInterval($input_a, $input_b, $flag)
|
||||
\CoreLibs\Combined\DateTime::calcDaysInterval(
|
||||
$input_a,
|
||||
$input_b,
|
||||
return_named:$return_named,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysInterval'
|
||||
);
|
||||
if ($return_named) {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysIntervalNamedIndex(
|
||||
$input_a,
|
||||
$input_b,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysIntervalNamedIndex'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysIntervalNumIndex(
|
||||
$input_a,
|
||||
$input_b,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysIntervalNamedIndex'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1187,7 +1285,38 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
'2023-07-03',
|
||||
'2023-07-27',
|
||||
true
|
||||
]
|
||||
],
|
||||
// reverse
|
||||
'reverse: no weekend' => [
|
||||
'2023-07-04',
|
||||
'2023-07-03',
|
||||
false
|
||||
],
|
||||
'reverse: start weekend sat' => [
|
||||
'2023-07-04',
|
||||
'2023-07-01',
|
||||
true
|
||||
],
|
||||
'reverse: start weekend sun' => [
|
||||
'2023-07-04',
|
||||
'2023-07-02',
|
||||
true
|
||||
],
|
||||
'reverse: end weekend sat' => [
|
||||
'2023-07-08',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
'reverse: end weekend sun' => [
|
||||
'2023-07-09',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
'reverse: long period > 6 days' => [
|
||||
'2023-07-27',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
4 => '1.00 KB',
|
||||
5 => '1.02KiB',
|
||||
],
|
||||
'invalud string number' => [
|
||||
'invalid string number' => [
|
||||
0 => '1024 MB',
|
||||
1 => '1024 MB',
|
||||
2 => '1024 MB',
|
||||
|
||||
@@ -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'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,838 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Security\CreateKey;
|
||||
use CoreLibs\Security\AsymmetricAnonymousEncryption;
|
||||
|
||||
/**
|
||||
* Test class for Security\AsymmetricAnonymousEncryption and Security\CreateKey
|
||||
* @coversDefaultClass \CoreLibs\Security\AsymmetricAnonymousEncryption
|
||||
* @testdox \CoreLibs\Security\AsymmetricAnonymousEncryption method tests
|
||||
*/
|
||||
final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
{
|
||||
// MARK: key set and compare
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if init class set key pair matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairInitGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'automatic set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'automatic set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if init class set key pair and public key matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairPublicKeyInitGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if init class set public key matches to created public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPublicKeyInitGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
null,
|
||||
$crypt->getKeyPair(),
|
||||
'unset set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setKeyPair
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if set key pair after class init matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairSetGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption();
|
||||
$crypt->setKeyPair($key_pair);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'post class init set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'post class init automatic set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'post class init set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'post class init automatic set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setKeyPair
|
||||
* @covers ::setPublicKey
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if set key pair after class init matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairPublicKeySetGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption();
|
||||
$crypt->setKeyPair($key_pair);
|
||||
$crypt->setPublicKey($public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'post class init set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'post class init set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'post class init set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'post class init set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setPublicKey
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if set key pair after class init matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPublicKeySetGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption();
|
||||
$crypt->setPublicKey($public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'post class init set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
null,
|
||||
$crypt->getKeyPair(),
|
||||
'post class init unset key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'post class init set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @testdox Check different key pair and public key set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDifferentSetKeyPairPublicKey()
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$key_pair_2 = CreateKey::createKeyPair();
|
||||
$public_key_2 = CreateKey::getPublicKey($key_pair_2);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key_2);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'key pair set matches key pair created'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key_2),
|
||||
'alternate public key set matches alternate public key created'
|
||||
);
|
||||
$this->assertFalse(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'alternate public key set does not match key pair public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @testdox Check if new set privat key does not overwrite set public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testUpdateKeyPairNotUpdatePublicKey(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'set public key not equal to original public key'
|
||||
);
|
||||
$key_pair_2 = CreateKey::createKeyPair();
|
||||
$public_key_2 = CreateKey::getPublicKey($key_pair_2);
|
||||
$crypt->setKeyPair($key_pair_2);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair_2),
|
||||
'new set key pair not equal to original new key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'original set public key not equal to original public key'
|
||||
);
|
||||
$this->assertFalse(
|
||||
$crypt->comparePublicKey($public_key_2),
|
||||
'new public key equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: empty encrytped string
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @covers ::decrypt
|
||||
* @testdox Test empty encrypted string to decrypt
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEmptyDecryptionString(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Encrypted string cannot be empty');
|
||||
AsymmetricAnonymousEncryption::decryptKey('', CreateKey::generateRandomKey());
|
||||
}
|
||||
|
||||
// MARK: encrypt/decrypt
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerEncryptDecryptSuccess(): array
|
||||
{
|
||||
return [
|
||||
'valid string' => [
|
||||
'input' => 'I am a secret',
|
||||
'expected' => 'I am a secret',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccess(string $input, string $expected): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test class
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$decrypted = $crypt->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class call',
|
||||
);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$decrypted = $crypt->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class call botjh set',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt indirect $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessIndirect(string $input, string $expected): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test indirect
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
|
||||
$decrypted = AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class Instance call',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt indirect with public key $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessIndirectPublicKey(string $input, string $expected): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test indirect
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
|
||||
$decrypted = AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class Instance call public key',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt static $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessStatic(string $input, string $expected): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test static
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey($input, $public_key);
|
||||
$decrypted = AsymmetricAnonymousEncryption::decryptKey($encrypted, $key_pair);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Static call',
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: invalid decrypt key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerEncryptFailed(): array
|
||||
{
|
||||
return [
|
||||
'wrong decryption key' => [
|
||||
'input' => 'I am a secret',
|
||||
'excpetion_message' => 'Invalid key pair'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailed(string $input, string $exception_message): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$wrong_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// wrong key in class call
|
||||
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKeyPair($wrong_key_pair);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt indirect with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailedIndirect(string $input, string $exception_message): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$wrong_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// class instance
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($wrong_key_pair)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt static with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailedStatic(string $input, string $exception_message): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$wrong_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// class static
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey($input, $public_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($encrypted, $wrong_key_pair);
|
||||
}
|
||||
|
||||
// MARK: invalid key pair
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongKeyPair(): array
|
||||
{
|
||||
return [
|
||||
'not hex key pair' => [
|
||||
'key_pair' => 'not_a_hex_key_pair',
|
||||
'exception_message' => 'Invalid hex key pair'
|
||||
],
|
||||
'too short hex key pair' => [
|
||||
'key_pair' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
|
||||
'excpetion_message' => 'Key pair is not the correct size (must be '
|
||||
],
|
||||
'empty key pair' => [
|
||||
'key_pair' => '',
|
||||
'excpetion_message' => 'Key pair cannot be empty'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKeyPair
|
||||
* @testdox wrong key pair $key_pair throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyPair(string $key_pair, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// class
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->encrypt('test');
|
||||
$crypt->setKeyPair($enc_key_pair);
|
||||
$encrypted = $crypt->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKeyPair($key_pair);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKeyPair
|
||||
* @testdox wrong key pair indirect $key_pair throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyPairIndirect(string $key_pair, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// set valid encryption
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance($enc_key_pair)->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKeyPair
|
||||
* @testdox wrong key pair static $key_pair throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyPairStatic(string $key_pair, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// set valid encryption
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey('test', CreateKey::getPublicKey($enc_key_pair));
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($encrypted, $key_pair);
|
||||
}
|
||||
|
||||
// MARK: invalid public key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongPublicKey(): array
|
||||
{
|
||||
return [
|
||||
'not hex public key' => [
|
||||
'public_key' => 'not_a_hex_public_key',
|
||||
'exception_message' => 'Invalid hex public key'
|
||||
],
|
||||
'too short hex public key' => [
|
||||
'public_key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
|
||||
'excpetion_message' => 'Public key is not the correct size (must be '
|
||||
],
|
||||
'empty public key' => [
|
||||
'public_key' => '',
|
||||
'excpetion_message' => 'Public key cannot be empty'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongPublicKey
|
||||
* @testdox wrong public key $public_key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $public_key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongPublicKey(string $public_key, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
// $enc_public_key = CreateKey::getPublicKey($enc_key_pair);
|
||||
|
||||
// class
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->decrypt('test');
|
||||
$crypt->setKeyPair($enc_key_pair);
|
||||
$encrypted = $crypt->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setPublicKey($public_key);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongPublicKey
|
||||
* @testdox wrong public key indirect $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongPublicKeyIndirect(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::createKeyPair();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance(public_key:$key)->encrypt('test');
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance($enc_key)->encrypt('test');
|
||||
// $this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($key)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongPublicKey
|
||||
* @testdox wrong public key static $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongPublicKeyStatic(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::createKeyPair();
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::encryptKey('test', $key);
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey('test', $enc_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
// MARK: wrong cipher text
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongCiphertext(): array
|
||||
{
|
||||
return [
|
||||
'invalid cipher text' => [
|
||||
'input' => 'short',
|
||||
'exception_message' => 'base642bin failed: '
|
||||
],
|
||||
'cannot decrypt' => [
|
||||
// phpcs:disable Generic.Files.LineLength
|
||||
'input' => 'Um8tBGiVfFAOg2YoUgA5fTqK1wXPB1S7uxhPNE1lqDxgntkEhYJDOmjXa0DMpBlYHjab6sC4mgzwZSzGCUnXDAgsHckwYwfAzs/r',
|
||||
// phpcs:enable Generic.Files.LineLength
|
||||
'exception_message' => 'Invalid key pair'
|
||||
],
|
||||
'invalid text' => [
|
||||
'input' => 'U29tZSB0ZXh0IGhlcmU=',
|
||||
'exception_message' => 'Invalid key pair'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertext(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::createKeyPair();
|
||||
// class
|
||||
$crypt = new AsymmetricAnonymousEncryption($key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->decrypt($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext indirect $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertextIndirect(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::createKeyPair();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($key)->decrypt($input);
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($input, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext static $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertextStatic(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::createKeyPair();
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($input, $key);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -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)
|
||||
|
||||
@@ -15,6 +15,77 @@ use CoreLibs\Security\SymmetricEncryption;
|
||||
*/
|
||||
final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
{
|
||||
// MARK: key set compare
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::compareKey
|
||||
* @covers ::getKey
|
||||
* @testdox Check if init class set key matches to created key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyInitGetCompare(): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$crypt = new SymmetricEncryption($key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKey($key),
|
||||
'set key not equal to original key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key,
|
||||
$crypt->getKey(),
|
||||
'set key returned not equal to original key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setKey
|
||||
* @covers ::compareKey
|
||||
* @covers ::getKey
|
||||
* @testdox Check if set key after class init matches to created key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeySetGetCompare(): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$crypt = new SymmetricEncryption();
|
||||
$crypt->setKey($key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKey($key),
|
||||
'set key not equal to original key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key,
|
||||
$crypt->getKey(),
|
||||
'set key returned not equal to original key'
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: empty encrypted string
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @covers ::decrypt
|
||||
* @testdox Test empty encrypted string to decrypt
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEmptyDecryptionString(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Encrypted string cannot be empty');
|
||||
SymmetricEncryption::decryptKey('', CreateKey::generateRandomKey());
|
||||
}
|
||||
|
||||
// MARK: encrypt/decrypt compare
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -88,8 +159,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @covers ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt static $input must be $expected [$_dataName]
|
||||
*
|
||||
@@ -111,6 +182,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: invalid key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -180,8 +253,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @covers ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt static with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
@@ -200,6 +273,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
SymmetricEncryption::decryptKey($encrypted, $wrong_key);
|
||||
}
|
||||
|
||||
// MARK: wrong key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -216,6 +291,10 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
|
||||
'excpetion_message' => 'Key is not the correct size (must be '
|
||||
],
|
||||
'empty key' => [
|
||||
'key' => '',
|
||||
'excpetion_message' => 'Key cannot be empty'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -236,6 +315,7 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$enc_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt = new SymmetricEncryption($key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->encrypt('test');
|
||||
@@ -244,22 +324,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKey($key);
|
||||
$crypt->decrypt($encrypted);
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->encrypt('test');
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->decrypt($encrypted);
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::encryptKey('test', $key);
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = SymmetricEncryption::encryptKey('test', $enc_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,8 +354,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @covers ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerWrongKey
|
||||
* @testdox wrong key static $key throws $exception_message [$_dataName]
|
||||
*
|
||||
@@ -312,6 +376,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
// MARK: wrong input
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -358,7 +424,7 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext indirect $input throws $exception_message [$_dataName]
|
||||
*
|
||||
@@ -382,7 +448,7 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext static $input throws $exception_message [$_dataName]
|
||||
*
|
||||
|
||||
@@ -969,44 +969,76 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
"query" => ["foo-get" => "bar"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: get response code not matching");
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"GET",'
|
||||
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_FIRST_CALL":"get",'
|
||||
. '"HTTP_ACCEPT":"*\/*"},"REQUEST_TYPE":"GET","PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"GET",'
|
||||
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
}
|
||||
// post
|
||||
$response = $curl->post($this->url_basic, [
|
||||
"headers" => ["second-call" => "post"],
|
||||
"body" => ["foo-post" => "baz"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: post response code not matching");
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"POST",'
|
||||
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*"},'
|
||||
. '"REQUEST_TYPE":"POST","PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"POST",'
|
||||
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
}
|
||||
// delete
|
||||
$response = $curl->delete($this->url_basic, [
|
||||
"headers" => ["third-call" => "delete"],
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: delete response code not matching");
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"DELETE",'
|
||||
. '"PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"},'
|
||||
. '"REQUEST_TYPE":"DELETE","PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"DELETE",'
|
||||
. '"PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: auth header set via config
|
||||
|
||||
Reference in New Issue
Block a user