Compare commits

...

9 Commits

Author SHA1 Message Date
Clemens Schwaighofer
d19842007c ACL Login cuid and cuuid update and add 2024-12-03 13:29:50 +09:00
Clemens Schwaighofer
c5bd16de74 Release: v9.21.1 2024-12-02 17:20:25 +09:00
Clemens Schwaighofer
e580e5430c Add ECUID (edit user cuid) to session and return in acl array 2024-12-02 17:18:34 +09:00
Clemens Schwaighofer
da9717265c Release: v9.21.0 2024-12-02 15:55:33 +09:00
Clemens Schwaighofer
6c6b33cacc add uuidv4 verify method 2024-12-02 15:54:35 +09:00
Clemens Schwaighofer
5509d1c92e Release: v9.20.1 2024-11-27 14:43:58 +09:00
Clemens Schwaighofer
16d789f061 Merge branch 'development' 2024-11-27 14:42:18 +09:00
Clemens Schwaighofer
3450b6263b Fixes in SQL PgSQL for identity column 2024-11-27 14:42:04 +09:00
Clemens Schwaighofer
bcc1e833c1 Release: v9.20.0 2024-11-20 22:54:37 +09:00
12 changed files with 789 additions and 514 deletions

View File

@@ -1 +1 @@
9.19.1 9.21.1

View File

@@ -69,12 +69,17 @@ declare(strict_types=1);
namespace CoreLibs\ACL; namespace CoreLibs\ACL;
use CoreLibs\Security\Password; use CoreLibs\Security\Password;
use CoreLibs\Create\Uids;
use CoreLibs\Convert\Json; use CoreLibs\Convert\Json;
class Login class Login
{ {
/** @var ?int the user id var*/ /** @var ?int the user id var*/
private ?int $euid; private ?int $euid;
/** @var ?string the user cuid (note will be super seeded with uuid v4 later) */
private ?string $ecuid;
/** @var ?string UUIDv4, will superseed the ecuid and replace euid as login id */
private ?string $ecuuid;
/** @var string _GET/_POST loginUserId parameter for non password login */ /** @var string _GET/_POST loginUserId parameter for non password login */
private string $login_user_id = ''; private string $login_user_id = '';
/** @var string source, either _GET or _POST or empty */ /** @var string source, either _GET or _POST or empty */
@@ -193,6 +198,12 @@ class Login
/** @var bool */ /** @var bool */
private bool $login_is_ajax_page = false; private bool $login_is_ajax_page = false;
// logging
/** @var array<string> list of allowed types for edit log write */
private const WRITE_TYPES = ['BINARY', 'BZIP2', 'LZIP', 'STRING', 'SERIAL', 'JSON'];
/** @var array<string> list of available write types for log */
private array $write_types_available = [];
// settings // settings
/** @var array<string,mixed> options */ /** @var array<string,mixed> options */
private array $options = []; private array $options = [];
@@ -379,6 +390,8 @@ class Login
$_SESSION['DEFAULT_ACL_LIST'] = $this->default_acl_list; $_SESSION['DEFAULT_ACL_LIST'] = $this->default_acl_list;
$_SESSION['DEFAULT_ACL_LIST_TYPE'] = $this->default_acl_list_type; $_SESSION['DEFAULT_ACL_LIST_TYPE'] = $this->default_acl_list_type;
$this->loginSetEditLogWriteTypeAvailable();
// this will be deprecated // this will be deprecated
if ($this->options['auto_login'] === true) { if ($this->options['auto_login'] === true) {
$this->loginMainCall(); $this->loginMainCall();
@@ -757,7 +770,7 @@ class Login
} }
// have to get the global stuff here for setting it later // have to get the global stuff here for setting it later
// we have to get the themes in here too // we have to get the themes in here too
$q = "SELECT eu.edit_user_id, eu.username, eu.password, " $q = "SELECT eu.edit_user_id, eu.cuid, eu.cuuid, eu.username, eu.password, "
. "eu.edit_group_id, " . "eu.edit_group_id, "
. "eg.name AS edit_group_name, eu.admin, " . "eg.name AS edit_group_name, eu.admin, "
// additinal acl lists // additinal acl lists
@@ -889,6 +902,8 @@ class Login
// normal user processing // normal user processing
// set class var and session var // set class var and session var
$_SESSION['EUID'] = $this->euid = (int)$res['edit_user_id']; $_SESSION['EUID'] = $this->euid = (int)$res['edit_user_id'];
$_SESSION['ECUID'] = $this->ecuid = (string)$res['cuid'];
$_SESSION['ECUUID'] = $this->ecuuid = (string)$res['cuuid'];
// check if user is okay // check if user is okay
$this->loginCheckPermissions(); $this->loginCheckPermissions();
if ($this->login_error == 0) { if ($this->login_error == 0) {
@@ -1132,6 +1147,9 @@ class Login
// username (login), group name // username (login), group name
$this->acl['user_name'] = $_SESSION['USER_NAME']; $this->acl['user_name'] = $_SESSION['USER_NAME'];
$this->acl['group_name'] = $_SESSION['GROUP_NAME']; $this->acl['group_name'] = $_SESSION['GROUP_NAME'];
// edit user cuid
$this->acl['ecuid'] = $_SESSION['ECUID'];
$this->acl['ecuuid'] = $_SESSION['ECUUID'];
// set additional acl // set additional acl
$this->acl['additional_acl'] = [ $this->acl['additional_acl'] = [
'user' => $_SESSION['USER_ADDITIONAL_ACL'], 'user' => $_SESSION['USER_ADDITIONAL_ACL'],
@@ -1425,7 +1443,7 @@ class Login
$data = 'Illegal user for password change: ' . $this->pw_username; $data = 'Illegal user for password change: ' . $this->pw_username;
} }
// log this password change attempt // log this password change attempt
$this->writeLog($event, $data, $this->login_error, $this->pw_username); $this->writeEditLog($event, $data, $this->login_error, $this->pw_username);
} }
/** /**
@@ -1566,7 +1584,7 @@ class Login
$username = $res['username']; $username = $res['username'];
} }
} // if euid is set, get username (or try) } // if euid is set, get username (or try)
$this->writeLog($event, '', $this->login_error, $username); $this->writeEditLog($event, '', $this->login_error, $username);
} // write log under certain settings } // write log under certain settings
// now close DB connection // now close DB connection
// $this->error_msg = $this->_login(); // $this->error_msg = $this->_login();
@@ -1722,6 +1740,8 @@ HTML;
} }
} }
// MARK: LOGGING
/** /**
* writes detailed data into the edit user log table (keep log what user does) * writes detailed data into the edit user log table (keep log what user does)
* *
@@ -1731,7 +1751,7 @@ HTML;
* @param string $username login user username * @param string $username login user username
* @return void has no return * @return void has no return
*/ */
private function writeLog( private function writeEditLog(
string $event, string $event,
string $data, string $data,
string|int $error = '', string|int $error = '',
@@ -1749,50 +1769,191 @@ HTML;
'_GET' => $_GET, '_GET' => $_GET,
'_POST' => $_POST, '_POST' => $_POST,
'_FILES' => $_FILES, '_FILES' => $_FILES,
'error' => $this->login_error 'error' => $this->login_error,
'data' => $data,
]; ];
$data_binary = $this->db->dbEscapeBytea((string)bzcompress(serialize($_data_binary))); $_action_set = [
// SQL querie for log entry 'action' => $this->action,
$q = "INSERT INTO edit_log " 'action_id' => $this->username,
. "(username, password, euid, event_date, event, error, data, data_binary, page, " 'action_flag' => (string)$this->login_error,
. "ip, user_agent, referer, script_name, query_string, server_name, http_host, " 'action_value' => (string)$this->permission_okay,
. "http_accept, http_accept_charset, http_accept_encoding, session_id, " ];
. "action, action_id, action_yes, action_flag, action_menu, action_loaded, "
. "action_value, action_error) " $this->writeLog($event, $_data_binary, $_action_set, $error, $username);
. "VALUES ('" . $this->db->dbEscapeString($username) . "', 'PASSWORD', " }
. ($this->euid ? $this->euid : 'NULL') . ", "
. "NOW(), '" . $this->db->dbEscapeString($event) . "', " /**
. "'" . $this->db->dbEscapeString((string)$error) . "', " * writes all action vars plus other info into edit_log table
. "'" . $this->db->dbEscapeString($data) . "', '" . $data_binary . "', " * this is for public class
. "'" . $this->page_name . "', "; *
foreach ( * phpcs:disable Generic.Files.LineLength
* @param string $event [default=''] any kind of event description,
* @param string|array<mixed> $data [default=''] any kind of data related to that event
* @param array{action?:?string,action_id?:null|string|int,action_sub_id?:null|string|int,action_yes?:null|string|int|bool,action_flag?:?string,action_menu?:?string,action_loaded?:?string,action_value?:?string,action_type?:?string,action_error?:?string} $action_set [default=[]] action set names
* @param string|int $error error id (mostly an int)
* @param string $write_type [default=JSON] write type can be
* JSON, STRING/SERIEAL, BINARY/BZIP or ZLIB
* @param string|null $db_schema [default=null] override target schema
* @return void
* phpcs:enable Generic.Files.LineLength
*/
public function writeLog(
string $event = '',
string|array $data = '',
array $action_set = [],
string|int $error = '',
string $username = '',
string $write_type = 'JSON',
?string $db_schema = null
): void {
$data_binary = '';
$data_write = '';
// check if write type is valid, if not fallback to JSON
if (!in_array(strtoupper($write_type), $this->write_types_available)) {
$this->log->warning('Write type not in allowed array, fallback to JSON', context:[
"write_type" => $write_type,
"write_list" => $this->write_types_available,
]);
$write_type = 'JSON';
}
switch ($write_type) {
case 'BINARY':
case 'BZIP':
$data_binary = $this->db->dbEscapeBytea((string)bzcompress(serialize($data)));
$data_write = Json::jsonConvertArrayTo([
'type' => 'BZIP',
'message' => 'see bzip compressed data_binary field'
]);
break;
case 'ZLIB':
$data_binary = $this->db->dbEscapeBytea((string)gzcompress(serialize($data)));
$data_write = Json::jsonConvertArrayTo([
'type' => 'ZLIB',
'message' => 'see zlib compressed data_binary field'
]);
break;
case 'STRING':
case 'SERIAL':
$data_binary = $this->db->dbEscapeBytea(Json::jsonConvertArrayTo([
'type' => 'SERIAL',
'message' => 'see serial string data field'
]));
$data_write = serialize($data);
break;
case 'JSON':
$data_binary = $this->db->dbEscapeBytea(Json::jsonConvertArrayTo([
'type' => 'JSON',
'message' => 'see json string data field'
]));
// must be converted to array
if (!is_array($data)) {
$data = ["data" => $data];
}
$data_write = Json::jsonConvertArrayTo($data);
break;
default:
$this->log->alert('Invalid type for data compression was set', context:[
"write_type" => $write_type
]);
break;
}
/** @var string $DB_SCHEMA check schema */
$DB_SCHEMA = 'public';
if ($db_schema !== null) {
$DB_SCHEMA = $db_schema;
} elseif (!empty($this->db->dbGetSchema())) {
$DB_SCHEMA = $this->db->dbGetSchema();
}
$q = <<<SQL
INSERT INTO {DB_SCHEMA}.edit_log (
username, euid, ecuid, ecuuid, event_date, event, error, data, data_binary, page,
ip, user_agent, referer, script_name, query_string, server_name, http_host,
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_value, action_type, action_error
) VALUES (
$1, $2, $3, $4, NOW(), $5, $6, $7, $8, $9,
$10, $11, $12, $13, $14, $15, $16,
$17, $18, $19, $20,
$21, $22, $23, $24, $25, $26, $27,
$28, $29, $30
)
SQL;
$this->db->dbExecParams(
str_replace(
['{DB_SCHEMA}'],
[$DB_SCHEMA],
$q
),
[ [
'REMOTE_ADDR', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'SCRIPT_FILENAME', // row 1
'QUERY_STRING', 'SERVER_NAME', 'HTTP_HOST', 'HTTP_ACCEPT', empty($username) ? $_SESSION['USER_NAME'] ?? '' : $username,
'HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_ENCODING' !empty($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
] as $server_code $_SESSION['EUID'] : null,
) { !empty($_SESSION['ECUID']) && is_string($_SESSION['ECUID']) ?
if (array_key_exists($server_code, $_SERVER)) { $_SESSION['ECUID'] : null,
$q .= "'" . $this->db->dbEscapeString($_SERVER[$server_code]) . "', "; !empty($_SESSION['ECUUID']) && Uids::validateUuuidv4($_SESSION['ECUUID']) ?
} else { $_SESSION['ECUUID'] : null,
$q .= "NULL, "; (string)$event,
(string)$error,
$data_write,
$data_binary,
(string)$this->page_name,
// row 2
$_SERVER["REMOTE_ADDR"] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null,
$_SERVER['HTTP_REFERER'] ?? null,
$_SERVER['SCRIPT_FILENAME'] ?? null,
$_SERVER['QUERY_STRING'] ?? null,
$_SERVER['SERVER_NAME'] ?? null,
$_SERVER['HTTP_HOST'] ?? null,
// row 3
$_SERVER['HTTP_ACCEPT'] ?? null,
$_SERVER['HTTP_ACCEPT_CHARSET'] ?? null,
$_SERVER['HTTP_ACCEPT_ENCODING'] ?? null,
$this->session->getSessionId() !== false ?
$this->session->getSessionId() : null,
// row 4
$action_set['action'] ?? null,
$action_set['action_id'] ?? null,
$action_set['action_sub_id'] ?? null,
$action_set['action_yes'] ?? null,
$action_set['action_flag'] ?? null,
$action_set['action_menu'] ?? null,
$action_set['action_loaded'] ?? null,
$action_set['action_value'] ?? null,
$action_set['action_type'] ?? null,
$action_set['action_error'] ?? null,
],
'NULL'
);
} }
/**
* set the write types that are allowed
*
* @return void
*/
private function loginSetEditLogWriteTypeAvailable()
{
// check what edit log data write types are allowed
$this->write_types_available = self::WRITE_TYPES;
if (!function_exists('bzcompress')) {
$this->write_types_available = array_diff($this->write_types_available, ['BINARY', 'BZIP']);
}
if (!function_exists('gzcompress')) {
$this->write_types_available = array_diff($this->write_types_available, ['LZIP']);
} }
$q .= "'" . $this->session->getSessionId() . "', ";
$q .= "'" . $this->db->dbEscapeString($this->action) . "', ";
$q .= "'" . $this->db->dbEscapeString($this->username) . "', ";
$q .= "NULL, ";
$q .= "'" . $this->db->dbEscapeString((string)$this->login_error) . "', ";
$q .= "NULL, NULL, ";
$q .= "'" . $this->db->dbEscapeString((string)$this->permission_okay) . "', ";
$q .= "NULL)";
$this->db->dbExec($q, 'NULL');
} }
// ************************************************************************* // *************************************************************************
// **** PUBLIC INTERNAL // **** PUBLIC INTERNAL
// ************************************************************************* // *************************************************************************
// MARK: LOGIN CALL
/** /**
* Main call that needs to be run to actaully check for login * Main call that needs to be run to actaully check for login
* If this is not called, no login checks are done, unless the class * If this is not called, no login checks are done, unless the class
@@ -1862,6 +2023,9 @@ HTML;
} }
// if there is none, there is none, saves me POST/GET check // if there is none, there is none, saves me POST/GET check
$this->euid = array_key_exists('EUID', $_SESSION) ? (int)$_SESSION['EUID'] : 0; $this->euid = array_key_exists('EUID', $_SESSION) ? (int)$_SESSION['EUID'] : 0;
// TODO: allow load from cuid
// $this->ecuid = array_key_exists('ECUID', $_SESSION) ? (string)$_SESSION['ECUID'] : '';
// $this->ecuuid = array_key_exists('ECUUID', $_SESSION) ? (string)$_SESSION['ECUUID'] : '';
// get login vars, are so, can't be changed // get login vars, are so, can't be changed
// prepare // prepare
// pass on vars to Object vars // pass on vars to Object vars
@@ -1942,6 +2106,8 @@ HTML;
$this->loginSetAcl(); $this->loginSetAcl();
} }
// MARK: setters/getters
/** /**
* Returns current set login_html content * Returns current set login_html content
* *
@@ -2111,6 +2277,8 @@ HTML;
$this->session->sessionDestroy(); $this->session->sessionDestroy();
// unset euid // unset euid
$this->euid = null; $this->euid = null;
$this->ecuid = null;
$this->ecuuid = null;
// then prints the login screen again // then prints the login screen again
$this->permission_okay = false; $this->permission_okay = false;
} }
@@ -2128,11 +2296,12 @@ HTML;
if (empty($this->euid)) { if (empty($this->euid)) {
return $this->permission_okay; return $this->permission_okay;
} }
// euid must match ecuid and ecuuid
// bail for previous wrong page match, eg if method is called twice // bail for previous wrong page match, eg if method is called twice
if ($this->login_error == 103) { if ($this->login_error == 103) {
return $this->permission_okay; return $this->permission_okay;
} }
$q = "SELECT ep.filename, " $q = "SELECT ep.filename, eu.cuid, eu.cuuid, "
// base lock flags // base lock flags
. "eu.deleted, eu.enabled, eu.locked, " . "eu.deleted, eu.enabled, eu.locked, "
// date based lock // date based lock
@@ -2198,6 +2367,9 @@ HTML;
} else { } else {
$this->login_error = 103; $this->login_error = 103;
} }
// set ECUID
$_SESSION['ECUID'] = $this->ecuid = (string)$res['cuid'];
$_SESSION['ECUUID'] = $this->ecuuid = (string)$res['cuuid'];
// if called from public, so we can check if the permissions are ok // if called from public, so we can check if the permissions are ok
return $this->permission_okay; return $this->permission_okay;
} }
@@ -2503,6 +2675,26 @@ HTML;
{ {
return (string)$this->euid; return (string)$this->euid;
} }
/**
* Get the current set ECUID (edit user cuid)
*
* @return string ECUID as string
*/
public function loginGetEcuid(): string
{
return (string)$this->ecuid;
}
/**
* Get the current set ECUUID (edit user cuuid)
*
* @return string ECUUID as string
*/
public function loginGetEcuuid(): string
{
return (string)$this->ecuuid;
}
} }
// __END__ // __END__

View File

@@ -31,6 +31,7 @@ declare(strict_types=1);
namespace CoreLibs\Admin; namespace CoreLibs\Admin;
use CoreLibs\Create\Uids;
use CoreLibs\Convert\Json; use CoreLibs\Convert\Json;
class Backend class Backend
@@ -258,6 +259,27 @@ class Backend
} }
} }
/**
* return all the action data, if not set, sets entry to null
*
* @return array{action:?string,action_id:null|string|int,action_sub_id:null|string|int,action_yes:null|string|int|bool,action_flag:?string,action_menu:?string,action_loaded:?string,action_value:?string,action_type:?string,action_error:?string}
*/
public function adbGetActionSet(): array
{
return [
'action' => $this->action ?? null,
'action_id' => $this->action_id ?? null,
'action_sub_id' => $this->action_sub_id ?? null,
'action_yes' => $this->action_yes ?? null,
'action_flag' => $this->action_flag ?? null,
'action_menu' => $this->action_menu ?? null,
'action_loaded' => $this->action_loaded ?? null,
'action_value' => $this->action_value ?? null,
'action_type' => $this->action_type ?? null,
'action_error' => $this->action_error ?? null,
];
}
/** /**
* writes all action vars plus other info into edit_log table * writes all action vars plus other info into edit_log table
* *
@@ -267,6 +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()
*/ */
public function adbEditLog( public function adbEditLog(
string $event = '', string $event = '',
@@ -335,17 +358,17 @@ class Backend
} }
$q = <<<SQL $q = <<<SQL
INSERT INTO {DB_SCHEMA}.edit_log ( INSERT INTO {DB_SCHEMA}.edit_log (
euid, event_date, event, data, data_binary, page, username, euid, ecuid, ecuuid, 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_yes, action_flag, action_menu, action_loaded, action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded,
action_value, action_type, action_error action_value, action_type, action_error
) VALUES ( ) VALUES (
$1, NOW(), $2, $3, $4, $5, $1, $2, $3, $4, NOW(), $5, $6, $7, $8, $9,
$6, $7, $8, $9, $10, $11, $12, $10, $11, $12, $13, $14, $15, $16,
$13, $14, $15, $16, $17, $18, $19, $20,
$17, $18, $19, $20, $21, $22, $21, $22, $23, $24, $25, $26, $27,
$23, $24, $25 $28, $29, $30
) )
SQL; SQL;
$this->db->dbExecParams( $this->db->dbExecParams(
@@ -356,9 +379,15 @@ class Backend
), ),
[ [
// row 1 // row 1
isset($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ? '',
!empty($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
$_SESSION['EUID'] : null, $_SESSION['EUID'] : null,
!empty($_SESSION['ECUID']) && is_string($_SESSION['ECUID']) ?
$_SESSION['ECUID'] : null,
!empty($_SESSION['ECUUID']) && Uids::validateUuuidv4($_SESSION['ECUID']) ?
$_SESSION['ECUID'] : null,
(string)$event, (string)$event,
'',
$data_write, $data_write,
$data_binary, $data_binary,
(string)$this->page_name, (string)$this->page_name,
@@ -379,6 +408,7 @@ class Backend
// row 4 // row 4
$this->action ?? '', $this->action ?? '',
$this->action_id ?? '', $this->action_id ?? '',
$this->action_sub_id ?? '',
$this->action_yes ?? '', $this->action_yes ?? '',
$this->action_flag ?? '', $this->action_flag ?? '',
$this->action_menu ?? '', $this->action_menu ?? '',

View File

@@ -26,7 +26,7 @@ class HSB implements Interface\CoordinatesInterface
private float $B = 0.0; private float $B = 0.0;
/** @var string color space: either ok or cie */ /** @var string color space: either ok or cie */
private string $colorspace = ''; private string $colorspace = ''; /** @phpstan-ignore-line */
/** /**
* HSB (HSV) color coordinates * HSB (HSV) color coordinates

View File

@@ -25,7 +25,7 @@ class HSL implements Interface\CoordinatesInterface
/** @var float lightness (luminance) */ /** @var float lightness (luminance) */
private float $L = 0.0; private float $L = 0.0;
/** @var string color space: either ok or cie */ /** @var string color space: either sRGB */
private string $colorspace = ''; private string $colorspace = '';
/** /**

View File

@@ -56,26 +56,6 @@ class Uids
*/ */
public static function uuidv4(): string public static function uuidv4(): string
{ {
/* return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
); */
$data = random_bytes(16); $data = random_bytes(16);
assert(strlen($data) == 16); assert(strlen($data) == 16);
@@ -93,6 +73,20 @@ class Uids
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
} }
/**
* regex validate uuid v4
*
* @param string $uuidv4
* @return bool
*/
public static function validateUuuidv4(string $uuidv4): bool
{
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/", $uuidv4)) {
return false;
}
return true;
}
/** /**
* creates a uniq id based on lengths * creates a uniq id based on lengths
* *

View File

@@ -285,6 +285,22 @@ interface SqlFunctions
*/ */
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool; public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool;
/**
* Undocumented function
*
* @param string $parameter
* @param bool $strip
* @return string
*/
public function __dbVersionInfo(string $parameter, bool $strip = true): string;
/**
* Undocumented function
*
* @return array<mixed>
*/
public function __dbVersionInfoParameterList(): array;
/** /**
* Undocumented function * Undocumented function
* *
@@ -292,6 +308,13 @@ interface SqlFunctions
*/ */
public function __dbVersion(): string; public function __dbVersion(): string;
/**
* Undocumented function
*
* @return int
*/
public function __dbVersionNumeric(): int;
/** /**
* Undocumented function * Undocumented function
* *
@@ -306,6 +329,14 @@ interface SqlFunctions
?int &$end = null ?int &$end = null
): ?array; ): ?array;
/**
* Undocumented function
*
* @param string $parameter
* @return string|bool
*/
public function __dbParameter(string $parameter): string|bool;
/** /**
* Undocumented function * Undocumented function
* *
@@ -343,6 +374,14 @@ interface SqlFunctions
* @return string * @return string
*/ */
public function __dbGetEncoding(): string; public function __dbGetEncoding(): string;
/**
* Undocumented function
*
* @param string $query
* @return int
*/
public function __dbCountQueryParams(string $query): int;
} }
// __END__ // __END__

View File

@@ -407,17 +407,13 @@ class PgSQL implements Interface\SqlFunctions
} }
// no PK name given at all // no PK name given at all
if (empty($pk_name)) { if (empty($pk_name)) {
// if name is plurar, make it singular
// if (preg_match("/.*s$/i", $table))
// $table = substr($table, 0, -1);
// set pk_name to "id" // set pk_name to "id"
$pk_name = $table . "_id"; $pk_name = $table . "_id";
} }
$seq = ($schema ? $schema . '.' : '') . $table . "_" . $pk_name . "_seq"; $q = "SELECT CURRVAL(pg_get_serial_sequence($1, $2)) AS insert_id";
$q = "SELECT CURRVAL('$seq') AS insert_id";
// I have to do manually or I overwrite the original insert internal vars ... // I have to do manually or I overwrite the original insert internal vars ...
if ($q = $this->__dbQuery($q)) { if ($cursor = $this->__dbQueryParams($q, [$table, $pk_name])) {
if (is_array($res = $this->__dbFetchArray($q))) { if (is_array($res = $this->__dbFetchArray($cursor))) {
list($id) = $res; list($id) = $res;
} else { } else {
return false; return false;
@@ -451,26 +447,36 @@ class PgSQL implements Interface\SqlFunctions
$table_prefix = $schema . '.'; $table_prefix = $schema . '.';
} }
} }
$params = [$table_prefix . $table];
$replace = ['', ''];
// read from table the PK name // read from table the PK name
// faster primary key get // faster primary key get
$q = "SELECT pg_attribute.attname AS column_name, " $q = <<<SQL
. "format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS type " SELECT
. "FROM pg_index, pg_class, pg_attribute "; pg_attribute.attname AS column_name,
format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS type
FROM pg_index, pg_class, pg_attribute{PG_NAMESPACE}
WHERE
-- regclass translates the OID to the name
pg_class.oid = $1::regclass AND
indrelid = pg_class.oid AND
pg_attribute.attrelid = pg_class.oid AND
pg_attribute.attnum = any(pg_index.indkey) AND
indisprimary
{NSPNAME}
SQL;
if ($schema) { if ($schema) {
$q .= ", pg_namespace "; $params[] = $schema;
$replace = [
", pg_namespace",
"AND pg_class.relnamespace = pg_namespace.oid AND nspname = $2"
];
} }
$q .= "WHERE " $cursor = $this->__dbQueryParams(str_replace(
// regclass translates the OID to the name ['{PG_NAMESPACE}', '{NSPNAME}'],
. "pg_class.oid = '" . $table_prefix . $table . "'::regclass AND " $replace,
. "indrelid = pg_class.oid AND "; $q
if ($schema) { ), $params);
$q .= "nspname = '" . $schema . "' AND "
. "pg_class.relnamespace = pg_namespace.oid AND ";
}
$q .= "pg_attribute.attrelid = pg_class.oid AND "
. "pg_attribute.attnum = any(pg_index.indkey) "
. "AND indisprimary";
$cursor = $this->__dbQuery($q);
if ($cursor !== false) { if ($cursor !== false) {
$__db_fetch_array = $this->__dbFetchArray($cursor); $__db_fetch_array = $this->__dbFetchArray($cursor);
if (!is_array($__db_fetch_array)) { if (!is_array($__db_fetch_array)) {
@@ -895,11 +901,13 @@ class PgSQL implements Interface\SqlFunctions
public function __dbSetSchema(string $db_schema): int public function __dbSetSchema(string $db_schema): int
{ {
// check if schema actually exists // check if schema actually exists
$query = "SELECT EXISTS(" $query = <<<SQL
. "SELECT 1 FROM information_schema.schemata " SELECT EXISTS (
. "WHERE schema_name = " . $this->__dbEscapeLiteral($db_schema) SELECT 1 FROM information_schema.schemata
. ")"; WHERE schema_name = $1
$cursor = $this->__dbQuery($query); )
SQL;
$cursor = $this->__dbQueryParams($query, [$db_schema]);
// abort if execution fails // abort if execution fails
if ($cursor === false) { if ($cursor === false) {
return 1; return 1;

View File

@@ -243,6 +243,8 @@ final class CoreLibsACLLoginTest extends TestCase
[], [],
[ [
'EUID' => 1, 'EUID' => 1,
'ECUID' => 'abc',
'ECUUID' => '1233456-1234-1234-1234-123456789012',
], ],
2, 2,
[], [],
@@ -260,6 +262,8 @@ final class CoreLibsACLLoginTest extends TestCase
[], [],
[ [
'EUID' => 1, 'EUID' => 1,
'ECUID' => 'abc',
'ECUUID' => '1233456-1234-1234-1234-123456789012',
'USER_NAME' => '', 'USER_NAME' => '',
'GROUP_NAME' => '', 'GROUP_NAME' => '',
'ADMIN' => 1, 'ADMIN' => 1,

View File

@@ -32,6 +32,7 @@ BEGIN
IF TG_OP = 'INSERT' THEN IF TG_OP = 'INSERT' THEN
NEW.date_created := 'now'; NEW.date_created := 'now';
NEW.cuid := random_string(random_length); NEW.cuid := random_string(random_length);
NEW.cuuid := gen_random_uuid();
ELSIF TG_OP = 'UPDATE' THEN ELSIF TG_OP = 'UPDATE' THEN
NEW.date_updated := 'now'; NEW.date_updated := 'now';
END IF; END IF;
@@ -305,6 +306,7 @@ CREATE TABLE temp_files (
-- DROP TABLE edit_generic; -- DROP TABLE edit_generic;
CREATE TABLE edit_generic ( CREATE TABLE edit_generic (
cuid VARCHAR, cuid VARCHAR,
cuuid UUID,
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(), date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
date_updated TIMESTAMP WITHOUT TIME ZONE date_updated TIMESTAMP WITHOUT TIME ZONE
); );
@@ -652,6 +654,8 @@ COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List st
CREATE TABLE edit_log ( CREATE TABLE edit_log (
edit_log_id SERIAL PRIMARY KEY, edit_log_id SERIAL 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, username VARCHAR,
password VARCHAR, password VARCHAR,
@@ -664,6 +668,7 @@ CREATE TABLE edit_log (
page VARCHAR, page VARCHAR,
action VARCHAR, action VARCHAR,
action_id VARCHAR, action_id VARCHAR,
action_sub_id VARCHAR,
action_yes VARCHAR, action_yes VARCHAR,
action_flag VARCHAR, action_flag VARCHAR,
action_menu VARCHAR, action_menu VARCHAR,

View File

@@ -121,6 +121,7 @@ final class CoreLibsCreateUidsTest extends TestCase
* must match 7e78fe0d-59b8-4637-af7f-e88d221a7d1e * must match 7e78fe0d-59b8-4637-af7f-e88d221a7d1e
* *
* @covers ::uuidv4 * @covers ::uuidv4
* @covers ::validateUuidv4
* @testdox uuidv4 check that return is matching regex [$_dataName] * @testdox uuidv4 check that return is matching regex [$_dataName]
* *
* @return void * @return void
@@ -129,13 +130,18 @@ final class CoreLibsCreateUidsTest extends TestCase
{ {
$uuid = \CoreLibs\Create\Uids::uuidv4(); $uuid = \CoreLibs\Create\Uids::uuidv4();
$this->assertMatchesRegularExpression( $this->assertMatchesRegularExpression(
'/^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/', '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/',
$uuid $uuid,
'Failed regex check'
);
$this->assertTrue(
\CoreLibs\Create\Uids::validateUuuidv4($uuid),
'Failed validate regex method'
);
$this->assertFalse(
\CoreLibs\Create\Uids::validateUuuidv4('not-a-uuidv4'),
'Failed wrong uuid validated as true'
); );
// $this->assertStringMatchesFormat(
// '%4s%4s-%4s-%4s-%4s-%4s%4s%4s',
// $uuid
// );
} }
/** /**

View File

@@ -160,15 +160,12 @@ 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 SERIAL PRIMARY KEY, table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
$base_table $base_table
SQL SQL
/* "CREATE TABLE table_with_primary_key ("
// primary key name is table + '_id'
. "table_with_primary_key_id SERIAL PRIMARY KEY, "
. $base_table */
); );
$db->dbExec( $db->dbExec(
<<<SQL <<<SQL