Compare commits

...

9 Commits

Author SHA1 Message Date
Clemens Schwaighofer
fbea8f4aca Fix for Symmetric encryption key handling 2024-12-12 21:11:25 +09:00
Clemens Schwaighofer
346cdaad72 Fix for params regex comment update 2024-12-11 11:23:19 +09:00
Clemens Schwaighofer
6887f17e15 Release: v9.23.2 2024-12-10 15:31:45 +09:00
Clemens Schwaighofer
5b1ca4241c phan min php update to 8.3, add missing phpunit test folder for language check 2024-12-10 15:29:54 +09:00
Clemens Schwaighofer
c8d6263c0f Fix DB IO placeholder count 2024-12-10 14:59:58 +09:00
Clemens Schwaighofer
bd1972d894 Composer keywords 2024-12-10 14:52:50 +09:00
Clemens Schwaighofer
fa29477c80 Release: v9.23.1 2024-12-05 14:08:56 +09:00
Clemens Schwaighofer
20ee958db9 Session class update with many methods and general clean up 2024-12-05 14:07:39 +09:00
Clemens Schwaighofer
157616582f Release: v9.23.0 2024-12-04 16:40:51 +09:00
14 changed files with 560 additions and 194 deletions

View File

@@ -54,7 +54,8 @@ return [
// Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist. // Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist.
// (See `backward_compatibility_checks` for additional options) // (See `backward_compatibility_checks` for additional options)
// Automatically inferred from composer.json requirement for "php" of ">=8.2" // Automatically inferred from composer.json requirement for "php" of ">=8.2"
'target_php_version' => '8.1', 'target_php_version' => '8.2',
"minimum_target_php_version" => "8.2",
// If enabled, missing properties will be created when // If enabled, missing properties will be created when
// they are first seen. If false, we'll report an // they are first seen. If false, we'll report an

View File

@@ -3,6 +3,7 @@
"description": "CoreLibs in a composer package", "description": "CoreLibs in a composer package",
"type": "library", "type": "library",
"license": "MIT", "license": "MIT",
"keywords": ["corelib", "logging", "database", "templating", "tools"],
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"CoreLibs\\": "src/" "CoreLibs\\": "src/"

View File

@@ -1 +1 @@
9.22.0 9.23.2

View File

@@ -372,9 +372,6 @@ class Login
], ],
]; ];
// init default ACL list array
$_SESSION['DEFAULT_ACL_LIST'] = [];
$_SESSION['DEFAULT_ACL_LIST_TYPE'] = [];
// read the current edit_access_right list into an array // read the current edit_access_right list into an array
$q = "SELECT level, type, name FROM edit_access_right " $q = "SELECT level, type, name FROM edit_access_right "
. "WHERE level >= 0 ORDER BY level"; . "WHERE level >= 0 ORDER BY level";
@@ -387,8 +384,10 @@ class Login
$this->default_acl_list_type[(string)$res['type']] = (int)$res['level']; $this->default_acl_list_type[(string)$res['type']] = (int)$res['level'];
} }
// write that into the session // write that into the session
$_SESSION['DEFAULT_ACL_LIST'] = $this->default_acl_list; $this->session->setMany([
$_SESSION['DEFAULT_ACL_LIST_TYPE'] = $this->default_acl_list_type; 'DEFAULT_ACL_LIST' => $this->default_acl_list,
'DEFAULT_ACL_LIST_TYPE' => $this->default_acl_list_type,
]);
$this->loginSetEditLogWriteTypeAvailable(); $this->loginSetEditLogWriteTypeAvailable();
@@ -580,7 +579,7 @@ class Login
// set path // set path
$options['locale_path'] = BASE . INCLUDES . LOCALE; $options['locale_path'] = BASE . INCLUDES . LOCALE;
} }
$_SESSION['LOCALE_PATH'] = $options['locale_path']; $this->session->set('LOCALE_PATH', $options['locale_path']);
// LANG: LOCALE // LANG: LOCALE
if (empty($options['site_locale'])) { if (empty($options['site_locale'])) {
trigger_error( trigger_error(
@@ -615,7 +614,7 @@ class Login
$options['set_domain'] = str_replace(DIRECTORY_SEPARATOR, '', CONTENT_PATH); $options['set_domain'] = str_replace(DIRECTORY_SEPARATOR, '', CONTENT_PATH);
} }
} }
$_SESSION['DEFAULT_DOMAIN'] = $options['site_domain']; $this->session->set('DEFAULT_DOMAIN', $options['site_domain']);
// LANG: ENCODING // LANG: ENCODING
if (empty($options['site_encoding'])) { if (empty($options['site_encoding'])) {
trigger_error( trigger_error(
@@ -901,9 +900,14 @@ class Login
} }
// normal user processing // normal user processing
// set class var and session var // set class var and session var
$_SESSION['EUID'] = $this->euid = (int)$res['edit_user_id']; $this->euid = (int)$res['edit_user_id'];
$_SESSION['ECUID'] = $this->ecuid = (string)$res['cuid']; $this->ecuid = (string)$res['cuid'];
$_SESSION['ECUUID'] = $this->ecuuid = (string)$res['cuuid']; $this->ecuuid = (string)$res['cuuid'];
$this->session->setMany([
'EUID' => $this->euid,
'ECUID' => $this->ecuid,
'ECUUID' => $this->ecuuid,
]);
// check if user is okay // check if user is okay
$this->loginCheckPermissions(); $this->loginCheckPermissions();
if ($this->login_error == 0) { if ($this->login_error == 0) {
@@ -916,27 +920,39 @@ class Login
. "WHERE edit_user_id = " . $this->euid; . "WHERE edit_user_id = " . $this->euid;
$this->db->dbExec($q); $this->db->dbExec($q);
} }
// now set all session vars and read page permissions $locale = $res['locale'] ?? 'en';
$_SESSION['DEBUG_ALL'] = $this->db->dbBoolean($res['debug']); $encoding = $res['encoding'] ?? 'UTF-8';
$_SESSION['DB_DEBUG'] = $this->db->dbBoolean($res['db_debug']); $this->session->setMany([
// general info for user logged in // now set all session vars and read page permissions
$_SESSION['USER_NAME'] = $res['username']; 'DEBUG_ALL' => $this->db->dbBoolean($res['debug']),
$_SESSION['ADMIN'] = $res['admin']; 'DB_DEBUG' => $this->db->dbBoolean($res['db_debug']),
$_SESSION['GROUP_NAME'] = $res['edit_group_name']; // general info for user logged in
$_SESSION['USER_ACL_LEVEL'] = $res['user_level']; 'USER_NAME' => $res['username'],
$_SESSION['USER_ACL_TYPE'] = $res['user_type']; 'ADMIN' => $res['admin'],
$_SESSION['USER_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['user_additional_acl']); 'GROUP_NAME' => $res['edit_group_name'],
$_SESSION['GROUP_ACL_LEVEL'] = $res['group_level']; 'USER_ACL_LEVEL' => $res['user_level'],
$_SESSION['GROUP_ACL_TYPE'] = $res['group_type']; 'USER_ACL_TYPE' => $res['user_type'],
$_SESSION['GROUP_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['group_additional_acl']); 'USER_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['user_additional_acl']),
// deprecated TEMPLATE setting 'GROUP_ACL_LEVEL' => $res['group_level'],
$_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : ''; 'GROUP_ACL_TYPE' => $res['group_type'],
$_SESSION['HEADER_COLOR'] = !empty($res['second_header_color']) ? 'GROUP_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['group_additional_acl']),
$res['second_header_color'] : // deprecated TEMPLATE setting
$res['first_header_color']; 'TEMPLATE' => $res['template'] ? $res['template'] : '',
'HEADER_COLOR' => !empty($res['second_header_color']) ?
$res['second_header_color'] :
$res['first_header_color'],
// LANGUAGE/LOCALE/ENCODING:
'LANG' => $locale,
'DEFAULT_CHARSET' => $encoding,
'DEFAULT_LOCALE' => $locale . '.' . strtoupper($encoding),
'DEFAULT_LANG' => $locale . '_' . strtolower(str_replace('-', '', $encoding))
]);
// missing # before, this is for legacy data, will be deprecated // missing # before, this is for legacy data, will be deprecated
if (preg_match("/^[\dA-Fa-f]{6,8}$/", $_SESSION['HEADER_COLOR'])) { if (
$_SESSION['HEADER_COLOR'] = '#' . $_SESSION['HEADER_COLOR']; !empty($this->session->get('HEADER_COLOR')) &&
preg_match("/^[\dA-Fa-f]{6,8}$/", $this->session->get('HEADER_COLOR'))
) {
$this->session->set('HEADER_COLOR', '#' . $this->session->get('HEADER_COLOR'));
} }
// TODO: make sure that header color is valid: // TODO: make sure that header color is valid:
// # + 6 hex // # + 6 hex
@@ -945,13 +961,6 @@ class Login
// rgb: nnn.n for each // rgb: nnn.n for each
// hsl: nnn.n for first, nnn.n% for 2nd, 3rd // hsl: nnn.n for first, nnn.n% for 2nd, 3rd
// Check\Colors::validateColor() // Check\Colors::validateColor()
// LANGUAGE/LOCALE/ENCODING:
$_SESSION['LANG'] = $res['locale'] ?? 'en';
$_SESSION['DEFAULT_CHARSET'] = $res['encoding'] ?? 'UTF-8';
$_SESSION['DEFAULT_LOCALE'] = $_SESSION['LANG']
. '.' . strtoupper($_SESSION['DEFAULT_CHARSET']);
$_SESSION['DEFAULT_LANG'] = $_SESSION['LANG'] . '_'
. strtolower(str_replace('-', '', $_SESSION['DEFAULT_CHARSET']));
// reset any login error count for this user // reset any login error count for this user
if ($res['login_error_count'] > 0) { if ($res['login_error_count'] > 0) {
$q = "UPDATE edit_user " $q = "UPDATE edit_user "
@@ -1041,8 +1050,10 @@ class Login
]; ];
} }
// write back the pages data to the output array // write back the pages data to the output array
$_SESSION['PAGES'] = $pages; $this->session->setMany([
$_SESSION['PAGES_ACL_LEVEL'] = $pages_acl; 'PAGES' => $pages,
'PAGES_ACL_LEVEL' => $pages_acl,
]);
// load the edit_access user rights // load the edit_access user rights
$q = "SELECT ea.edit_access_id, level, type, ea.name, " $q = "SELECT ea.edit_access_id, level, type, ea.name, "
. "ea.color, ea.uid, edit_default, ea.additional_acl " . "ea.color, ea.uid, edit_default, ea.additional_acl "
@@ -1054,6 +1065,7 @@ class Login
$unit_access = []; $unit_access = [];
$eauid = []; $eauid = [];
$unit_acl = []; $unit_acl = [];
$unit_uid = [];
while (is_array($res = $this->db->dbReturn($q))) { while (is_array($res = $this->db->dbReturn($q))) {
// read edit access data fields and drop them into the unit access array // read edit access data fields and drop them into the unit access array
$q_sub = "SELECT name, value " $q_sub = "SELECT name, value "
@@ -1077,16 +1089,19 @@ class Login
]; ];
// set the default unit // set the default unit
if ($res['edit_default']) { if ($res['edit_default']) {
$_SESSION['UNIT_DEFAULT'] = (int)$res['edit_access_id']; $this->session->set('UNIT_DEFAULT', (int)$res['edit_access_id']);
} }
$_SESSION['UNIT_UID'][$res['uid']] = (int)$res['edit_access_id']; $unit_uid[$res['uid']] = (int)$res['edit_access_id'];
// sub arrays for simple access // sub arrays for simple access
array_push($eauid, $res['edit_access_id']); array_push($eauid, $res['edit_access_id']);
$unit_acl[$res['edit_access_id']] = $res['level']; $unit_acl[$res['edit_access_id']] = $res['level'];
} }
$_SESSION['UNIT'] = $unit_access; $this->session->setMany([
$_SESSION['UNIT_ACL_LEVEL'] = $unit_acl; 'UNIT_UID' => $unit_uid,
$_SESSION['EAID'] = $eauid; 'UNIT' => $unit_access,
'UNIT_ACL_LEVEL' => $unit_acl,
'EAID' => $eauid,
]);
} // user has permission to THIS page } // user has permission to THIS page
} // user was not enabled or other login error } // user was not enabled or other login error
if ($this->login_error && is_array($res)) { if ($this->login_error && is_array($res)) {
@@ -1182,7 +1197,7 @@ class Login
$this->acl['base'] = (int)$_SESSION['USER_ACL_LEVEL']; $this->acl['base'] = (int)$_SESSION['USER_ACL_LEVEL'];
} }
} }
$_SESSION['BASE_ACL_LEVEL'] = $this->acl['base']; $this->session->set('BASE_ACL_LEVEL', $this->acl['base']);
// set the current page acl // set the current page acl
// start with base acl // start with base acl
@@ -1889,13 +1904,13 @@ HTML;
), ),
[ [
// row 1 // row 1
empty($username) ? $_SESSION['USER_NAME'] ?? '' : $username, empty($username) ? $this->session->get('USER_NAME') ?? '' : $username,
!empty($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ? is_numeric($this->session->get('EUID')) ?
$_SESSION['EUID'] : null, $this->session->get('EUID') : null,
!empty($_SESSION['ECUID']) && is_string($_SESSION['ECUID']) ? is_string($this->session->get('ECUID')) ?
$_SESSION['ECUID'] : null, $this->session->get('ECUID') : null,
!empty($_SESSION['ECUUID']) && Uids::validateUuuidv4($_SESSION['ECUUID']) ? !empty($this->session->get('ECUUID')) && Uids::validateUuuidv4($this->session->get('ECUUID')) ?
$_SESSION['ECUUID'] : null, $this->session->get('ECUUID') : null,
(string)$event, (string)$event,
(string)$error, (string)$error,
$data_write, $data_write,
@@ -2022,10 +2037,10 @@ HTML;
} }
} }
// if there is none, there is none, saves me POST/GET check // if there is none, there is none, saves me POST/GET check
$this->euid = array_key_exists('EUID', $_SESSION) ? (int)$_SESSION['EUID'] : 0; $this->euid = (int)($this->session->get('EUID') ?? 0);
// TODO: allow load from cuid // TODO: allow load from cuid
// $this->ecuid = array_key_exists('ECUID', $_SESSION) ? (string)$_SESSION['ECUID'] : ''; // $this->ecuid = (string)($this->session->get('ECUID') ?? '');
// $this->ecuuid = array_key_exists('ECUUID', $_SESSION) ? (string)$_SESSION['ECUUID'] : ''; // $this->ecuuid = (string)($this->session->get('ECUUID') ?? '');
// get login vars, are so, can't be changed // get login vars, are so, can't be changed
// prepare // prepare
// pass on vars to Object vars // pass on vars to Object vars
@@ -2368,8 +2383,12 @@ HTML;
$this->login_error = 103; $this->login_error = 103;
} }
// set ECUID // set ECUID
$_SESSION['ECUID'] = $this->ecuid = (string)$res['cuid']; $this->ecuid = (string)$res['cuid'];
$_SESSION['ECUUID'] = $this->ecuuid = (string)$res['cuuid']; $this->ecuuid = (string)$res['cuuid'];
$this->session->setMany([
'ECUID' => $this->ecuid,
'ECUUID' => $this->ecuuid,
]);
// if called from public, so we can check if the permissions are ok // if called from public, so we can check if the permissions are ok
return $this->permission_okay; return $this->permission_okay;
} }
@@ -2515,13 +2534,12 @@ HTML;
{ {
if ( if (
$edit_access_id !== null && $edit_access_id !== null &&
isset($_SESSION['UNIT']) && is_array($this->session->get('UNIT')) &&
is_array($_SESSION['UNIT']) && !array_key_exists($edit_access_id, $this->session->get('UNIT'))
!array_key_exists($edit_access_id, $_SESSION['UNIT'])
) { ) {
$edit_access_id = null; $edit_access_id = null;
if (is_numeric($_SESSION['UNIT_DEFAULT'])) { if (is_numeric($this->session->get('UNIT_DEFAULT'))) {
$edit_access_id = (int)$_SESSION['UNIT_DEFAULT']; $edit_access_id = (int)$this->session->get('UNIT_DEFAULT');
} }
} }
return $edit_access_id; return $edit_access_id;
@@ -2652,7 +2670,7 @@ HTML;
*/ */
public function loginGetHeaderColor(): ?string public function loginGetHeaderColor(): ?string
{ {
return $_SESSION['HEADER_COLOR'] ?? null; return $this->session->get('HEADER_COLOR');
} }
/** /**
@@ -2663,7 +2681,7 @@ HTML;
public function loginGetPages(): array public function loginGetPages(): array
{ {
return $_SESSION['PAGES'] ?? []; return $this->session->get('PAGES');
} }
/** /**

View File

@@ -380,12 +380,12 @@ class Backend
[ [
// row 1 // row 1
'', '',
!empty($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ? is_numeric($this->session->get('EUID')) ?
$_SESSION['EUID'] : null, $this->session->get('EUID') : null,
!empty($_SESSION['ECUID']) && is_string($_SESSION['ECUID']) ? is_string($this->session->get('ECUID')) ?
$_SESSION['ECUID'] : null, $this->session->get('ECUID') : null,
!empty($_SESSION['ECUUID']) && Uids::validateUuuidv4($_SESSION['ECUID']) ? !empty($this->session->get('ECUUID')) && Uids::validateUuuidv4($this->session->get('ECUID')) ?
$_SESSION['ECUID'] : null, $this->session->get('ECUID') : null,
(string)$event, (string)$event,
'', '',
$data_write, $data_write,
@@ -468,7 +468,7 @@ class Backend
} }
// get the session pages array // get the session pages array
$PAGES = $_SESSION['PAGES'] ?? null; $PAGES = $this->session->get('PAGES');
if (!isset($PAGES) || !is_array($PAGES)) { if (!isset($PAGES) || !is_array($PAGES)) {
$PAGES = []; $PAGES = [];
} }

View File

@@ -97,6 +97,23 @@ class Session
return true; return true;
} }
/**
* validate _SESSION key, must be valid variable
*
* @param int|float|string $key
* @return true
*/
private function checkValidSessionEntryKey(int|float|string $key): true
{
if (!is_string($key) || is_numeric($key)) {
throw new \UnexpectedValueException(
'[SESSION] Given key for _SESSION is not a valid value for a varaible: ' . $key,
1
);
}
return true;
}
// MARK: init session (on class start) // MARK: init session (on class start)
/** /**
@@ -162,6 +179,9 @@ class Session
public function restartSession(): bool public function restartSession(): bool
{ {
if (!$this->checkActiveSession()) { if (!$this->checkActiveSession()) {
if (empty($this->session_name)) {
throw new \RuntimeException('[SESSION] Cannot restart session without a session name', 1);
}
$this->startSessionCall(); $this->startSessionCall();
return true; return true;
} }
@@ -235,6 +255,21 @@ class Session
return \CoreLibs\Get\System::checkCLI(); return \CoreLibs\Get\System::checkCLI();
} }
/**
* get session status
* PHP_SESSION_DISABLED if sessions are disabled.
* PHP_SESSION_NONE if sessions are enabled, but none exists.
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
*
* https://www.php.net/manual/en/function.session-status.php
*
* @return int See possible return int values above
*/
public function getSessionStatus(): int
{
return session_status();
}
// MARK: write close session // MARK: write close session
/** /**
@@ -256,13 +291,18 @@ class Session
* Proper destroy a session * Proper destroy a session
* - unset the _SESSION array * - unset the _SESSION array
* - unset cookie if cookie on and we have not strict mode * - unset cookie if cookie on and we have not strict mode
* - unset session_name and session_id internal vars
* - destroy session * - destroy session
* *
* @return bool * @return bool True on successful session destroy
*/ */
public function sessionDestroy(): bool public function sessionDestroy(): bool
{ {
$_SESSION = []; // abort to false if not unsetable
if (!session_unset()) {
return false;
}
$this->clear();
if ( if (
ini_get('session.use_cookies') && ini_get('session.use_cookies') &&
!ini_get('session.use_strict_mode') !ini_get('session.use_strict_mode')
@@ -282,24 +322,12 @@ class Session
$params['httponly'] $params['httponly']
); );
} }
// unset internal vars
$this->session_name = '';
$this->session_id = '';
return session_destroy(); return session_destroy();
} }
/**
* get session status
* PHP_SESSION_DISABLED if sessions are disabled.
* PHP_SESSION_NONE if sessions are enabled, but none exists.
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
*
* https://www.php.net/manual/en/function.session-status.php
*
* @return int See possible return int values above
*/
public function getSessionStatus(): int
{
return session_status();
}
// MARK: _SESSION set/unset methods // MARK: _SESSION set/unset methods
/** /**
@@ -307,11 +335,14 @@ class Session
* *
* @return void * @return void
*/ */
public function unsetAll(): void public function clear(): void
{ {
$this->restartSession(); $this->restartSession();
foreach (array_keys($_SESSION ?? []) as $name) { if (!session_unset()) {
unset($_SESSION[$name]); throw new \RuntimeException('[SESSION] Cannot unset session vars', 1);
}
if (!empty($_SESSION)) {
$_SESSION = [];
} }
$this->closeSessionCall(); $this->closeSessionCall();
} }
@@ -319,35 +350,64 @@ class Session
/** /**
* set _SESSION entry 'name' with any value * set _SESSION entry 'name' with any value
* *
* @param string|int $name array name in _SESSION * @param string $name array name in _SESSION
* @param mixed $value value to set (can be anything) * @param mixed $value value to set (can be anything)
* @return void * @return void
*/ */
public function set(string|int $name, mixed $value): void public function set(string $name, mixed $value): void
{ {
$this->checkValidSessionEntryKey($name);
$this->restartSession(); $this->restartSession();
$_SESSION[$name] = $value; $_SESSION[$name] = $value;
$this->closeSessionCall(); $this->closeSessionCall();
} }
/**
* set many session entries in one set
*
* @param array<string,mixed> $set key is the key in the _SESSION, value is any data to set
* @return void
*/
public function setMany(array $set): void
{
$this->restartSession();
// skip any that are not valid
foreach ($set as $key => $value) {
$this->checkValidSessionEntryKey($key);
$_SESSION[$key] = $value;
}
$this->closeSessionCall();
}
/** /**
* get _SESSION 'name' entry or empty string if not set * get _SESSION 'name' entry or empty string if not set
* *
* @param string|int $name value key to get from _SESSION * @param string $name value key to get from _SESSION
* @return mixed value stored in _SESSION * @return mixed value stored in _SESSION, if not found set to null
*/ */
public function get(string|int $name): mixed public function get(string $name): mixed
{ {
return $_SESSION[$name] ?? null; return $_SESSION[$name] ?? null;
} }
/**
* get multiple session entries
*
* @param array<string> $set
* @return array<string,mixed>
*/
public function getMany(array $set): array
{
return array_intersect_key($_SESSION, array_flip($set));
}
/** /**
* Check if a name is set in the _SESSION array * Check if a name is set in the _SESSION array
* *
* @param string|int $name Name to check for * @param string $name Name to check for
* @return bool True for set, False fornot set * @return bool True for set, False fornot set
*/ */
public function isset(string|int $name): bool public function isset(string $name): bool
{ {
return isset($_SESSION[$name]); return isset($_SESSION[$name]);
} }
@@ -355,10 +415,10 @@ class Session
/** /**
* unset one _SESSION entry 'name' if exists * unset one _SESSION entry 'name' if exists
* *
* @param string|int $name _SESSION key name to remove * @param string $name _SESSION key name to remove
* @return void * @return void
*/ */
public function unset(string|int $name): void public function unset(string $name): void
{ {
if (!isset($_SESSION[$name])) { if (!isset($_SESSION[$name])) {
return; return;
@@ -368,65 +428,21 @@ class Session
$this->closeSessionCall(); $this->closeSessionCall();
} }
// MARK: [DEPRECATED] __set/__get magic methods
// ->var = value;
/** /**
* Undocumented function * reset many session entry
* *
* @param string|int $name * @param array<string> $set list of session keys to reset
* @param mixed $value
* @return void * @return void
* @deprecated use ->set()
*/ */
public function __set(string|int $name, mixed $value): void public function unsetMany(array $set): void
{ {
$this->restartSession(); $this->restartSession();
$_SESSION[$name] = $value; foreach ($set as $key) {
$this->closeSessionCall(); if (!isset($_SESSION[$key])) {
} continue;
}
/** unset($_SESSION[$key]);
* Undocumented function
*
* @param string|int $name
* @return mixed If name is not found, it will return null
* @deprecated use ->get()
*/
public function __get(string|int $name): mixed
{
if (isset($_SESSION[$name])) {
return $_SESSION[$name];
} }
return null;
}
/**
* Undocumented function
*
* @param string|int $name
* @return bool
* @deprecated use ->isset()
*/
public function __isset(string|int $name): bool
{
return isset($_SESSION[$name]);
}
/**
* Undocumented function
*
* @param string|int $name
* @return void
* @deprecated use ->unset()
*/
public function __unset(string|int $name): void
{
if (!isset($_SESSION[$name])) {
return;
}
$this->restartSession();
unset($_SESSION[$name]);
$this->closeSessionCall(); $this->closeSessionCall();
} }
} }

View File

@@ -14,10 +14,24 @@ namespace CoreLibs\DB\Support;
class ConvertPlaceholder class ConvertPlaceholder
{ {
/** @var string split regex */ // NOTE for missing: range */+ are not iplemented in the regex below, but - is for now
private const PATTERN_QUERY_SPLIT = '[(<>=,?-]|->|->>|#>|#>>|@>|<@|\?\|\?\&|\|\||#-'; // NOTE some combinations are allowed, but the query will fail before this
/** @var string split regex, entries before $ group */
private const PATTERN_QUERY_SPLIT =
'\?\?|' // UNKNOWN: double ??, is this to avoid something?
. '[\(,]|' // for ',' and '(' mostly in INSERT or ANY()
. '[<>=]|' // general set for <, >, = in any query with any combination
. '\^@|' // text search for start from text with ^@
. '\|\||' // concats two elements
. '&&|' // array overlap
. '\-\|\-|' // range overlap for array
. '[^-]-{1}|' // single -, used in JSON too
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-'; //JSON searches, Array searchs, etc
/** @var string the main regex including the pattern query split */ /** @var string the main regex including the pattern query split */
private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:\?\?|' . self::PATTERN_QUERY_SPLIT . ')\s*'; private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:' . self::PATTERN_QUERY_SPLIT . ')\s*';
/** @var string comment regex
* anything that starts with -- and ends with a line break but any character that is not line break inbetween */
private const PATTERN_COMMENT = '(?:\-\-[^\r\n]*?\r?\n)*\s*';
/** @var string parts to ignore in the SQL */ /** @var string parts to ignore in the SQL */
private const PATTERN_IGNORE = private const PATTERN_IGNORE =
// digit -> ignore // digit -> ignore
@@ -34,6 +48,7 @@ class ConvertPlaceholder
/** @var string replace regex for named (:...) entries */ /** @var string replace regex for named (:...) entries */
public const REGEX_REPLACE_NAMED = '/' public const REGEX_REPLACE_NAMED = '/'
. '(' . self::PATTERN_ELEMENT . ')' . '(' . self::PATTERN_ELEMENT . ')'
. self::PATTERN_COMMENT
. '(' . '('
. self::PATTERN_IGNORE . self::PATTERN_IGNORE
. self::PATTERN_NAMED . self::PATTERN_NAMED
@@ -42,6 +57,7 @@ class ConvertPlaceholder
/** @var string replace regex for question mark (?) entries */ /** @var string replace regex for question mark (?) entries */
public const REGEX_REPLACE_QUESTION_MARK = '/' public const REGEX_REPLACE_QUESTION_MARK = '/'
. '(' . self::PATTERN_ELEMENT . ')' . '(' . self::PATTERN_ELEMENT . ')'
. self::PATTERN_COMMENT
. '(' . '('
. self::PATTERN_IGNORE . self::PATTERN_IGNORE
. self::PATTERN_QUESTION_MARK . self::PATTERN_QUESTION_MARK
@@ -50,6 +66,7 @@ class ConvertPlaceholder
/** @var string replace regex for numbered ($n) entries */ /** @var string replace regex for numbered ($n) entries */
public const REGEX_REPLACE_NUMBERED = '/' public const REGEX_REPLACE_NUMBERED = '/'
. '(' . self::PATTERN_ELEMENT . ')' . '(' . self::PATTERN_ELEMENT . ')'
. self::PATTERN_COMMENT
. '(' . '('
. self::PATTERN_IGNORE . self::PATTERN_IGNORE
. self::PATTERN_NUMBERED . self::PATTERN_NUMBERED
@@ -60,6 +77,7 @@ class ConvertPlaceholder
// prefix string part, must match towards // prefix string part, must match towards
// seperator for ( = , ? - [and json/jsonb in pg doc section 9.15] // seperator for ( = , ? - [and json/jsonb in pg doc section 9.15]
. self::PATTERN_ELEMENT . self::PATTERN_ELEMENT
. self::PATTERN_COMMENT
// match for replace part // match for replace part
. '(?:' . '(?:'
// ignore parts // ignore parts

View File

@@ -2,7 +2,7 @@
/* /*
* sets a form token in the _SESSION variable * sets a form token in the _SESSION variable
* session must be started for this to work * session must be started and running for this to work
*/ */
declare(strict_types=1); declare(strict_types=1);

View File

@@ -49,7 +49,11 @@ class SymmetricEncryption
*/ */
public static function getInstance(string|null $key = null): self public static function getInstance(string|null $key = null): self
{ {
if (empty(self::$instance)) { // new if no instsance or key is different
if (
empty(self::$instance) ||
self::$instance->key != $key
) {
self::$instance = new self($key); self::$instance = new self($key);
} }
return self::$instance; return self::$instance;
@@ -130,7 +134,7 @@ class SymmetricEncryption
*/ */
private function encryptData(string $message, ?string $key): string private function encryptData(string $message, ?string $key): string
{ {
if (empty($this->key) || $key === null) { if ($key === null) {
throw new \UnexpectedValueException('Key not set'); throw new \UnexpectedValueException('Key not set');
} }
$key = $this->createKey($key); $key = $this->createKey($key);

View File

@@ -321,7 +321,7 @@ CREATE TABLE edit_generic (
-- DROP TABLE edit_visible_group; -- DROP TABLE edit_visible_group;
CREATE TABLE edit_visible_group ( CREATE TABLE edit_visible_group (
edit_visible_group_id SERIAL PRIMARY KEY, edit_visible_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR, name VARCHAR,
flag VARCHAR flag VARCHAR
) INHERITS (edit_generic) WITHOUT OIDS; ) INHERITS (edit_generic) WITHOUT OIDS;
@@ -336,7 +336,7 @@ CREATE TABLE edit_visible_group (
-- DROP TABLE edit_menu_group; -- DROP TABLE edit_menu_group;
CREATE TABLE edit_menu_group ( CREATE TABLE edit_menu_group (
edit_menu_group_id SERIAL PRIMARY KEY, edit_menu_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR, name VARCHAR,
flag VARCHAR, flag VARCHAR,
order_number INT NOT NULL order_number INT NOT NULL
@@ -354,7 +354,7 @@ CREATE TABLE edit_menu_group (
-- DROP TABLE edit_page; -- DROP TABLE edit_page;
CREATE TABLE edit_page ( CREATE TABLE edit_page (
edit_page_id SERIAL PRIMARY KEY, edit_page_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages
FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE, FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
filename VARCHAR, filename VARCHAR,
@@ -378,7 +378,7 @@ CREATE TABLE edit_page (
-- DROP TABLE edit_query_string; -- DROP TABLE edit_query_string;
CREATE TABLE edit_query_string ( CREATE TABLE edit_query_string (
edit_query_string_id SERIAL PRIMARY KEY, edit_query_string_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_page_id INT NOT NULL, edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
@@ -430,7 +430,7 @@ CREATE TABLE edit_page_menu_group (
-- DROP TABLE edit_access_right; -- DROP TABLE edit_access_right;
CREATE TABLE edit_access_right ( CREATE TABLE edit_access_right (
edit_access_right_id SERIAL PRIMARY KEY, edit_access_right_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR, name VARCHAR,
level SMALLINT, level SMALLINT,
type VARCHAR, type VARCHAR,
@@ -447,7 +447,7 @@ CREATE TABLE edit_access_right (
-- DROP TABLE edit_scheme; -- DROP TABLE edit_scheme;
CREATE TABLE edit_scheme ( CREATE TABLE edit_scheme (
edit_scheme_id SERIAL PRIMARY KEY, edit_scheme_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR, name VARCHAR,
header_color VARCHAR, header_color VARCHAR,
@@ -466,7 +466,7 @@ CREATE TABLE edit_scheme (
-- DROP TABLE edit_language; -- DROP TABLE edit_language;
CREATE TABLE edit_language ( CREATE TABLE edit_language (
edit_language_id SERIAL PRIMARY KEY, edit_language_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
lang_default SMALLINT NOT NULL DEFAULT 0, lang_default SMALLINT NOT NULL DEFAULT 0,
long_name VARCHAR, long_name VARCHAR,
@@ -485,7 +485,7 @@ CREATE TABLE edit_language (
-- DROP TABLE edit_group; -- DROP TABLE edit_group;
CREATE TABLE edit_group ( CREATE TABLE edit_group (
edit_group_id SERIAL PRIMARY KEY, edit_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_scheme_id INT, edit_scheme_id INT,
FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL, edit_access_right_id INT NOT NULL,
@@ -507,7 +507,7 @@ CREATE TABLE edit_group (
-- DROP TABLE edit_page_access; -- DROP TABLE edit_page_access;
CREATE TABLE edit_page_access ( CREATE TABLE edit_page_access (
edit_page_access_id SERIAL PRIMARY KEY, edit_page_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_group_id INT NOT NULL, edit_group_id INT NOT NULL,
FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_page_id INT NOT NULL, edit_page_id INT NOT NULL,
@@ -530,7 +530,7 @@ CREATE TABLE edit_page_access (
-- DROP TABLE edit_page_content; -- DROP TABLE edit_page_content;
CREATE TABLE edit_page_content ( CREATE TABLE edit_page_content (
edit_page_content_id SERIAL PRIMARY KEY, edit_page_content_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_page_id INT NOT NULL, edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL, edit_access_right_id INT NOT NULL,
@@ -551,7 +551,7 @@ CREATE TABLE edit_page_content (
-- DROP TABLE edit_user; -- DROP TABLE edit_user;
CREATE TABLE edit_user ( CREATE TABLE edit_user (
edit_user_id SERIAL PRIMARY KEY, edit_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
connect_edit_user_id INT, -- possible reference to other user connect_edit_user_id INT, -- possible reference to other user
FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_language_id INT NOT NULL, edit_language_id INT NOT NULL,
@@ -652,11 +652,11 @@ COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List st
-- DROP TABLE edit_log; -- DROP TABLE edit_log;
CREATE TABLE edit_log ( CREATE TABLE edit_log (
edit_log_id SERIAL PRIMARY KEY, edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
euid INT, -- this is a foreign key, but I don't nedd to reference to it euid INT, -- this is a foreign key, but I don't nedd to reference to it
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
ecuid VARCHAR, ecuid VARCHAR,
ecuuid UUID, ecuuid UUID,
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
username VARCHAR, username VARCHAR,
password VARCHAR, password VARCHAR,
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
@@ -712,7 +712,7 @@ ALTER TABLE edit_log_overflow ADD CONSTRAINT edit_log_overflow_euid_fkey FOREIGN
-- DROP TABLE edit_access; -- DROP TABLE edit_access;
CREATE TABLE edit_access ( CREATE TABLE edit_access (
edit_access_id SERIAL PRIMARY KEY, edit_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
protected SMALLINT DEFAULT 0, protected SMALLINT DEFAULT 0,
deleted SMALLINT DEFAULT 0, deleted SMALLINT DEFAULT 0,
@@ -733,7 +733,7 @@ CREATE TABLE edit_access (
-- DROP TABLE edit_access_user; -- DROP TABLE edit_access_user;
CREATE TABLE edit_access_user ( CREATE TABLE edit_access_user (
edit_access_user_id SERIAL PRIMARY KEY, edit_access_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_access_id INT NOT NULL, edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_user_id INT NOT NULL, edit_user_id INT NOT NULL,
@@ -754,7 +754,7 @@ CREATE TABLE edit_access_user (
-- DROP TABLE edit_access_data; -- DROP TABLE edit_access_data;
CREATE TABLE edit_access_data ( CREATE TABLE edit_access_data (
edit_access_data_id SERIAL PRIMARY KEY, edit_access_data_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_access_id INT NOT NULL, edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,

View File

@@ -272,24 +272,27 @@ final class CoreLibsCreateSessionTest extends TestCase
* *
* @return array * @return array
*/ */
public function sessionDataProvider(): array public function providerSessionData(): array
{ {
return [ return [
'test' => [ 'test' => [
'foo', 'foo',
'bar', 'bar',
'bar', 'bar',
null,
], ],
'int key test' => [ 'int key test' => [
123, 123,
'bar', 'bar',
'bar', 'bar',
\UnexpectedValueException::class
], ],
// more complex value tests // more complex value tests
'array values' => [ 'array values' => [
'array', 'array',
[1, 2, 3], [1, 2, 3],
[1, 2, 3], [1, 2, 3],
null,
] ]
]; ];
} }
@@ -299,21 +302,28 @@ final class CoreLibsCreateSessionTest extends TestCase
/** /**
* method call test * method call test
* *
* @covers ::setS * @covers ::set
* @covers ::getS * @covers ::get
* @covers ::issetS * @covers ::isset
* @covers ::unsetS * @covers ::unset
* @dataProvider sessionDataProvider * @dataProvider providerSessionData
* @testdox setS/getS/issetS/unsetS $name with $input is $expected [$_dataName] * @testdox set/get/isset/unset $name with $input is $expected ($exception) [$_dataName]
* *
* @param string|int $name * @param string|int $name
* @param mixed $input * @param mixed $input
* @param mixed $expected * @param mixed $expected
* @param ?mixed $exception
* @return void * @return void
*/ */
/* public function testMethodSetGet($name, $input, $expected): void public function testMethodSetGet($name, $input, $expected, $exception): void
{ {
if (\CoreLibs\Get\System::checkCLI()) {
$this->markTestSkipped('Cannot run testMethodSetGet in CLI');
}
$session = new \CoreLibs\Create\Session('TEST_METHOD'); $session = new \CoreLibs\Create\Session('TEST_METHOD');
if ($expected !== null) {
$this->expectException($exception);
}
$session->set($name, $input); $session->set($name, $input);
$this->assertEquals( $this->assertEquals(
$expected, $expected,
@@ -331,12 +341,80 @@ final class CoreLibsCreateSessionTest extends TestCase
$session->get($name), $session->get($name),
'method unset assert' 'method unset assert'
); );
// iset false // isset false
$this->assertFalse( $this->assertFalse(
$session->isset($name), $session->isset($name),
'method isset assert false' 'method isset assert false'
); );
} */ }
/**
* Undocumented function
*
* @return array
*/
public function providerSessionDataMany(): array
{
return [
'valid set' => [
[
'foo 1' => 'bar 1',
'foo 2' => 'bar 1',
],
[
'foo 1' => 'bar 1',
'foo 2' => 'bar 1',
],
null,
],
'invalid entry' => [
[
'foo 1' => 'bar 1',
123 => 'bar 1',
],
[
'foo 1' => 'bar 1',
],
\UnexpectedValueException::class
]
];
}
/**
* Undocumented function
*
* @covers ::setMany
* @covers ::getMany
* @dataProvider providerSessionDataMany
* @testdox setMany/getMany/unsetMany $set is $expected ($exception) [$_dataName]
*
* @param array<string|int,mixed> $set
* @param array<string,mixed> $expected
* @param ?mixed $exception
* @return void
*/
public function testMany($set, $expected, $exception): void
{
if (\CoreLibs\Get\System::checkCLI()) {
$this->markTestSkipped('Cannot run testMethodSetGet in CLI');
}
$session = new \CoreLibs\Create\Session('TEST_METHOD');
if ($expected !== null) {
$this->expectException($exception);
}
$session->setMany($set);
$this->assertEquals(
$expected,
$session->getMany(array_keys($set)),
'set many failed'
);
$session->unsetMany(array_keys($set));
$this->assertEquals(
[],
$session->getMany(array_keys($set)),
'unset many failed'
);
}
/** /**
* unset all test * unset all test
@@ -346,8 +424,11 @@ final class CoreLibsCreateSessionTest extends TestCase
* *
* @return void * @return void
*/ */
/* public function testUnsetAll(): void public function testUnsetAll(): void
{ {
if (\CoreLibs\Get\System::checkCLI()) {
$this->markTestSkipped('Cannot run testUnsetAll in CLI');
}
$test_values = [ $test_values = [
'foo' => 'abc', 'foo' => 'abc',
'bar' => '123' 'bar' => '123'
@@ -363,7 +444,7 @@ final class CoreLibsCreateSessionTest extends TestCase
); );
} }
// unset all // unset all
$session->unsetAll(); $session->clear();
// check unset // check unset
foreach (array_keys($test_values) as $name) { foreach (array_keys($test_values) as $name) {
$this->assertEquals( $this->assertEquals(
@@ -372,7 +453,7 @@ final class CoreLibsCreateSessionTest extends TestCase
'unsert assert: ' . $name 'unsert assert: ' . $name
); );
} }
} */ }
} }
// __END__ // __END__

View File

@@ -17,7 +17,7 @@ Table with Primary Key: table_with_primary_key
Table without Primary Key: table_without_primary_key Table without Primary Key: table_without_primary_key
Table with primary key has additional row: Table with primary key has additional row:
row_primary_key SERIAL PRIMARY KEY, row_primary_key INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Each table has the following rows Each table has the following rows
row_int INT, row_int INT,
row_numeric NUMERIC, row_numeric NUMERIC,
@@ -160,7 +160,6 @@ final class CoreLibsDBIOTest extends TestCase
// create the tables // create the tables
$db->dbExec( $db->dbExec(
// primary key name is table + '_id' // primary key name is table + '_id'
// table_with_primary_key_id SERIAL PRIMARY KEY,
<<<SQL <<<SQL
CREATE TABLE table_with_primary_key ( CREATE TABLE table_with_primary_key (
table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
@@ -5136,6 +5135,67 @@ final class CoreLibsDBIOTest extends TestCase
SQL, SQL,
'count' => 6, 'count' => 6,
'convert' => false, 'convert' => false,
],
'comments in insert' => [
'query' => <<<SQL
INSERT INTO table_with_primary_key (
row_int, row_numeric, row_varchar, row_varchar_literal
) VALUES (
-- comment 1 かな
$1, $2,
-- comment 2 -
$3
-- comment 3
, $4
)
SQL,
'count' => 4,
'convert' => false
],
'comment in update' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
row_int =
-- COMMENT 1
$1,
row_numeric =
$2 -- COMMENT 2
,
row_varchar -- COMMENT 3
= $3
WHERE
row_varchar = $4
SQL,
'count' => 4,
'convert' => false,
],
// Note some are not set
'a complete set of possible' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
-- ROW
row_varchar = $1
WHERE
row_varchar = ANY($2) AND row_varchar <> $3
AND row_varchar > $4 AND row_varchar < $5
AND row_varchar >= $6 AND row_varchar <=$7
AND row_jsonb->'a' = $8 AND row_jsonb->>$9 = 'a'
AND row_jsonb<@$10 AND row_jsonb@>$11
AND row_varchar ^@ $12
SQL,
'count' => 12,
'convert' => false,
],
// all the same
'all the same numbered' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
row_int = $1::INT, row_numeric = $1::NUMERIC, row_varchar = $1
WHERE
row_varchar = $1
SQL,
'count' => 1,
'convert' => false,
] ]
]; ];
} }

View File

@@ -0,0 +1,2 @@
*
!.gitignore

View File

@@ -56,7 +56,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$decrypted, $decrypted,
'Class call', 'Class call',
); );
}
/**
* 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 = CreateKey::generateRandomKey();
// test indirect // test indirect
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
$decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted); $decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted);
@@ -65,7 +82,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$decrypted, $decrypted,
'Class Instance call', 'Class Instance call',
); );
}
/**
* 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 = CreateKey::generateRandomKey();
// test static // test static
$encrypted = SymmetricEncryption::encryptKey($input, $key); $encrypted = SymmetricEncryption::encryptKey($input, $key);
$decrypted = SymmetricEncryption::decryptKey($encrypted, $key); $decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
@@ -114,13 +148,51 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$crypt = new SymmetricEncryption($key); $crypt = new SymmetricEncryption($key);
$encrypted = $crypt->encrypt($input); $encrypted = $crypt->encrypt($input);
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
$crypt->setKey($key); $crypt->setKey($wrong_key);
$crypt->decrypt($encrypted); $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 = CreateKey::generateRandomKey();
$wrong_key = CreateKey::generateRandomKey();
// class instance // class instance
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted); SymmetricEncryption::getInstance($wrong_key)->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 = CreateKey::generateRandomKey();
$wrong_key = CreateKey::generateRandomKey();
// class static // class static
$encrypted = SymmetricEncryption::encryptKey($input, $key); $encrypted = SymmetricEncryption::encryptKey($input, $key);
@@ -190,6 +262,56 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
SymmetricEncryption::decryptKey($encrypted, $key); SymmetricEncryption::decryptKey($encrypted, $key);
} }
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKey
* @testdox wrong key indirect $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKeyIndirect(string $key, string $exception_message): void
{
$enc_key = CreateKey::generateRandomKey();
// 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);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKey
* @testdox wrong key static $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKeyStatic(string $key, string $exception_message): void
{
$enc_key = CreateKey::generateRandomKey();
// 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);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -232,6 +354,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key); SymmetricEncryption::decryptKey($input, $key);
} }
/**
* Undocumented function
*
* @covers ::decrypt
* @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::generateRandomKey();
// class instance
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($key)->decrypt($input);
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
/**
* Undocumented function
*
* @covers ::decrypt
* @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::generateRandomKey();
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
} }
// __END__ // __END__