Compare commits

...

38 Commits

Author SHA1 Message Date
Clemens Schwaighofer
ace02b14d8 Merge branch 'development' 2025-01-17 14:39:05 +09:00
Clemens Schwaighofer
58e916d314 Fix ACL Login edit access cuid <-> id lookup 2025-01-17 14:38:41 +09:00
Clemens Schwaighofer
4f6d85f4da Release: v9.26.5 2025-01-17 12:52:30 +09:00
Clemens Schwaighofer
cd45590a72 ACL Login add lookup edit access id to cuid 2025-01-17 12:51:25 +09:00
Clemens Schwaighofer
4d42da201c Release: v9.26.4 2025-01-17 10:06:57 +09:00
Clemens Schwaighofer
e310cb626a Logging file block separator character change, deprecated php 8.4 helpers 2025-01-17 10:05:54 +09:00
Clemens Schwaighofer
c04c71d755 Release: v9.26.3 2025-01-16 14:52:16 +09:00
Clemens Schwaighofer
9fc40a6629 ACL Login add edit access id to acl array 2025-01-16 14:51:29 +09:00
Clemens Schwaighofer
6362e7f2f0 Release: v9.26.2 2025-01-16 14:40:08 +09:00
Clemens Schwaighofer
50dfc10d31 Merge branch 'development' 2025-01-16 14:38:59 +09:00
Clemens Schwaighofer
24077e483f ACL Login add edit access id to cuid lookup 2025-01-16 14:38:49 +09:00
Clemens Schwaighofer
6585c6bfef Release: v9.26.1 2025-01-16 14:11:41 +09:00
Clemens Schwaighofer
f180046283 ACL Login unit detail info update, deprecated message fix 2025-01-16 14:10:46 +09:00
Clemens Schwaighofer
b64d0ce5f0 Release: v9.26.0 2025-01-16 10:27:00 +09:00
Clemens Schwaighofer
bab8460f80 PHP 8.4 compatible release 2025-01-16 10:25:58 +09:00
Clemens Schwaighofer
a092217201 Release: v9.25.3 2024-12-24 12:52:33 +09:00
Clemens Schwaighofer
e286d7f913 DB IO placeholder counter fix 2024-12-24 12:49:49 +09:00
Clemens Schwaighofer
e148a39902 Release: v9.25.2 2024-12-23 11:37:26 +09:00
Clemens Schwaighofer
b7d5a79c3a Allow method chaining in Session and encryption class
For session set/unset/auto write close flag

In the encryption classes for setting keys
2024-12-23 11:36:06 +09:00
Clemens Schwaighofer
9f8a86b4b0 Release:v 9.25.1.1 2024-12-18 10:59:47 +09:00
Clemens Schwaighofer
50e593789e Asyemmetric Anonymous Encryption 2024-12-18 10:55:16 +09:00
Clemens Schwaighofer
4ee141f8df Release: v9.24.1 2024-12-13 11:47:05 +09:00
Clemens Schwaighofer
9ee8f43478 Rename all table columns from ecuid and ecuuid to eucuid and eucuuid 2024-12-13 11:45:16 +09:00
Clemens Schwaighofer
2c75dbdf6c Release: v9.24.0 2024-12-13 11:12:39 +09:00
Clemens Schwaighofer
5fe61388fc phpunit xml update 2024-12-13 11:11:42 +09:00
Clemens Schwaighofer
a03c7e7319 Class ACL Login and Session update
Session:
- can recreate session id periodic (Default never)
- options are set via array like in other classes
- checks for strict session settings on default

ACL Login:
- remove all DEBUG/DB_DEBUG variables, calls, etc
	- removed from the EditBase/EditUsers classes too
- switch to UUIDv4 as the session lookup variable
- all session vars are prefixed with "LOGIN_"
	- the charset ones are left as DEFAULT_CHARSET, DEFAULT_LOCALE, DEFAULT_LANG
	- the old LOGIN_LANG has been removed (deprecated)
	- TEMPLATE session has been removed, there is no template data in the edit class
- session is resynced (ACL lookup), default 5min, adjustable via option
- sets strict header options as default
- moves several methods parts into their own classes
	- plan to split up class into sub classes for certain actions
- new force logout counter in DB
- edit logger is moved into this class
	- plan to move logging into sub class
- all SQL calls user heredoc and params
- update login/change password to new layout for pc/smartphone compatible
	- change password will be replaced with reset password in future
- last login success is now set as timestamp
- all old PK lookups for edit access etc are deprecated and replaced with cuid lookups

ArrayHandling:
- add array return matching key
Give any array with key values and a list of keys and only return matching keys
Wrapper for array_filter call
2024-12-13 10:54:20 +09:00
Clemens Schwaighofer
7e01152bb4 Release: v9.23.3 2024-12-12 21:12:24 +09:00
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
Clemens Schwaighofer
0f7bf0ab44 Session class rewrite 2024-12-04 14:22:26 +09:00
Clemens Schwaighofer
10dc56c7cb Release: v9.22.0 2024-12-03 13:34:56 +09:00
33 changed files with 4427 additions and 1697 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

@@ -4,4 +4,9 @@
verbose="true" verbose="true"
bootstrap="test/phpunit/bootstrap.php" bootstrap="test/phpunit/bootstrap.php"
> >
<testsuites>
<testsuite name="deploy">
<directory>test/phpunit</directory>
</testsuite>
</testsuites>
</phpunit> </phpunit>

View File

@@ -1 +1 @@
9.21.1 9.26.5

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/12/12
* DESCRIPTION:
* ACL Login user status bitmap list
*/
declare(strict_types=1);
namespace CoreLibs\ACL;
final class LoginUserStatus
{
// lock status bitmap (smallint, 256)
/** @var int enabled flag */
public const ENABLED = 1;
/** @var int deleted flag */
public const DELETED = 2;
/** @var int locked flag */
public const LOCKED = 4;
/** @var int banned/suspened flag [not implemented] */
public const BANNED = 8;
/** @var int password reset in progress [not implemented] */
public const RESET = 16;
/** @var int confirm/paending, eg waiting for confirm of email [not implemented] */
public const CONFIRM = 32;
/** @var int strict, on error lock */
public const STRICT = 64;
/** @var int proected, cannot delete */
public const PROTECTED = 128;
/** @var int master admin flag */
public const ADMIN = 256;
/**
* Returns an array mapping the numerical role values to their descriptive names
*
* @return array<int|string,string>
*/
public static function getMap()
{
return array_flip((new \ReflectionClass(static::class))->getConstants());
}
/**
* Returns the descriptive role names
*
* @return string[]
*/
public static function getNames()
{
return array_keys((new \ReflectionClass(static::class))->getConstants());
}
/**
* Returns the numerical role values
*
* @return int[]
*/
public static function getValues()
{
return array_values((new \ReflectionClass(static::class))->getConstants());
}
}
// __END__

View File

@@ -289,7 +289,7 @@ class Backend
* JSON, STRING/SERIEAL, BINARY/BZIP or ZLIB * JSON, STRING/SERIEAL, BINARY/BZIP or ZLIB
* @param string|null $db_schema [default=null] override target schema * @param string|null $db_schema [default=null] override target schema
* @return void * @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( public function adbEditLog(
string $event = '', string $event = '',
@@ -358,7 +358,7 @@ class Backend
} }
$q = <<<SQL $q = <<<SQL
INSERT INTO {DB_SCHEMA}.edit_log ( INSERT INTO {DB_SCHEMA}.edit_log (
username, euid, ecuid, ecuuid, event_date, event, error, data, data_binary, page, username, euid, eucuid, eucuuid, event_date, event, error, data, data_binary, page,
ip, user_agent, referer, script_name, query_string, server_name, http_host, ip, user_agent, referer, script_name, query_string, server_name, http_host,
http_accept, http_accept_charset, http_accept_encoding, session_id, http_accept, http_accept_charset, http_accept_encoding, session_id,
action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded, action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded,
@@ -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,
@@ -403,7 +403,7 @@ class Backend
$_SERVER['HTTP_ACCEPT'] ?? '', $_SERVER['HTTP_ACCEPT'] ?? '',
$_SERVER['HTTP_ACCEPT_CHARSET'] ?? '', $_SERVER['HTTP_ACCEPT_CHARSET'] ?? '',
$_SERVER['HTTP_ACCEPT_ENCODING'] ?? '', $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '',
$this->session->getSessionId() !== false ? $this->session->getSessionId() !== '' ?
$this->session->getSessionId() : null, $this->session->getSessionId() : null,
// row 4 // row 4
$this->action ?? '', $this->action ?? '',
@@ -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

@@ -14,9 +14,6 @@ declare(strict_types=1);
namespace CoreLibs\Admin; namespace CoreLibs\Admin;
use Exception;
use SmartyException;
class EditBase class EditBase
{ {
/** @var array<mixed> */ /** @var array<mixed> */
@@ -63,6 +60,7 @@ class EditBase
// smarty template engine (extended Translation version) // smarty template engine (extended Translation version)
$this->smarty = new \CoreLibs\Template\SmartyExtend( $this->smarty = new \CoreLibs\Template\SmartyExtend(
$l10n, $l10n,
$log,
$options['cache_id'] ?? '', $options['cache_id'] ?? '',
$options['compile_id'] ?? '', $options['compile_id'] ?? '',
); );
@@ -415,8 +413,6 @@ class EditBase
$elements[] = $this->form->formCreateElement('lock_until'); $elements[] = $this->form->formCreateElement('lock_until');
$elements[] = $this->form->formCreateElement('lock_after'); $elements[] = $this->form->formCreateElement('lock_after');
$elements[] = $this->form->formCreateElement('admin'); $elements[] = $this->form->formCreateElement('admin');
$elements[] = $this->form->formCreateElement('debug');
$elements[] = $this->form->formCreateElement('db_debug');
$elements[] = $this->form->formCreateElement('edit_language_id'); $elements[] = $this->form->formCreateElement('edit_language_id');
$elements[] = $this->form->formCreateElement('edit_scheme_id'); $elements[] = $this->form->formCreateElement('edit_scheme_id');
$elements[] = $this->form->formCreateElementListTable('edit_access_user'); $elements[] = $this->form->formCreateElementListTable('edit_access_user');
@@ -540,8 +536,7 @@ class EditBase
* builds the smarty content and runs smarty display output * builds the smarty content and runs smarty display output
* *
* @return void * @return void
* @throws Exception * @throws \Smarty\Exception
* @throws SmartyException
*/ */
public function editBaseRun( public function editBaseRun(
?string $template_dir = null, ?string $template_dir = null,

View File

@@ -525,6 +525,32 @@ class ArrayHandler
{ {
return array_diff($array, $remove); return array_diff($array, $remove);
} }
/**
* From the array with key -> mixed values,
* return only the entries where the key matches the key given in the key list parameter
*
* key list is a list[string]
* if key list is empty, return array as is
*
* @param array<string,mixed> $array
* @param array<string> $key_list
* @return array<string,mixed>
*/
public static function arrayReturnMatchingKeyOnly(
array $array,
array $key_list
): array {
// on empty return as is
if (empty($key_list)) {
return $array;
}
return array_filter(
$array,
fn($key) => in_array($key, $key_list),
ARRAY_FILTER_USE_KEY
);
}
} }
// __END__ // __END__

View File

@@ -15,17 +15,111 @@ namespace CoreLibs\Create;
class Session class Session
{ {
/** @var string current session name */
private string $session_name = '';
/** @var string current session id */
private string $session_id = '';
/** @var bool flag auto write close */
private bool $auto_write_close = false;
/** @var string regenerate option, default never */
private string $regenerate = 'never';
/** @var int regenerate interval either 1 to 100 for random or 0 to 3600 for interval */
private int $regenerate_interval = 0;
/** @var array<string> allowed session id regenerate (rotate) options */
private const ALLOWED_REGENERATE_OPTIONS = ['none', 'random', 'interval'];
/** @var int default random interval */
public const DEFAULT_REGENERATE_RANDOM = 100;
/** @var int default rotate internval in minutes */
public const DEFAULT_REGENERATE_INTERVAL = 5 * 60;
/** @var int maximum time for regenerate interval is one hour */
public const MAX_REGENERATE_INTERAL = 60 * 60;
/** /**
* init a session, if array is empty or array does not have session_name set * init a session, if array is empty or array does not have session_name set
* then no auto init is run * then no auto init is run
* *
* @param string $session_name if set and not empty, will start session * @param string $session_name if set and not empty, will start session
* @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options
*/ */
public function __construct(string $session_name = '') public function __construct(
string $session_name,
array $options = []
) {
$this->setOptions($options);
$this->initSession($session_name);
}
// MARK: private methods
/**
* set session class options
*
* @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options
* @return void
*/
private function setOptions(array $options): void
{ {
if (!empty($session_name)) { if (
$this->startSession($session_name); !isset($options['auto_write_close']) ||
!is_bool($options['auto_write_close'])
) {
$options['auto_write_close'] = false;
} }
$this->auto_write_close = $options['auto_write_close'];
if (
!isset($options['session_strict']) ||
!is_bool($options['session_strict'])
) {
$options['session_strict'] = true;
}
// set strict options, on not started sessiononly
if (
$options['session_strict'] &&
$this->getSessionStatus() === PHP_SESSION_NONE
) {
// use cookies to store session IDs
ini_set('session.use_cookies', 1);
// use cookies only (do not send session IDs in URLs)
ini_set('session.use_only_cookies', 1);
// do not send session IDs in URLs
ini_set('session.use_trans_sid', 0);
}
// session regenerate id options
if (
empty($options['regenerate']) ||
!in_array($options['regenerate'], self::ALLOWED_REGENERATE_OPTIONS)
) {
$options['regenerate'] = 'never';
}
$this->regenerate = (string)$options['regenerate'];
// for regenerate: 'random' (default 100)
// regenerate_interval must be between (1 = always) and 100 (1 in 100)
// for regenerate: 'interval' (default 5min)
// regenerate_interval must be 0 = always, to 3600 (every hour)
if (
$options['regenerate'] == 'random' &&
(
!isset($options['regenerate_interval']) ||
!is_numeric($options['regenerate_interval']) ||
$options['regenerate_interval'] < 0 ||
$options['regenerate_interval'] > 100
)
) {
$options['regenerate_interval'] = self::DEFAULT_REGENERATE_RANDOM;
}
if (
$options['regenerate'] == 'interval' &&
(
!isset($options['regenerate_interval']) ||
!is_numeric($options['regenerate_interval']) ||
$options['regenerate_interval'] < 1 ||
$options['regenerate_interval'] > self::MAX_REGENERATE_INTERAL
)
) {
$options['regenerate_interval'] = self::DEFAULT_REGENERATE_INTERVAL;
}
$this->regenerate_interval = (int)($options['regenerate_interval'] ?? 0);
} }
/** /**
@@ -36,38 +130,100 @@ class Session
* *
* @return void * @return void
*/ */
protected function startSessionCall(): void private function startSessionCall(): void
{ {
session_start(); session_start();
} }
/** /**
* check if we are in CLI, we set this, so we can mock this * get current set session id or false if none started
* Not this is just a wrapper for the static System::checkCLI call
* *
* @return bool True if we are in a CLI enviroment, or false for everything else * @return string|false
*/ */
public function checkCliStatus(): bool public function getSessionIdCall(): string|false
{ {
return \CoreLibs\Get\System::checkCLI(); return session_id();
} }
/** /**
* Set session name call. If not valid session name, will return false * automatically closes a session if the auto write close flag is set
* *
* @param string $session_name A valid string for session name * @return bool
* @return bool True if session name is valid,
* False if not
*/ */
public function setSessionName(string $session_name): bool private function closeSessionCall(): bool
{ {
if (!$this->checkValidSessionName($session_name)) { if ($this->auto_write_close) {
return false; return $this->writeClose();
} }
session_name($session_name); return false;
return true;
} }
// MARK: regenerate session
/**
* auto rotate session id
*
* @return void
* @throws \RuntimeException failure to regenerate session id
* @throws \UnexpectedValueException failed to get new session id
* @throws \RuntimeException failed to set new sesson id
* @throws \UnexpectedValueException new session id generated does not match the new set one
*/
private function sessionRegenerateSessionId()
{
// never
if ($this->regenerate == 'never') {
return;
}
// regenerate
if (
!(
// is not session obsolete
empty($_SESSION['SESSION_REGENERATE_OBSOLETE']) &&
(
(
// random
$this->regenerate == 'random' &&
mt_rand(1, $this->regenerate_interval) == 1
) || (
// interval type
$this->regenerate == 'interval' &&
($_SESSION['SESSION_REGENERATE_TIMESTAMP'] ?? 0) + $this->regenerate_interval < time()
)
)
)
) {
return;
}
// Set current session to expire in 1 minute
$_SESSION['SESSION_REGENERATE_OBSOLETE'] = true;
$_SESSION['SESSION_REGENERATE_EXPIRES'] = time() + 60;
$_SESSION['SESSION_REGENERATE_TIMESTAMP'] = time();
// Create new session without destroying the old one
if (session_regenerate_id(false) === false) {
throw new \RuntimeException('[SESSION] Session id regeneration failed', 1);
}
// Grab current session ID and close both sessions to allow other scripts to use them
if (false === ($new_session_id = $this->getSessionIdCall())) {
throw new \UnexpectedValueException('[SESSION] getSessionIdCall did not return a session id', 2);
}
$this->writeClose();
// Set session ID to the new one, and start it back up again
if (($get_new_session_id = session_id($new_session_id)) === false) {
throw new \RuntimeException('[SESSION] set session_id failed', 3);
}
if ($get_new_session_id != $new_session_id) {
throw new \UnexpectedValueException('[SESSION] new session id does not match the new set one', 4);
}
$this->session_id = $new_session_id;
$this->startSessionCall();
// Don't want this one to expire
unset($_SESSION['SESSION_REGENERATE_OBSOLETE']);
unset($_SESSION['SESSION_REGENERATE_EXPIRES']);
}
// MARK: session validation
/** /**
* check if session name is valid * check if session name is valid
* *
@@ -94,15 +250,34 @@ class Session
} }
/** /**
* start session with given session name if set * 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)
/**
* stinitart session with given session name if set
* aborts on command line or if sessions are not enabled * aborts on command line or if sessions are not enabled
* also aborts if session cannot be started * also aborts if session cannot be started
* On sucess returns the session id * On sucess returns the session id
* *
* @param string|null $session_name * @param string $session_name
* @return string|bool * @return void
*/ */
public function startSession(?string $session_name = null): string|bool private function initSession(string $session_name): void
{ {
// we can't start sessions on command line // we can't start sessions on command line
if ($this->checkCliStatus()) { if ($this->checkCliStatus()) {
@@ -115,39 +290,95 @@ class Session
// session_status // session_status
// initial the session if there is no session running already // initial the session if there is no session running already
if (!$this->checkActiveSession()) { if (!$this->checkActiveSession()) {
// if session name is emtpy, check if there is a global set // invalid session name, abort
// this is a deprecated fallback if (!$this->checkValidSessionName($session_name)) {
$session_name = $session_name ?? $GLOBALS['SET_SESSION_NAME'] ?? ''; throw new \UnexpectedValueException('[SESSION] Invalid session name: ' . $this->session_name, 3);
// DEPRECTED: constant SET_SESSION_NAME is no longer used
// if set, set special session name
if (!empty($session_name)) {
// invalid session name, abort
if (!$this->checkValidSessionName($session_name)) {
throw new \UnexpectedValueException('[SESSION] Invalid session name: ' . $session_name, 3);
}
$this->setSessionName($session_name);
} }
// set session name
$this->session_name = $session_name;
session_name($this->session_name);
// start session // start session
$this->startSessionCall(); $this->startSessionCall();
// if we faild to start the session
if (!$this->checkActiveSession()) {
throw new \RuntimeException('[SESSION] Failed to activate session', 5);
}
if (
!empty($_SESSION['SESSION_REGENERATE_OBSOLETE']) &&
!empty($_SESSION['SESSION_REGENERATE_EXPIRES']) && $_SESSION['SESSION_REGENERATE_EXPIRES'] < time()
) {
$this->sessionDestroy();
throw new \RuntimeException('[SESSION] Expired session found', 6);
}
} elseif ($session_name != $this->getSessionName()) {
throw new \UnexpectedValueException(
'[SESSION] Another session exists with a different name: ' . $this->getSessionName(),
4
);
} }
// if we still have no active session // check session id
if (false === ($session_id = $this->getSessionIdCall())) {
throw new \UnexpectedValueException('[SESSION] getSessionIdCall did not return a session id', 7);
}
// set session id
$this->session_id = $session_id;
// run session id re-create from time to time
$this->sessionRegenerateSessionId();
// if flagged auto close, write close session
if ($this->auto_write_close) {
$this->writeClose();
}
}
// MARK: public set/get status
/**
* start session, will only run after initSession
*
* @return bool True if started, False if alrady running
*/
public function restartSession(): bool
{
if (!$this->checkActiveSession()) { if (!$this->checkActiveSession()) {
throw new \RuntimeException('[SESSION] Failed to activate session', 4); if (empty($this->session_name)) {
throw new \RuntimeException('[SESSION] Cannot restart session without a session name', 1);
}
$this->startSessionCall();
return true;
} }
if (false === ($session_id = $this->getSessionId())) { return false;
throw new \UnexpectedValueException('[SESSION] getSessionId did not return a session id', 5);
}
return $session_id;
} }
/** /**
* get current set session id or false if none started * current set session id
* *
* @return string|bool * @return string
*/ */
public function getSessionId(): string|bool public function getSessionId(): string
{ {
return session_id(); return $this->session_id;
}
/**
* set the auto write close flag
*
* @param bool $flag
* @return Session
*/
public function setAutoWriteClose(bool $flag): Session
{
$this->auto_write_close = $flag;
return $this;
}
/**
* return the auto write close flag
*
* @return bool
*/
public function checkAutoWriteClose(): bool
{
return $this->auto_write_close;
} }
/** /**
@@ -175,6 +406,34 @@ class Session
} }
} }
/**
* check if we are in CLI, we set this, so we can mock this
* Not this is just a wrapper for the static System::checkCLI call
*
* @return bool True if we are in a CLI enviroment, or false for everything else
*/
public function checkCliStatus(): bool
{
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
/** /**
* unlock the session file, so concurrent AJAX requests can be done * unlock the session file, so concurrent AJAX requests can be done
* NOTE: after this has been called, no changes in _SESSION will be stored * NOTE: after this has been called, no changes in _SESSION will be stored
@@ -188,17 +447,24 @@ class Session
return session_write_close(); return session_write_close();
} }
// MARK: session close and clean up
/** /**
* 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')
@@ -218,68 +484,93 @@ class Session
$params['httponly'] $params['httponly']
); );
} }
// unset internal vars
$this->session_name = '';
$this->session_id = '';
return session_destroy(); return session_destroy();
} }
/** // MARK: _SESSION set/unset methods
* 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();
}
// _SESSION set/unset methods
/** /**
* unset all _SESSION entries * unset all _SESSION entries
* *
* @return void * @return void
*/ */
public function unsetAllS(): void public function clear(): void
{ {
foreach (array_keys($_SESSION ?? []) as $name) { $this->restartSession();
unset($_SESSION[$name]); if (!session_unset()) {
throw new \RuntimeException('[SESSION] Cannot unset session vars', 1);
} }
if (!empty($_SESSION)) {
$_SESSION = [];
}
$this->closeSessionCall();
} }
/** /**
* 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 Session
*/
public function set(string $name, mixed $value): Session
{
$this->checkValidSessionEntryKey($name);
$this->restartSession();
$_SESSION[$name] = $value;
$this->closeSessionCall();
return $this;
}
/**
* 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 * @return void
*/ */
public function setS(string|int $name, mixed $value): void public function setMany(array $set): void
{ {
$_SESSION[$name] = $value; $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 getS(string|int $name): mixed public function get(string $name): mixed
{ {
return $_SESSION[$name] ?? ''; 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 issetS(string|int $name): bool public function isset(string $name): bool
{ {
return isset($_SESSION[$name]); return isset($_SESSION[$name]);
} }
@@ -287,67 +578,36 @@ 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 Session
*/
public function unset(string $name): Session
{
if (!isset($_SESSION[$name])) {
return $this;
}
$this->restartSession();
unset($_SESSION[$name]);
$this->closeSessionCall();
return $this;
}
/**
* reset many session entry
*
* @param array<string> $set list of session keys to reset
* @return void * @return void
*/ */
public function unsetS(string|int $name): void public function unsetMany(array $set): void
{ {
if (isset($_SESSION[$name])) { $this->restartSession();
unset($_SESSION[$name]); foreach ($set as $key) {
} if (!isset($_SESSION[$key])) {
} continue;
}
// set/get below unset($_SESSION[$key]);
// ->var = value;
/**
* Undocumented function
*
* @param string|int $name
* @param mixed $value
* @return void
*/
public function __set(string|int $name, mixed $value): void
{
$_SESSION[$name] = $value;
}
/**
* Undocumented function
*
* @param string|int $name
* @return mixed If name is not found, it will return null
*/
public function __get(string|int $name): mixed
{
if (isset($_SESSION[$name])) {
return $_SESSION[$name];
}
return null;
}
/**
* Undocumented function
*
* @param string|int $name
* @return bool
*/
public function __isset(string|int $name): bool
{
return isset($_SESSION[$name]);
}
/**
* Undocumented function
*
* @param string|int $name
* @return void
*/
public function __unset(string|int $name): void
{
if (isset($_SESSION[$name])) {
unset($_SESSION[$name]);
} }
$this->closeSessionCall();
} }
} }

View File

@@ -14,10 +14,26 @@ 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
. 'THEN|ELSE' // command parts (CASE)
;
/** @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 +50,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 +59,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 +68,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 +79,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

@@ -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__

View File

@@ -30,6 +30,10 @@ class Logging
{ {
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */ /** @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; 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 // 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}} */ /** @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 = ''; private string $log_folder = '';
/** @var string a alphanumeric name that has to be set as global definition */ /** @var string a alphanumeric name that has to be set as global definition */
private string $log_file_id = ''; 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 */ /** @var string log file name with folder, for actual writing */
private string $log_file_name = ''; private string $log_file_name = '';
/** @var int set in bytes */ /** @var int set in bytes */
@@ -431,7 +433,7 @@ class Logging
private function buildLogFileName(Level $level, string $group_id = ''): string private function buildLogFileName(Level $level, string $group_id = ''): string
{ {
// init base file path // 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 // log ID prefix settings, if not valid, replace with empty
if (!empty($this->log_file_id)) { if (!empty($this->log_file_id)) {
$rpl_string = $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) $fn = str_replace('{LOGID}', $rpl_string, $fn); // log id (like a log file prefix)
$rpl_string = !$this->getLogFlag(Flag::per_level) ? '' : $rpl_string = $this->getLogFlag(Flag::per_level) ?
'_' . $level->getName(); self::LOG_FILE_BLOCK_SEPARATOR . $level->getName() :
'';
$fn = str_replace('{LEVEL}', $rpl_string, $fn); // create output filename $fn = str_replace('{LEVEL}', $rpl_string, $fn); // create output filename
// write per level // 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 - // normalize level, replace all non alphanumeric characters with -
'_' . ( self::LOG_FILE_BLOCK_SEPARATOR . (
// if return is only - then set error string // if return is only - then set error string
preg_match( preg_match(
"/^-+$/", "/^-+$/",
@@ -455,25 +458,29 @@ class Logging
) ? ) ?
'INVALID-LEVEL-STRING' : 'INVALID-LEVEL-STRING' :
$level_string $level_string
); ) :
'';
$fn = str_replace('{GROUP}', $rpl_string, $fn); // create output filename $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 // set per class, but don't use get_class as we will only get self
$rpl_string = !$this->getLogFlag(Flag::per_class) ? '' : '_' $rpl_string = $this->getLogFlag(Flag::per_class) ?
// set sub class settings // set sub class settings
. str_replace('\\', '-', Support::getCallerTopLevelClass()); self::LOG_FILE_BLOCK_SEPARATOR . str_replace('\\', '-', Support::getCallerTopLevelClass()) :
'';
$fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename $fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename
// if request to write to one file // if request to write to one file
$rpl_string = !$this->getLogFlag(Flag::per_page) ? $rpl_string = $this->getLogFlag(Flag::per_page) ?
'' : self::LOG_FILE_BLOCK_SEPARATOR . System::getPageName(System::NO_EXTENSION) :
'_' . System::getPageName(System::NO_EXTENSION); '';
$fn = str_replace('{PAGENAME}', $rpl_string, $fn); // create output filename $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 run id, we auto add ymd, so we ignore the log file date
if ($this->getLogFlag(Flag::per_run)) { 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)) { } 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 { } else {
$rpl_string = ''; $rpl_string = '';
} }
@@ -739,7 +746,10 @@ class Logging
{ {
if (empty($this->log_file_unique_id) || $override == true) { if (empty($this->log_file_unique_id) || $override == true) {
$this->log_file_unique_id = $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( . substr(hash(
'sha1', 'sha1',
random_bytes(63) random_bytes(63)

View File

@@ -135,30 +135,6 @@ class EditUsers implements Interface\TableArraysInterface
'min_edit_acl' => '100', 'min_edit_acl' => '100',
'min_show_acl' => '100', 'min_show_acl' => '100',
], ],
'debug' => [
'value' => $_POST['debug'] ?? '',
'output_name' => 'Debug',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'db_debug' => [
'value' => $_POST['db_debug'] ?? '',
'output_name' => 'DB Debug',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'email' => [ 'email' => [
'value' => $_POST['email'] ?? '', 'value' => $_POST['email'] ?? '',
'output_name' => 'E-Mail', 'output_name' => 'E-Mail',

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

@@ -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__

View File

@@ -35,14 +35,39 @@ class CreateKey
return random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); 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 * convert binary key to hex string
* *
* @param string $hex_key Convert binary key string to hex * @param string $hex_key Convert binary key string to hex
* @return string * @return string
*/ */
public static function bin2hex(string $hex_key): string public static function bin2hex(
{ #[\SensitiveParameter]
string $hex_key
): string {
return sodium_bin2hex($hex_key); return sodium_bin2hex($hex_key);
} }
@@ -52,8 +77,10 @@ class CreateKey
* @param string $string_key Convery hex key string to binary * @param string $string_key Convery hex key string to binary
* @return string * @return string
*/ */
public static function hex2bin(string $string_key): string public static function hex2bin(
{ #[\SensitiveParameter]
string $string_key
): string {
return sodium_hex2bin($string_key); return sodium_hex2bin($string_key);
} }
} }

View File

@@ -16,8 +16,10 @@ class Password
* @param string $password password * @param string $password password
* @return string hashed 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 // always use the PHP default for the password
// password options ca be set in the password init, // password options ca be set in the password init,
// but should be kept as default // but should be kept as default
@@ -31,8 +33,11 @@ class Password
* @param string $hash password hash * @param string $hash password hash
* @return bool true or false * @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)) { if (password_verify($password, $hash)) {
return true; return true;
} else { } else {

View File

@@ -24,19 +24,19 @@ class SymmetricEncryption
/** @var SymmetricEncryption self instance */ /** @var SymmetricEncryption self instance */
private static SymmetricEncryption $instance; private static SymmetricEncryption $instance;
/** @var string bin hex key */ /** @var ?string bin hex key */
private string $key = ''; private ?string $key = null;
/** /**
* init class * init class
* if key not passed, key must be set with createKey * if key not passed, key must be set with createKey
* *
* @param string|null|null $key * @param string|null $key encryption key
*/ */
public function __construct( public function __construct(
string|null $key = null ?string $key = null
) { ) {
if ($key != null) { if ($key !== null) {
$this->setKey($key); $this->setKey($key);
} }
} }
@@ -45,16 +45,49 @@ class SymmetricEncryption
* Returns the singleton self object. * Returns the singleton self object.
* For function wrapper use * For function wrapper use
* *
* @param string|null $key encryption key
* @return SymmetricEncryption object * @return SymmetricEncryption object
*/ */
public static function getInstance(string|null $key = null): self public static function getInstance(?string $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;
} }
/**
* 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 * MARK: PRIVATE
* *************************************************************************/ * *************************************************************************/
@@ -62,11 +95,19 @@ class SymmetricEncryption
/** /**
* create key and check validity * create key and check validity
* *
* @param string $key The key from which the binary key will be created * @param ?string $key The key from which the binary key will be created
* @return string Binary key string * @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 { try {
$key = CreateKey::hex2bin($key); $key = CreateKey::hex2bin($key);
} catch (SodiumException $e) { } catch (SodiumException $e) {
@@ -87,36 +128,42 @@ class SymmetricEncryption
* @param string $encrypted Text to decrypt * @param string $encrypted Text to decrypt
* @param ?string $key Mandatory encryption key, will throw exception if empty * @param ?string $key Mandatory encryption key, will throw exception if empty
* @return string Plain text * @return string Plain text
* @throws \RangeException * @throws \UnexpectedValueException key cannot be empty
* @throws \UnexpectedValueException * @throws \UnexpectedValueException decipher message failed
* @throws \UnexpectedValueException * @throws \UnexpectedValueException invalid key
*/ */
private function decryptData(string $encrypted, ?string $key): string private function decryptData(
{ #[\SensitiveParameter]
if (empty($key)) { string $encrypted,
throw new \UnexpectedValueException('Key not set'); #[\SensitiveParameter]
?string $key
): string {
if (empty($encrypted)) {
throw new \UnexpectedValueException('Encrypted string cannot be empty');
} }
$key = $this->createKey($key); $key = $this->createKey($key);
$decoded = base64_decode($encrypted); $decoded = base64_decode($encrypted);
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plain = false; $plaintext = false;
try { try {
$plain = sodium_crypto_secretbox_open( $plaintext = sodium_crypto_secretbox_open(
$ciphertext, $ciphertext,
$nonce, $nonce,
$key $key
); );
} catch (SodiumException $e) { } catch (SodiumException $e) {
sodium_memzero($ciphertext);
sodium_memzero($key);
throw new \UnexpectedValueException('Decipher message failed: ' . $e->getMessage()); throw new \UnexpectedValueException('Decipher message failed: ' . $e->getMessage());
} }
if (!is_string($plain)) {
throw new \UnexpectedValueException('Invalid Key');
}
sodium_memzero($ciphertext); sodium_memzero($ciphertext);
sodium_memzero($key); sodium_memzero($key);
return $plain; if (!is_string($plaintext)) {
throw new \UnexpectedValueException('Invalid Key');
}
return $plaintext;
} }
/** /**
@@ -124,15 +171,15 @@ class SymmetricEncryption
* *
* @param string $message Message to encrypt * @param string $message Message to encrypt
* @param ?string $key Mandatory encryption key, will throw exception if empty * @param ?string $key Mandatory encryption key, will throw exception if empty
* @return string * @return string Ciphered text
* @throws \Exception * @throws \UnexpectedValueException create message failed
* @throws \RangeException
*/ */
private function encryptData(string $message, ?string $key): string private function encryptData(
{ #[\SensitiveParameter]
if (empty($this->key) || $key === null) { string $message,
throw new \UnexpectedValueException('Key not set'); #[\SensitiveParameter]
} ?string $key
): string {
$key = $this->createKey($key); $key = $this->createKey($key);
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
try { try {
@@ -145,6 +192,8 @@ class SymmetricEncryption
) )
); );
} catch (SodiumException $e) { } catch (SodiumException $e) {
sodium_memzero($message);
sodium_memzero($key);
throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage()); throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage());
} }
sodium_memzero($message); sodium_memzero($message);
@@ -156,19 +205,49 @@ class SymmetricEncryption
* MARK: PUBLIC * MARK: PUBLIC
* *************************************************************************/ * *************************************************************************/
/** /**
* set a new key for encryption * set a new key for encryption
* *
* @param string $key * @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)) { if (empty($key)) {
throw new \UnexpectedValueException('Key cannot be empty'); throw new \UnexpectedValueException('Key cannot be empty');
} }
// check that this is a valid key
$this->createKey($key);
// set key
$this->key = $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;
} }
/** /**
@@ -178,13 +257,13 @@ class SymmetricEncryption
* @param string $encrypted Message encrypted with safeEncrypt() * @param string $encrypted Message encrypted with safeEncrypt()
* @param string $key Encryption key (as hex string) * @param string $key Encryption key (as hex string)
* @return 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); return self::getInstance()->decryptData($encrypted, $key);
} }
@@ -193,12 +272,11 @@ class SymmetricEncryption
* *
* @param string $encrypted Message encrypted with safeEncrypt() * @param string $encrypted Message encrypted with safeEncrypt()
* @return string * @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); return $this->decryptData($encrypted, $this->key);
} }
@@ -209,11 +287,13 @@ class SymmetricEncryption
* @param string $message Message to encrypt * @param string $message Message to encrypt
* @param string $key Encryption key (as hex string) * @param string $key Encryption key (as hex string)
* @return 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); return self::getInstance()->encryptData($message, $key);
} }
@@ -222,11 +302,11 @@ class SymmetricEncryption
* *
* @param string $message Message to encrypt * @param string $message Message to encrypt
* @return string * @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); return $this->encryptData($message, $this->key);
} }
} }

View File

@@ -19,12 +19,13 @@ declare(strict_types=1);
namespace CoreLibs\Template; namespace CoreLibs\Template;
// leading slash if this is in lib\Smarty class SmartyExtend extends \Smarty\Smarty
class SmartyExtend extends \Smarty
{ {
// internal translation engine // internal translation engine
/** @var \CoreLibs\Language\L10n */ /** @var \CoreLibs\Language\L10n language class */
public \CoreLibs\Language\L10n $l10n; public \CoreLibs\Language\L10n $l10n;
/** @var \CoreLibs\Logging\Logging $log logging class */
public \CoreLibs\Logging\Logging $log;
// lang & encoding // lang & encoding
/** @var string */ /** @var string */
@@ -157,14 +158,18 @@ class SmartyExtend extends \Smarty
* calls L10 for pass on internaly in smarty * calls L10 for pass on internaly in smarty
* also registers the getvar caller plugin * also registers the getvar caller plugin
* *
* @param \CoreLibs\Language\L10n $l10n l10n language class * @param \CoreLibs\Language\L10n $l10n l10n language class
* @param string|null $cache_id * @param \CoreLibs\Logging\Logging $log Logger class
* @param string|null $compile_id * @param string|null $cache_id [default=null]
* @param string|null $compile_id [default=null]
* @param array<string,mixed> $options [default=[]]
*/ */
public function __construct( public function __construct(
\CoreLibs\Language\L10n $l10n, \CoreLibs\Language\L10n $l10n,
\CoreLibs\Logging\Logging $log,
?string $cache_id = null, ?string $cache_id = null,
?string $compile_id = null ?string $compile_id = null,
array $options = []
) { ) {
// trigger deprecation // trigger deprecation
if ( if (
@@ -177,14 +182,33 @@ class SmartyExtend extends \Smarty
E_USER_DEPRECATED E_USER_DEPRECATED
); );
} }
// set variables (to be deprecated) // set variables from global constants (deprecated)
$cache_id = $cache_id ?? if ($cache_id === null && defined('CACHE_ID')) {
(defined('CACHE_ID') ? CACHE_ID : ''); trigger_error(
$compile_id = $compile_id ?? 'SmartyExtended: No cache_id set and CACHE_ID constant set, this is deprecated',
(defined('COMPILE_ID') ? COMPILE_ID : ''); 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 // call basic smarty
// or Smarty::__construct();
parent::__construct(); parent::__construct();
$this->log = $log;
// init lang // init lang
$this->l10n = $l10n; $this->l10n = $l10n;
// parse and read, legacy stuff // parse and read, legacy stuff
@@ -194,7 +218,6 @@ class SmartyExtend extends \Smarty
$this->lang_short = $locale['lang_short']; $this->lang_short = $locale['lang_short'];
$this->domain = $locale['domain']; $this->domain = $locale['domain'];
$this->lang_dir = $locale['path']; $this->lang_dir = $locale['path'];
// opt load functions so we can use legacy init for smarty run perhaps // opt load functions so we can use legacy init for smarty run perhaps
\CoreLibs\Language\L10n::loadFunctions(); \CoreLibs\Language\L10n::loadFunctions();
_setlocale(LC_MESSAGES, $locale['locale']); _setlocale(LC_MESSAGES, $locale['locale']);
@@ -203,7 +226,6 @@ class SmartyExtend extends \Smarty
_bind_textdomain_codeset($this->domain, $this->encoding); _bind_textdomain_codeset($this->domain, $this->encoding);
// register smarty variable // register smarty variable
// $this->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']);
$this->registerPlugin(self::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']); $this->registerPlugin(self::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']);
$this->page_name = \CoreLibs\Get\System::getPageName(); $this->page_name = \CoreLibs\Get\System::getPageName();
@@ -211,6 +233,77 @@ class SmartyExtend extends \Smarty
// set internal settings // set internal settings
$this->CACHE_ID = $cache_id; $this->CACHE_ID = $cache_id;
$this->COMPILE_ID = $compile_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;
}
}
}
} }
/** /**

View File

@@ -22,8 +22,12 @@ Not yet covered tests:
*/ */
final class CoreLibsACLLoginTest extends TestCase final class CoreLibsACLLoginTest extends TestCase
{ {
private static $db; private static \CoreLibs\DB\IO $db;
private static $log; private static \CoreLibs\Logging\Logging $log;
private static string $edit_access_cuid;
private static string $edit_user_cuid;
private static string $edit_user_cuuid;
/** /**
* start DB conneciton, setup DB, etc * start DB conneciton, setup DB, etc
@@ -108,14 +112,40 @@ final class CoreLibsACLLoginTest extends TestCase
self::$db->dbSetMaxQueryCall(-1); self::$db->dbSetMaxQueryCall(-1);
// insert additional content for testing (locked user, etc) // insert additional content for testing (locked user, etc)
$queries = [ $queries = [
"INSERT INTO edit_access_data " <<<SQL
. "(edit_access_id, name, value, enabled) VALUES " INSERT INTO edit_access_data (
. "((SELECT edit_access_id FROM edit_access WHERE uid = 'AdminAccess'), " edit_access_id, name, value, enabled
. "'test', 'value', 1)" ) VALUES (
(SELECT edit_access_id FROM edit_access WHERE uid = 'AdminAccess'),
'test', 'value', 1
)
SQL
]; ];
foreach ($queries as $query) { foreach ($queries as $query) {
self::$db->dbExec($query); self::$db->dbExec($query);
} }
// read edit access cuid, edit user cuid and edit user cuuid
$row = self::$db->dbReturnRowParams(
"SELECT cuid FROM edit_access WHERE uid = $1",
["AdminAccess"]
);
self::$edit_access_cuid = $row['cuid'] ?? '';
if (empty(self::$edit_access_cuid)) {
self::markTestIncomplete(
'Cannot read edit access cuid for "AdminAccess".'
);
}
$row = self::$db->dbReturnRowParams(
"SELECT cuid, cuuid FROM edit_user WHERE username = $1",
["admin"]
);
self::$edit_user_cuid = $row['cuid'] ?? '';
self::$edit_user_cuuid = $row['cuuid'] ?? '';
if (empty(self::$edit_user_cuid) || empty(self::$edit_user_cuuid)) {
self::markTestIncomplete(
'Cannot read edit user cuid or cuuid for "admin".'
);
}
// define mandatory constant // define mandatory constant
// must set // must set
@@ -235,24 +265,25 @@ final class CoreLibsACLLoginTest extends TestCase
'ajax_post_action' => 'login', 'ajax_post_action' => 'login',
], ],
], ],
'load, session euid set only, php error' => [ 'load, session eucuuid set only, php error' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
], ],
[], [],
[], [],
[ [
'EUID' => 1, 'LOGIN_EUID' => 1,
'ECUID' => 'abc', 'LOGIN_EUCUID' => 'abc',
'ECUUID' => '1233456-1234-1234-1234-123456789012', 'LOGIN_EUCUUID' => '1233456-1234-1234-1234-123456789012',
], ],
2, 2,
[], [],
], ],
'load, session euid set, all set' => [ 'load, session eucuuid set, all set' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -261,22 +292,23 @@ final class CoreLibsACLLoginTest extends TestCase
[], [],
[], [],
[ [
'EUID' => 1, 'LOGIN_EUID' => 1,
'ECUID' => 'abc', 'LOGIN_EUCUID' => 'abc',
'ECUUID' => '1233456-1234-1234-1234-123456789012', 'LOGIN_EUCUUID' => 'SET_EUCUUID_IN_TEST',
'USER_NAME' => '', 'LOGIN_USER_NAME' => '',
'GROUP_NAME' => '', 'LOGIN_GROUP_NAME' => '',
'ADMIN' => 1, 'LOGIN_ADMIN' => 1,
'GROUP_ACL_LEVEL' => -1, 'LOGIN_GROUP_ACL_LEVEL' => -1,
'PAGES_ACL_LEVEL' => [], 'LOGIN_PAGES_ACL_LEVEL' => [],
'USER_ACL_LEVEL' => -1, 'LOGIN_USER_ACL_LEVEL' => -1,
'USER_ADDITIONAL_ACL' => [], 'LOGIN_USER_ADDITIONAL_ACL' => [],
'GROUP_ADDITIONAL_ACL' => [], 'LOGIN_GROUP_ADDITIONAL_ACL' => [],
'UNIT_UID' => [ 'LOGIN_UNIT_UID' => [
'AdminAccess' => 1, 'AdminAccess' => '123456789012',
], ],
'UNIT' => [ 'LOGIN_UNIT' => [
1 => [ '123456789012' => [
'id' => 1,
'acl_level' => 80, 'acl_level' => 80,
'name' => 'Admin Access', 'name' => 'Admin Access',
'uid' => 'AdminAccess', 'uid' => 'AdminAccess',
@@ -288,8 +320,8 @@ final class CoreLibsACLLoginTest extends TestCase
'additional_acl' => [] 'additional_acl' => []
], ],
], ],
// 'UNIT_DEFAULT' => '', // 'LOGIN_UNIT_DEFAULT' => '',
// 'DEFAULT_ACL_LIST' => [], // 'LOGIN_DEFAULT_ACL_LIST' => [],
], ],
0, 0,
[ [
@@ -297,6 +329,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -416,6 +449,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_deleted' => true 'test_deleted' => true
@@ -441,6 +475,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_enabled' => true 'test_enabled' => true
@@ -466,6 +501,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_locked' => true 'test_locked' => true
@@ -491,6 +527,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_get_locked' => true, 'test_get_locked' => true,
@@ -515,6 +552,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_locked_period_until' => 'on' 'test_locked_period_until' => 'on'
@@ -540,6 +578,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -559,6 +598,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -569,6 +609,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_locked_period_after' => 'on' 'test_locked_period_after' => 'on'
@@ -594,6 +635,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_locked_period_until' => 'on', 'test_locked_period_until' => 'on',
@@ -620,6 +662,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_login_user_id_locked' => true 'test_login_user_id_locked' => true
@@ -645,6 +688,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -663,6 +707,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -673,6 +718,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -692,6 +738,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -702,6 +749,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -721,6 +769,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -731,6 +780,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -750,6 +800,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -781,6 +832,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -804,6 +856,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -814,6 +867,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -837,6 +891,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -847,6 +902,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_login_user_id_revalidate_after' => 'on', 'test_login_user_id_revalidate_after' => 'on',
@@ -873,6 +929,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -893,6 +950,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -903,6 +961,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_login_user_id_valid_from' => 'on', 'test_login_user_id_valid_from' => 'on',
@@ -929,6 +988,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -949,6 +1009,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -959,6 +1020,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_login_user_id_valid_until' => 'on', 'test_login_user_id_valid_until' => 'on',
@@ -985,6 +1047,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'base_access' => 'list', 'base_access' => 'list',
'page_access' => 'list', 'page_access' => 'list',
'test_login_user_id_valid_from' => 'on', 'test_login_user_id_valid_from' => 'on',
@@ -1012,6 +1075,7 @@ final class CoreLibsACLLoginTest extends TestCase
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess', 'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test', 'edit_access_data' => 'test',
'base_access' => 'list', 'base_access' => 'list',
@@ -1042,6 +1106,7 @@ final class CoreLibsACLLoginTest extends TestCase
'admin_flag' => true, 'admin_flag' => true,
'check_access' => true, 'check_access' => true,
'check_access_id' => 1, 'check_access_id' => 1,
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'check_access_data' => 'value', 'check_access_data' => 'value',
'base_access' => true, 'base_access' => true,
'page_access' => true, 'page_access' => true,
@@ -1089,9 +1154,9 @@ final class CoreLibsACLLoginTest extends TestCase
/** @var \CoreLibs\Create\Session&MockObject */ /** @var \CoreLibs\Create\Session&MockObject */
$session_mock = $this->createPartialMock( $session_mock = $this->createPartialMock(
\CoreLibs\Create\Session::class, \CoreLibs\Create\Session::class,
['startSession', 'checkActiveSession', 'sessionDestroy'] ['getSessionId', 'checkActiveSession', 'sessionDestroy']
); );
$session_mock->method('startSession')->willReturn('ACLLOGINTEST12'); $session_mock->method('getSessionId')->willReturn('ACLLOGINTEST12');
$session_mock->method('checkActiveSession')->willReturn(true); $session_mock->method('checkActiveSession')->willReturn(true);
$session_mock->method('sessionDestroy')->will( $session_mock->method('sessionDestroy')->will(
$this->returnCallback(function () { $this->returnCallback(function () {
@@ -1111,11 +1176,15 @@ final class CoreLibsACLLoginTest extends TestCase
$_POST[$post_var] = $post_value; $_POST[$post_var] = $post_value;
} }
// set ingoing session cuuid if requested
if (isset($session['LOGIN_EUCUUID']) && $session['LOGIN_EUCUUID'] == 'SET_EUCUUID_IN_TEST') {
$session['LOGIN_EUCUUID'] = self::$edit_user_cuuid;
}
// set _SESSION data // set _SESSION data
foreach ($session as $session_var => $session_value) { foreach ($session as $session_var => $session_value) {
$_SESSION[$session_var] = $session_value; $_SESSION[$session_var] = $session_value;
} }
/** @var \CoreLibs\ACL\Login&MockObject */ /** @var \CoreLibs\ACL\Login&MockObject */
$login_mock = $this->getMockBuilder(\CoreLibs\ACL\Login::class) $login_mock = $this->getMockBuilder(\CoreLibs\ACL\Login::class)
->setConstructorArgs([ ->setConstructorArgs([
@@ -1134,7 +1203,7 @@ final class CoreLibsACLLoginTest extends TestCase
. 'locale' . DIRECTORY_SEPARATOR, . 'locale' . DIRECTORY_SEPARATOR,
] ]
]) ])
->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin']) ->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin', 'loginEnhanceHttpSecurity'])
->getMock(); ->getMock();
$login_mock->expects($this->any()) $login_mock->expects($this->any())
->method('loginTerminate') ->method('loginTerminate')
@@ -1152,6 +1221,10 @@ final class CoreLibsACLLoginTest extends TestCase
->method('loginPrintLogin') ->method('loginPrintLogin')
->willReturnCallback(function () { ->willReturnCallback(function () {
}); });
$login_mock->expects($this->any())
->method('loginEnhanceHttpSecurity')
->willReturnCallback(function () {
});
// if mock_settings: enabled OFF // if mock_settings: enabled OFF
// run DB update and set off // run DB update and set off
@@ -1369,6 +1442,19 @@ final class CoreLibsACLLoginTest extends TestCase
// run test // run test
try { try {
// preset, we cannot set that in the provider
if (
isset($expected['check_access_cuid']) &&
$expected['check_access_cuid'] == 'SET_EDIT_ACCESS_CUID_IN_TEST'
) {
$expected['check_access_cuid'] = self::$edit_access_cuid;
}
if (
isset($mock_settings['edit_access_cuid']) &&
$mock_settings['edit_access_cuid'] == 'SET_EDIT_ACCESS_CUID_IN_TEST'
) {
$mock_settings['edit_access_cuid'] = self::$edit_access_cuid;
}
// if ajax call // if ajax call
// check if parameter, or globals (old type) // check if parameter, or globals (old type)
// else normal call // else normal call
@@ -1427,6 +1513,31 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->loginCheckAccessPage($mock_settings['page_access']), $login_mock->loginCheckAccessPage($mock_settings['page_access']),
'Assert page access' 'Assert page access'
); );
// - loginCheckEditAccessCuid
$this->assertEquals(
$expected['check_access'],
$login_mock->loginCheckEditAccessCuid($mock_settings['edit_access_cuid']),
'Assert check access'
);
// - loginCheckEditAccessValidCuid
$this->assertEquals(
$expected['check_access_cuid'],
$login_mock->loginCheckEditAccessValidCuid($mock_settings['edit_access_cuid']),
'Assert check access cuid valid'
);
// - loginGetEditAccessCuidFromUid
$this->assertEquals(
$expected['check_access_cuid'],
$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 // - loginCheckEditAccess
$this->assertEquals( $this->assertEquals(
$expected['check_access'], $expected['check_access'],
@@ -1449,7 +1560,7 @@ final class CoreLibsACLLoginTest extends TestCase
$this->assertEquals( $this->assertEquals(
$expected['check_access_data'], $expected['check_access_data'],
$login_mock->loginGetEditAccessData( $login_mock->loginGetEditAccessData(
$mock_settings['edit_access_id'], $mock_settings['edit_access_uid'],
$mock_settings['edit_access_data'] $mock_settings['edit_access_data']
), ),
'Assert check access id data value valid' 'Assert check access id data value valid'
@@ -1480,11 +1591,12 @@ final class CoreLibsACLLoginTest extends TestCase
// - loginCheckPermissions // - loginCheckPermissions
// - loginGetPermissionOkay // - loginGetPermissionOkay
} catch (\Exception $e) { } catch (\Exception $e) {
// print "[E]: " . $e->getCode() . ", ERROR: " . $login_mock->loginGetLastErrorCode() . "/" /* print "[E]: " . $e->getCode() . ", ERROR: " . $login_mock->loginGetLastErrorCode() . "/"
// . ($expected['login_error'] ?? 0) . "\n"; . ($expected['login_error'] ?? 0) . "\n";
// print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n"; print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n";
// print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n"; print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n";
// print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n"; print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n";
print "POST exit: " . ($_POST['login_exit'] ?? '{0}') . "\n"; */
// if this is 100, then we do further error checks // if this is 100, then we do further error checks
if ( if (
$e->getCode() == 100 || $e->getCode() == 100 ||
@@ -1792,9 +1904,9 @@ final class CoreLibsACLLoginTest extends TestCase
/** @var \CoreLibs\Create\Session&MockObject */ /** @var \CoreLibs\Create\Session&MockObject */
$session_mock = $this->createPartialMock( $session_mock = $this->createPartialMock(
\CoreLibs\Create\Session::class, \CoreLibs\Create\Session::class,
['startSession', 'checkActiveSession', 'sessionDestroy'] ['getSessionId', 'checkActiveSession', 'sessionDestroy']
); );
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34'); $session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
$session_mock->method('checkActiveSession')->willReturn(true); $session_mock->method('checkActiveSession')->willReturn(true);
$session_mock->method('sessionDestroy')->will( $session_mock->method('sessionDestroy')->will(
$this->returnCallback(function () { $this->returnCallback(function () {
@@ -1906,9 +2018,9 @@ final class CoreLibsACLLoginTest extends TestCase
/** @var \CoreLibs\Create\Session&MockObject */ /** @var \CoreLibs\Create\Session&MockObject */
$session_mock = $this->createPartialMock( $session_mock = $this->createPartialMock(
\CoreLibs\Create\Session::class, \CoreLibs\Create\Session::class,
['startSession', 'checkActiveSession', 'sessionDestroy'] ['getSessionId', 'checkActiveSession', 'sessionDestroy']
); );
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34'); $session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
$session_mock->method('checkActiveSession')->willReturn(true); $session_mock->method('checkActiveSession')->willReturn(true);
$session_mock->method('sessionDestroy')->will( $session_mock->method('sessionDestroy')->will(
$this->returnCallback(function () { $this->returnCallback(function () {
@@ -1994,9 +2106,9 @@ final class CoreLibsACLLoginTest extends TestCase
/** @var \CoreLibs\Create\Session&MockObject */ /** @var \CoreLibs\Create\Session&MockObject */
$session_mock = $this->createPartialMock( $session_mock = $this->createPartialMock(
\CoreLibs\Create\Session::class, \CoreLibs\Create\Session::class,
['startSession', 'checkActiveSession', 'sessionDestroy'] ['getSessionId', 'checkActiveSession', 'sessionDestroy']
); );
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34'); $session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
$session_mock->method('checkActiveSession')->willReturn(true); $session_mock->method('checkActiveSession')->willReturn(true);
$session_mock->method('sessionDestroy')->will( $session_mock->method('sessionDestroy')->will(
$this->returnCallback(function () { $this->returnCallback(function () {
@@ -2090,9 +2202,9 @@ final class CoreLibsACLLoginTest extends TestCase
/** @var \CoreLibs\Create\Session&MockObject */ /** @var \CoreLibs\Create\Session&MockObject */
$session_mock = $this->createPartialMock( $session_mock = $this->createPartialMock(
\CoreLibs\Create\Session::class, \CoreLibs\Create\Session::class,
['startSession', 'checkActiveSession', 'sessionDestroy'] ['getSessionId', 'checkActiveSession', 'sessionDestroy']
); );
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34'); $session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
$session_mock->method('checkActiveSession')->willReturn(true); $session_mock->method('checkActiveSession')->willReturn(true);
$session_mock->method('sessionDestroy')->will( $session_mock->method('sessionDestroy')->will(
$this->returnCallback(function () { $this->returnCallback(function () {

View File

@@ -30,11 +30,11 @@ DECLARE
random_length INT = 12; -- that should be long enough random_length INT = 12; -- that should be long enough
BEGIN BEGIN
IF TG_OP = 'INSERT' THEN IF TG_OP = 'INSERT' THEN
NEW.date_created := 'now'; NEW.date_created := clock_timestamp();
NEW.cuid := random_string(random_length); NEW.cuid := random_string(random_length);
NEW.cuuid := gen_random_uuid(); NEW.cuuid := gen_random_uuid();
ELSIF TG_OP = 'UPDATE' THEN ELSIF TG_OP = 'UPDATE' THEN
NEW.date_updated := 'now'; NEW.date_updated := clock_timestamp();
END IF; END IF;
RETURN NEW; RETURN NEW;
END; END;
@@ -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,
@@ -579,11 +579,10 @@ CREATE TABLE edit_user (
strict SMALLINT DEFAULT 0, strict SMALLINT DEFAULT 0,
locked SMALLINT DEFAULT 0, locked SMALLINT DEFAULT 0,
protected SMALLINT NOT NULL DEFAULT 0, protected SMALLINT NOT NULL DEFAULT 0,
-- legacy, debug flags
debug SMALLINT NOT NULL DEFAULT 0,
db_debug SMALLINT NOT NULL DEFAULT 0,
-- is admin user -- is admin user
admin SMALLINT NOT NULL DEFAULT 0, admin SMALLINT NOT NULL DEFAULT 0,
-- forced logout counter
force_logout INT DEFAULT 0,
-- last login log -- last login log
last_login TIMESTAMP WITHOUT TIME ZONE, last_login TIMESTAMP WITHOUT TIME ZONE,
-- login error -- login error
@@ -620,8 +619,6 @@ COMMENT ON COLUMN edit_user.deleted IS 'Login is deleted (master switch), overri
COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be locked, default off'; COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be locked, default off';
COMMENT ON COLUMN edit_user.locked IS 'Locked from too many wrong password logins'; COMMENT ON COLUMN edit_user.locked IS 'Locked from too many wrong password logins';
COMMENT ON COLUMN edit_user.protected IS 'User can only be chnaged by admin user'; COMMENT ON COLUMN edit_user.protected IS 'User can only be chnaged by admin user';
COMMENT ON COLUMN edit_user.debug IS 'Turn debug flag on (legacy)';
COMMENT ON COLUMN edit_user.db_debug IS 'Turn DB debug flag on (legacy)';
COMMENT ON COLUMN edit_user.admin IS 'If set, this user is SUPER admin'; COMMENT ON COLUMN edit_user.admin IS 'If set, this user is SUPER admin';
COMMENT ON COLUMN edit_user.last_login IS 'Last succesfull login tiemstamp'; COMMENT ON COLUMN edit_user.last_login IS 'Last succesfull login tiemstamp';
COMMENT ON COLUMN edit_user.login_error_count IS 'Number of failed logins, reset on successful login'; COMMENT ON COLUMN edit_user.login_error_count IS 'Number of failed logins, reset on successful login';
@@ -652,40 +649,56 @@ 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
ecuid VARCHAR,
ecuuid UUID,
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
username VARCHAR, eucuid VARCHAR,
password VARCHAR, eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
-- date_created equal, but can be overridden
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
ip VARCHAR, -- session ID if set
session_id VARCHAR,
-- username
username VARCHAR,
-- DEPRECATED [password]
password VARCHAR,
ip_address JSONB, -- REMOTE_IP and all other IPs (X_FORWARD, etc) as JSON block
-- DEPRECATED [ip]
ip VARCHAR, -- just the REMOTE_IP, full set see ip_address
-- string blocks, general
error TEXT, error TEXT,
event TEXT, event TEXT,
-- bytea or string type storage of any data
data_binary BYTEA, data_binary BYTEA,
data TEXT, data TEXT,
-- set page name only
page VARCHAR, page VARCHAR,
action VARCHAR, -- various info data sets
action_id VARCHAR,
action_sub_id VARCHAR,
action_yes VARCHAR,
action_flag VARCHAR,
action_menu VARCHAR,
action_loaded VARCHAR,
action_value VARCHAR,
action_type VARCHAR,
action_error VARCHAR,
user_agent VARCHAR, user_agent VARCHAR,
referer VARCHAR, referer VARCHAR,
script_name VARCHAR, script_name VARCHAR,
query_string VARCHAR, query_string VARCHAR,
request_scheme VARCHAR, -- http or https
server_name VARCHAR, server_name VARCHAR,
http_host VARCHAR, http_host VARCHAR,
http_accept VARCHAR, http_data JSONB,
http_accept_charset VARCHAR, -- DEPRECATED [http*]
http_accept_encoding VARCHAR, http_accept VARCHAR, -- in http_data
session_id VARCHAR http_accept_charset VARCHAR, -- in http_data
http_accept_encoding VARCHAR, -- in http_data
-- any action var, -> same set in action_data as JSON
action_data JSONB,
-- DEPRECATED [action*]
action VARCHAR, -- in action_data
action_id VARCHAR, -- in action_data
action_sub_id VARCHAR, -- in action_data
action_yes VARCHAR, -- in action_data
action_flag VARCHAR, -- in action_data
action_menu VARCHAR, -- in action_data
action_loaded VARCHAR, -- in action_data
action_value VARCHAR, -- in action_data
action_type VARCHAR, -- in action_data
action_error VARCHAR -- in action_data
) INHERITS (edit_generic) WITHOUT OIDS; ) INHERITS (edit_generic) WITHOUT OIDS;
-- END: table/edit_log.sql -- END: table/edit_log.sql
-- START: table/edit_log_overflow.sql -- START: table/edit_log_overflow.sql
@@ -712,7 +725,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 +746,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 +767,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,
@@ -1015,7 +1028,7 @@ INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_
-- edit user -- edit user
-- inserts admin user so basic users can be created -- inserts admin user so basic users can be created
DELETE FROM edit_user; DELETE FROM edit_user;
INSERT INTO edit_user (username, password, enabled, debug, db_debug, email, protected, admin, edit_language_id, edit_group_id, edit_scheme_id, edit_access_right_id) VALUES ('admin', 'admin', 1, 1, 1, '', 1, 1, INSERT INTO edit_user (username, password, enabled, email, protected, admin, edit_language_id, edit_group_id, edit_scheme_id, edit_access_right_id) VALUES ('admin', 'admin', 1, 'test@tequila.jp', 1, 1,
(SELECT edit_language_id FROM edit_language WHERE short_name = 'en_US'), (SELECT edit_language_id FROM edit_language WHERE short_name = 'en_US'),
(SELECT edit_group_id FROM edit_group WHERE name = 'Admin'), (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
(SELECT edit_scheme_id FROM edit_scheme WHERE name = 'Admin'), (SELECT edit_scheme_id FROM edit_scheme WHERE name = 'Admin'),

View File

@@ -1201,6 +1201,91 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
'Find next key in array' 'Find next key in array'
); );
} }
public function providerReturnMatchingKeyOnley(): array
{
return [
'limited entries' => [
[
'a' => 'foo',
'b' => 'bar',
'c' => 'foobar'
],
[
'a', 'b'
],
[
'a' => 'foo',
'b' => 'bar',
],
],
'limited entries, with one wrong key' => [
[
'a' => 'foo',
'b' => 'bar',
'c' => 'foobar'
],
[
'a', 'b', 'f'
],
[
'a' => 'foo',
'b' => 'bar',
],
],
'wrong keys only' => [
[
'a' => 'foo',
'b' => 'bar',
'c' => 'foobar'
],
[
'f', 'f'
],
[
],
],
'empty keys' => [
[
'a' => 'foo',
'b' => 'bar',
'c' => 'foobar'
],
[],
[
'a' => 'foo',
'b' => 'bar',
'c' => 'foobar'
],
],
];
}
/**
* Undocumented function
*
* @covers ::arrayReturnMatchingKeyOnly
* @dataProvider providerReturnMatchingKeyOnley
* @testdox arrayReturnMatchingKeyOnly get only selected key entries from array [$_dataName]
*
* @param array $input
* @param array $key_list
* @param array $expected
* @return void
*/
public function testArrayReturnMatchingKeyOnly(
array $input,
array $key_list,
array $expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\ArrayHandler::arrayReturnMatchingKeyOnly(
$input,
$key_list
)
);
}
} }
// __END__ // __END__

View File

@@ -22,7 +22,6 @@ final class CoreLibsCreateSessionTest extends TestCase
public function sessionProvider(): array public function sessionProvider(): array
{ {
// 0: session name as parameter or for GLOBAL value // 0: session name as parameter or for GLOBAL value
// 1: type p: parameter, g: global, d: php.ini default
// 2: mock data as array // 2: mock data as array
// checkCliStatus: true/false, // checkCliStatus: true/false,
// getSessionStatus: PHP_SESSION_DISABLED for abort, // getSessionStatus: PHP_SESSION_DISABLED for abort,
@@ -31,13 +30,10 @@ final class CoreLibsCreateSessionTest extends TestCase
// checkActiveSession: true/false, [1st call, 2nd call] // checkActiveSession: true/false, [1st call, 2nd call]
// getSessionId: string or false // getSessionId: string or false
// 3: exepcted name (session)] // 3: exepcted name (session)]
// 4: Exception thrown on error // 4: auto write close flag
// 5: exception code, null for none
// 6: expected error string
return [ return [
'session parameter' => [ 'session parameter' => [
'sessionNameParameter', 'sessionNameParameter',
'p',
[ [
'checkCliStatus' => false, 'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_NONE, 'getSessionStatus' => PHP_SESSION_NONE,
@@ -47,12 +43,9 @@ final class CoreLibsCreateSessionTest extends TestCase
], ],
'sessionNameParameter', 'sessionNameParameter',
null, null,
null,
'',
], ],
'session globals' => [ 'session globals' => [
'sessionNameGlobals', 'sessionNameGlobals',
'g',
[ [
'checkCliStatus' => false, 'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_NONE, 'getSessionStatus' => PHP_SESSION_NONE,
@@ -61,13 +54,12 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'sessionNameGlobals', 'sessionNameGlobals',
null, [
null, 'auto_write_close' => false,
'', ],
], ],
'session name default' => [ 'auto write close' => [
'', 'sessionNameAutoWriteClose',
'd',
[ [
'checkCliStatus' => false, 'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_NONE, 'getSessionStatus' => PHP_SESSION_NONE,
@@ -75,109 +67,10 @@ final class CoreLibsCreateSessionTest extends TestCase
'checkActiveSession' => [false, true], 'checkActiveSession' => [false, true],
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'', 'sessionNameAutoWriteClose',
null,
null,
'',
],
// error checks
// 1: we are in cli
'on cli error' => [
'',
'd',
[ [
'checkCliStatus' => true, 'auto_write_close' => true,
'getSessionStatus' => PHP_SESSION_NONE,
'setSessionName' => true,
'checkActiveSession' => [false, true],
'getSessionId' => '1234abcd4567'
], ],
'',
'RuntimeException',
1,
'[SESSION] No sessions in php cli'
],
// 2: session disabled
'session disabled error' => [
'',
'd',
[
'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_DISABLED,
'setSessionName' => true,
'checkActiveSession' => [false, true],
'getSessionId' => '1234abcd4567'
],
'',
'RuntimeException',
2,
'[SESSION] Sessions are disabled'
],
// 3: invalid session name: string
'invalid name chars error' => [
'1invalid$session#;',
'p',
[
'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_NONE,
'setSessionName' => false,
'checkActiveSession' => [false, true],
'getSessionId' => '1234abcd4567'
],
'',
'UnexpectedValueException',
3,
'[SESSION] Invalid session name: 1invalid$session#;'
],
// 3: invalid session name: only numbers
'invalid name numbers only error' => [
'123',
'p',
[
'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_NONE,
'setSessionName' => false,
'checkActiveSession' => [false, true],
'getSessionId' => '1234abcd4567'
],
'',
'UnexpectedValueException',
3,
'[SESSION] Invalid session name: 123'
],
// 3: invalid session name: invalid name short
// 3: invalid session name: too long (128)
// 4: failed to start session (2nd false on check active session)
'invalid name numbers only error' => [
'',
'd',
[
'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_NONE,
'setSessionName' => true,
'checkActiveSession' => [false, false],
'getSessionId' => '1234abcd4567'
],
'',
'RuntimeException',
4,
'[SESSION] Failed to activate session'
],
// 5: get session id return false
'invalid name numbers only error' => [
'',
'd',
[
'checkCliStatus' => false,
'getSessionStatus' => PHP_SESSION_NONE,
'setSessionName' => true,
'checkActiveSession' => [false, true],
'getSessionId' => false
],
'',
'UnexpectedValueException',
5,
'[SESSION] getSessionId did not return a session id'
], ],
]; ];
} }
@@ -190,32 +83,24 @@ final class CoreLibsCreateSessionTest extends TestCase
* @testdox startSession $input name for $type will be $expected (error: $expected_error) [$_dataName] * @testdox startSession $input name for $type will be $expected (error: $expected_error) [$_dataName]
* *
* @param string $input * @param string $input
* @param string $type
* @param array<mixed> $mock_data * @param array<mixed> $mock_data
* @param string $expected * @param string $expected
* @param string|null $exception * @param array<string,mixed> $options
* @param string $expected_error
* @return void * @return void
*/ */
public function testStartSession( public function testStartSession(
string $input, string $input,
string $type,
array $mock_data, array $mock_data,
string $expected, string $expected,
?string $exception, ?array $options,
?int $exception_code,
string $expected_error
): void { ): void {
// override expected
if ($type == 'd') {
$expected = ini_get('session.name');
}
/** @var \CoreLibs\Create\Session&MockObject $session_mock */ /** @var \CoreLibs\Create\Session&MockObject $session_mock */
$session_mock = $this->createPartialMock( $session_mock = $this->createPartialMock(
\CoreLibs\Create\Session::class, \CoreLibs\Create\Session::class,
[ [
'checkCliStatus', 'getSessionStatus', 'checkActiveSession', 'checkCliStatus',
'setSessionName', 'startSessionCall', 'getSessionId', 'getSessionStatus', 'checkActiveSession',
'getSessionId',
'getSessionName' 'getSessionName'
] ]
); );
@@ -234,12 +119,8 @@ final class CoreLibsCreateSessionTest extends TestCase
$mock_data['checkActiveSession'][0], $mock_data['checkActiveSession'][0],
$mock_data['checkActiveSession'][1], $mock_data['checkActiveSession'][1],
); );
// dummy set for session name
$session_mock->method('setSessionName')->with($input)->willReturn($mock_data['setSessionName']);
// set session name & return bsed on request data // set session name & return bsed on request data
$session_mock->method('getSessionName')->willReturn($expected); $session_mock->method('getSessionName')->willReturn($expected);
// will not return anything
$session_mock->method('startSessionCall');
// in test case only return string // in test case only return string
// false: will return false // false: will return false
$session_mock->method('getSessionId')->willReturn($mock_data['getSessionId']); $session_mock->method('getSessionId')->willReturn($mock_data['getSessionId']);
@@ -247,25 +128,7 @@ final class CoreLibsCreateSessionTest extends TestCase
// regex for session id // regex for session id
$ression_id_regex = "/^\w+$/"; $ression_id_regex = "/^\w+$/";
if ($exception !== null) { $session_id = $session_mock->getSessionId();
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
unset($GLOBALS['SET_SESSION_NAME']);
$session_id = '';
switch ($type) {
case 'p':
$session_id = $session_mock->startSession($input);
break;
case 'g':
$GLOBALS['SET_SESSION_NAME'] = $input;
$session_id = $session_mock->startSession();
break;
case 'd':
$session_id = $session_mock->startSession();
break;
}
// asert checks // asert checks
if (!empty($session_id)) { if (!empty($session_id)) {
$this->assertMatchesRegularExpression( $this->assertMatchesRegularExpression(
@@ -284,6 +147,79 @@ final class CoreLibsCreateSessionTest extends TestCase
} }
} }
/**
* Undocumented function
*
* @return array
*/
public function providerSessionException(): array
{
return [
'not cli' => [
'TEST_EXCEPTION',
\RuntimeException::class,
1,
'/^\[SESSION\] No sessions in php cli$/',
],
/* 'session disabled ' => [
'TEST_EXCEPTION',
\RuntimeException::class,
2,
'/^\[SESSION\] Sessions are disabled/'
],
'invalid session name' => [
'--#as^-292p-',
\UnexpectedValueException::class,
3,
'/^\[SESSION\] Invalid session name: /'
],
'failed to activate session' => [
'TEST_EXCEPTION',
\RuntimeException::class,
4,
'/^\[SESSION\] Failed to activate session/'
],
'expired session' => [
\RuntimeException::class,
5,
'/^\[SESSION\] Expired session found/'
],
'not a valid session id returned' => [
\UnexpectedValueException::class,
6,
'/^\[SESSION\] getSessionId did not return a session id/'
], */
];
}
/**
* exception checks
*
* @covers ::initSession
* @dataProvider providerSessionException
* @testdox create session $session_name with exception $exception ($exception_code) [$_dataName]
*
* @param string $session_name
* @param string $exception
* @param int $exception_code
* @param string $expected_error
* @return void
*/
public function testSessionException(
string $session_name,
string $exception,
int $exception_code,
string $expected_error,
): void {
//
// throws only on new Object creation
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
$this->expectExceptionMessageMatches($expected_error);
// cannot set ini after header sent, plus we are on command line there are no headers
new \CoreLibs\Create\Session($session_name, ['session_strict' => false]);
}
/** /**
* provider for session name check * provider for session name check
* *
@@ -347,109 +283,147 @@ 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,
] ]
]; ];
} }
// NOTE: with auto start session, we cannot test this in the command line
/** /**
* 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
{ {
$session = new \CoreLibs\Create\Session(); if (\CoreLibs\Get\System::checkCLI()) {
$session->setS($name, $input); $this->markTestSkipped('Cannot run testMethodSetGet in CLI');
}
$session = new \CoreLibs\Create\Session('TEST_METHOD');
if ($expected !== null) {
$this->expectException($exception);
}
$session->set($name, $input);
$this->assertEquals( $this->assertEquals(
$expected, $expected,
$session->getS($name), $session->get($name),
'method set assert' 'method set assert'
); );
// isset true // isset true
$this->assertTrue( $this->assertTrue(
$session->issetS($name), $session->isset($name),
'method isset assert ok' 'method isset assert ok'
); );
$session->unsetS($name); $session->unset($name);
$this->assertEquals( $this->assertEquals(
'', '',
$session->getS($name), $session->get($name),
'method unset assert' 'method unset assert'
); );
// iset false // isset false
$this->assertFalse( $this->assertFalse(
$session->issetS($name), $session->isset($name),
'method isset assert false' 'method isset assert false'
); );
} }
/** /**
* magic call test * Undocumented function
* *
* @covers ::__set * @return array
* @covers ::__get */
* @covers ::__isset public function providerSessionDataMany(): array
* @covers ::__unset {
* @dataProvider sessionDataProvider return [
* @testdox __set/__get/__iseet/__unset $name with $input is $expected [$_dataName] '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
* *
* @param string|int $name * @covers ::setMany
* @param mixed $input * @covers ::getMany
* @param mixed $expected * @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 * @return void
*/ */
public function testMagicSetGet($name, $input, $expected): void public function testMany($set, $expected, $exception): void
{ {
$session = new \CoreLibs\Create\Session(); if (\CoreLibs\Get\System::checkCLI()) {
$session->$name = $input; $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( $this->assertEquals(
$expected, $expected,
$session->$name, $session->getMany(array_keys($set)),
'magic set assert' 'set many failed'
); );
// isset true $session->unsetMany(array_keys($set));
$this->assertTrue(
isset($session->$name),
'magic isset assert ok'
);
unset($session->$name);
$this->assertEquals( $this->assertEquals(
'', [],
$session->$name, $session->getMany(array_keys($set)),
'magic unset assert' 'unset many failed'
);
// isset true
$this->assertFalse(
isset($session->$name),
'magic isset assert false'
); );
} }
@@ -463,27 +437,30 @@ final class CoreLibsCreateSessionTest extends TestCase
*/ */
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'
]; ];
$session = new \CoreLibs\Create\Session(); $session = new \CoreLibs\Create\Session('TEST_UNSET');
foreach ($test_values as $name => $value) { foreach ($test_values as $name => $value) {
$session->setS($name, $value); $session->set($name, $value);
// confirm set // confirm set
$this->assertEquals( $this->assertEquals(
$value, $value,
$session->getS($name), $session->get($name),
'set assert: ' . $name 'set assert: ' . $name
); );
} }
// unset all // unset all
$session->unsetAllS(); $session->clear();
// check unset // check unset
foreach (array_keys($test_values) as $name) { foreach (array_keys($test_values) as $name) {
$this->assertEquals( $this->assertEquals(
'', '',
$session->getS($name), $session->get($name),
'unsert assert: ' . $name 'unsert assert: ' . $name
); );
} }

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,88 @@ 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,
],
'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,
] ]
]; ];
} }

View File

@@ -568,6 +568,9 @@ final class CoreLibsDebugSupportTest extends TestCase
'assert expected 12' 'assert expected 12'
); );
break; break;
default:
$this->assertTrue(true, 'Default fallback as true');
break;
} }
} }

View File

@@ -21,341 +21,6 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
. 'includes' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR
. 'locale' . DIRECTORY_SEPARATOR; . 'locale' . DIRECTORY_SEPARATOR;
/**
* set all constant variables that must be set before call
*
* @return void
*/
public static function setUpBeforeClass(): void
{
// default web page encoding setting
/* if (!defined('DEFAULT_ENCODING')) {
define('DEFAULT_ENCODING', 'UTF-8');
}
if (!defined('DEFAULT_LOCALE')) {
// default lang + encoding
define('DEFAULT_LOCALE', 'en_US.UTF-8');
}
// site
if (!defined('SITE_ENCODING')) {
define('SITE_ENCODING', DEFAULT_ENCODING);
}
if (!defined('SITE_LOCALE')) {
define('SITE_LOCALE', DEFAULT_LOCALE);
} */
// just set
/* if (!defined('BASE')) {
define('BASE', str_replace('/configs', '', __DIR__) . DIRECTORY_SEPARATOR);
}
if (!defined('INCLUDES')) {
define('INCLUDES', 'includes' . DIRECTORY_SEPARATOR);
}
if (!defined('LANG')) {
define('LANG', 'lang' . DIRECTORY_SEPARATOR);
}
if (!defined('LOCALE')) {
define('LOCALE', 'locale' . DIRECTORY_SEPARATOR);
}
if (!defined('CONTENT_PATH')) {
define('CONTENT_PATH', 'frontend' . DIRECTORY_SEPARATOR);
} */
// array session
$_SESSION = [];
global $_SESSION;
}
/**
* all the test data
*
* @return array<mixed>
*/
/* public function setLocaleProvider(): array
{
return [
// 0: locale
// 1: domain
// 2: encoding
// 3: path
// 4: SESSION: DEFAULT_LOCALE
// 5: SESSION: DEFAULT_CHARSET
// 6: expected array
// 7: deprecation message
'no params, all default constants' => [
// lang, domain, encoding, path
null, null, null, null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'en_US.UTF-8',
'lang' => 'en_US',
'domain' => 'frontend',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $locale or unset SESSION locale is deprecated',
],
'no params, session charset and lang' => [
// lang, domain, encoding, path
null, null, null, null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
'ja_JP', 'UTF-8',
// return array
[
'locale' => 'ja_JP',
'lang' => 'ja_JP',
'domain' => 'frontend',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $domain is deprecated'
],
'no params, session charset and lang short' => [
// lang, domain, encoding, path
null, null, null, null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
'ja', 'UTF-8',
// return array
[
'locale' => 'ja',
'lang' => 'ja',
'domain' => 'frontend',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $domain is deprecated',
],
// param lang (no sessions)
'locale param only, no sessions' => [
// lang, domain, encoding, path
'ja.UTF-8', null, null, null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'ja.UTF-8',
'lang' => 'ja',
'domain' => 'frontend',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $domain is deprecated',
],
// different locale setting
'locale complex param only, no sessions' => [
// lang, domain, encoding, path
'ja_JP.SJIS', null, null, null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'ja_JP.SJIS',
'lang' => 'ja_JP',
'domain' => 'frontend',
'encoding' => 'SJIS',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $domain is deprecated',
],
// param lang and domain (no override)
'locale, domain params, no sessions' => [
// lang, domain, encoding, path
'ja.UTF-8', 'admin', null, null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'ja.UTF-8',
'lang' => 'ja',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $path is deprecated',
],
// param lang and domain (no override)
'locale, domain, encoding params, no sessions' => [
// lang, domain, encoding, path
'ja.UTF-8', 'admin', 'UTF-8', null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'ja.UTF-8',
'lang' => 'ja',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $path is deprecated'
],
// lang, domain, path (no override)
'locale, domain and path, no sessions' => [
// lang, domain, encoding, path
'ja.UTF-8', 'admin', '', __DIR__ . '/locale_other/',
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'ja.UTF-8',
'lang' => 'ja',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?locale_other\/$/",
],
null
],
// all params set (no override)
'all parameter, no sessions' => [
// lang, domain, encoding, path
'ja', 'admin', 'UTF-8', __DIR__ . '/locale_other/',
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'ja',
'lang' => 'ja',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?locale_other\/$/",
],
null
],
// param lang and domain (no override)
'long locale, domain, encoding params, no sessions' => [
// lang, domain, encoding, path
'de_CH.UTF-8@euro', 'admin', 'UTF-8', null,
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
null, null,
// return array
[
'locale' => 'de_CH.UTF-8@euro',
'lang' => 'de_CH',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
'setLocale: Unset $path is deprecated',
],
// TODO invalid params (bad path) (no override)
// TODO param calls, but with override set
];
} */
/**
* Undocumented function
*
* @covers ::setLocale
* @dataProvider setLocaleProvider
* @testdox lang settings lang $language, domain $domain, encoding $encoding, path $path; session lang: $SESSION_DEFAULT_LOCALE, session char: $SESSION_DEFAULT_CHARSET [$_dataName]
*
* @param string|null $language
* @param string|null $domain
* @param string|null $encoding
* @param string|null $path
* @param string|null $SESSION_DEFAULT_LOCALE
* @param string|null $SESSION_DEFAULT_CHARSET
* @param array<mixed> $expected
* @param string|null $deprecation_message
* @return void
*/
/* public function testsetLocale(
?string $language,
?string $domain,
?string $encoding,
?string $path,
?string $SESSION_DEFAULT_LOCALE,
?string $SESSION_DEFAULT_CHARSET,
array $expected,
?string $deprecation_message
): void {
$return_lang_settings = [];
global $_SESSION;
// set override
if ($SESSION_DEFAULT_LOCALE !== null) {
$_SESSION['DEFAULT_LOCALE'] = $SESSION_DEFAULT_LOCALE;
}
if ($SESSION_DEFAULT_CHARSET !== null) {
$_SESSION['DEFAULT_CHARSET'] = $SESSION_DEFAULT_CHARSET;
}
if ($deprecation_message !== null) {
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \Exception($errstr, $errno);
},
E_USER_DEPRECATED
);
// catch this with the message
$this->expectExceptionMessage($deprecation_message);
}
// function call
if (
$language === null && $domain === null &&
$encoding === null && $path === null
) {
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale();
} elseif (
$language !== null && $domain === null &&
$encoding === null && $path === null
) {
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
$language
);
} elseif (
$language !== null && $domain !== null &&
$encoding === null && $path === null
) {
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
$language,
$domain
);
} elseif (
$language !== null && $domain !== null &&
$encoding !== null && $path === null
) {
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
$language,
$domain,
$encoding
);
} else {
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
$language,
$domain,
$encoding,
$path
);
}
restore_error_handler();
// print "RETURN: " . print_r($return_lang_settings, true) . "\n";
foreach (
[
'locale', 'lang', 'domain', 'encoding', 'path'
] as $key
) {
$value = $expected[$key];
if (strpos($value, "/") === 0) {
// this is regex
$this->assertMatchesRegularExpression(
$value,
$return_lang_settings[$key],
'assert regex failed for ' . $key
);
} else {
// assert equal
$this->assertEquals(
$value,
$return_lang_settings[$key],
'assert equal failed for ' . $key
);
}
}
// unset all vars
$_SESSION = [];
unset($GLOBALS['OVERRIDE_LANG']);
} */
/** /**
* all the test data * all the test data
* *

View File

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

View File

@@ -10,7 +10,7 @@ use CoreLibs\Logging\Logger\Level;
/** /**
* Test class for Logging * Test class for Logging
* @coversDefaultClass \CoreLibs\Logging\ErrorMessages * @coversDefaultClass \CoreLibs\Logging\ErrorMessages
* @testdox \CoreLibs\Logging\ErrorMEssages method tests * @testdox \CoreLibs\Logging\ErrorMessages method tests
*/ */
final class CoreLibsLoggingErrorMessagesTest extends TestCase final class CoreLibsLoggingErrorMessagesTest extends TestCase
{ {

View File

@@ -395,7 +395,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
} }
$per_run_id = $log->getLogUniqueId(); $per_run_id = $log->getLogUniqueId();
$this->assertMatchesRegularExpression( $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, $per_run_id,
'assert per log run id 1st' 'assert per log run id 1st'
); );
@@ -403,7 +403,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
$log->setLogUniqueId(true); $log->setLogUniqueId(true);
$per_run_id_2nd = $log->getLogUniqueId(); $per_run_id_2nd = $log->getLogUniqueId();
$this->assertMatchesRegularExpression( $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, $per_run_id_2nd,
'assert per log 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->assertTrue($log_ok, 'assert ::log (debug) OK');
$this->assertEquals( $this->assertEquals(
$log->getLogFile(), $log->getLogFile(),
$log->getLogFileId() . '_DEBUG.log' $log->getLogFileId() . '.DEBUG.log'
); );
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:'); $log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (info) OK'); $this->assertTrue($log_ok, 'assert ::log (info) OK');
$this->assertEquals( $this->assertEquals(
$log->getLogFile(), $log->getLogFile(),
$log->getLogFileId() . '_INFO.log' $log->getLogFileId() . '.INFO.log'
); );
} }

View File

@@ -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__

View File

@@ -13,6 +13,11 @@ use PHPUnit\Framework\TestCase;
*/ */
final class CoreLibsSecurityPasswordTest extends TestCase final class CoreLibsSecurityPasswordTest extends TestCase
{ {
/**
* Undocumented function
*
* @return array
*/
public function passwordProvider(): array public function passwordProvider(): array
{ {
return [ 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 public function passwordRehashProvider(): array
{ {
return [ return [
@@ -63,6 +73,10 @@ final class CoreLibsSecurityPasswordTest extends TestCase
*/ */
public function testPasswordRehashCheck(string $input, bool $expected): void 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( $this->assertEquals(
$expected, $expected,
\CoreLibs\Security\Password::passwordRehashCheck($input) \CoreLibs\Security\Password::passwordRehashCheck($input)

View File

@@ -15,6 +15,77 @@ use CoreLibs\Security\SymmetricEncryption;
*/ */
final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase 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 * Undocumented function
* *
@@ -56,7 +127,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 +153,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$decrypted, $decrypted,
'Class Instance call', 'Class Instance call',
); );
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encryptKey
* @covers ::decryptKey
* @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);
@@ -77,6 +182,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
); );
} }
// MARK: invalid key
/** /**
* Undocumented function * Undocumented function
* *
@@ -114,13 +221,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 ::encryptKey
* @covers ::decryptKey
* @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);
@@ -128,6 +273,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
SymmetricEncryption::decryptKey($encrypted, $wrong_key); SymmetricEncryption::decryptKey($encrypted, $wrong_key);
} }
// MARK: wrong key
/** /**
* Undocumented function * Undocumented function
* *
@@ -144,6 +291,10 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b', 'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
'excpetion_message' => 'Key is not the correct size (must be ' 'excpetion_message' => 'Key is not the correct size (must be '
], ],
'empty key' => [
'key' => '',
'excpetion_message' => 'Key cannot be empty'
]
]; ];
} }
@@ -164,6 +315,7 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$enc_key = CreateKey::generateRandomKey(); $enc_key = CreateKey::generateRandomKey();
// class // class
$this->expectExceptionMessage($exception_message);
$crypt = new SymmetricEncryption($key); $crypt = new SymmetricEncryption($key);
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
$crypt->encrypt('test'); $crypt->encrypt('test');
@@ -172,6 +324,23 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
$crypt->setKey($key); $crypt->setKey($key);
$crypt->decrypt($encrypted); $crypt->decrypt($encrypted);
}
/**
* 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 // class instance
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
@@ -180,6 +349,23 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test'); $encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test');
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($key)->decrypt($encrypted); SymmetricEncryption::getInstance($key)->decrypt($encrypted);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encryptKey
* @covers ::decryptKey
* @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 // class static
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
@@ -190,6 +376,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
SymmetricEncryption::decryptKey($encrypted, $key); SymmetricEncryption::decryptKey($encrypted, $key);
} }
// MARK: wrong input
/** /**
* Undocumented function * Undocumented function
* *
@@ -232,6 +420,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 ::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::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 ::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::generateRandomKey();
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
} }
// __END__ // __END__