ACL Login update with cuuid and cuid add/update and move write log to login class

Add a UUIDv4 column to edit_generic as cuuid, add the cuid column to all reads with
the cuuid too

The cuuid will replace the cuid and remove the EUID as the session login var

Moved the adbEditLog to login class as writeLog and renamed the current private writeLog to writeEditLog which is only for internal logging in the class

The Backend log class is deprecated and a new get all action var method has been added to get the action vars into the edit log
This commit is contained in:
Clemens Schwaighofer
2024-12-03 13:12:22 +09:00
parent 5a21d22c7b
commit 7354632479
12 changed files with 749 additions and 461 deletions

View File

@@ -42,6 +42,20 @@ $backend = new CoreLibs\Admin\Backend(
$l10n,
DEFAULT_ACL_LEVEL
);
$login = new CoreLibs\ACL\Login(
$db,
$log,
$session,
[
'auto_login' => false,
'default_acl_level' => DEFAULT_ACL_LEVEL,
'logout_target' => '',
'site_locale' => SITE_LOCALE,
'site_domain' => SITE_DOMAIN,
'site_encoding' => SITE_ENCODING,
'locale_path' => BASE . INCLUDES . LOCALE,
]
);
use CoreLibs\Debug\Support;
$PAGE_NAME = 'TEST CLASS: ADMIN BACKEND';
@@ -55,10 +69,30 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "SETACL[]: <br>";
$backend->setACL(['EMPTY' => 'EMPTY']);
print "ADBEDITLOG: <br>";
$backend->adbEditLog('CLASSTEST-ADMIN-BINARY', 'Some info string', 'BINARY');
$backend->adbEditLog('CLASSTEST-ADMIN-ZLIB', 'Some info string', 'ZLIB');
$backend->adbEditLog('CLASSTEST-ADMIN-SERIAL', 'Some info string', 'SERIAL');
$backend->adbEditLog('CLASSTEST-ADMIN-INVALID', 'Some info string', 'INVALID');
$login->writeLog(
'CLASSTEST-ADMIN-BINARY',
'Some info string',
$backend->adbGetActionSet(),
write_type:'BINARY'
);
$login->writeLog(
'CLASSTEST-ADMIN-ZLIB',
'Some info string',
$backend->adbGetActionSet(),
write_type:'ZLIB'
);
$login->writeLog(
'CLASSTEST-ADMIN-SERIAL',
'Some info string',
$backend->adbGetActionSet(),
write_type:'SERIAL'
);
$login->writeLog(
'CLASSTEST-ADMIN-INVALID',
'Some info string',
$backend->adbGetActionSet(),
write_type:'INVALID'
);
// test with various
$backend->action = 'TEST ACTION';
$backend->action_id = 'TEST ACTION ID';
@@ -69,10 +103,10 @@ $backend->action_loaded = 'TEST ACTION LOADED';
$backend->action_value = 'TEST ACTION VALUE';
$backend->action_type = 'TEST ACTION TYPE';
$backend->action_error = 'TEST ACTION ERROR';
$backend->adbEditLog('CLASSTEST-ADMIN-JSON', [
$login->writeLog('CLASSTEST-ADMIN-JSON', [
"_GET" => $_GET,
"_POST" => $_POST,
], 'JSON');
], $backend->adbGetActionSet(), write_type:'JSON');
print "ADBTOPMENU(0): " . Support::printAr($backend->adbTopMenu(CONTENT_PATH)) . "<br>";
print "ADBMSG: <br>";

View File

@@ -58,4 +58,16 @@ echo "ACL: " . \CoreLibs\Debug\Support::printAr($login->loginGetAcl()) . "<br>";
echo "ACL (MIN): " . \CoreLibs\Debug\Support::printAr($login->loginGetAcl()['min'] ?? []) . "<br>";
echo "LOCALE: " . \CoreLibs\Debug\Support::printAr($login->loginGetLocale()) . "<br>";
echo "ECUID: " . $login->loginGetEcuid() . "<br>";
echo "ECUUID: " . $login->loginGetEcuuid() . "<br>";
$login->writeLog(
'TEST LOG',
[
'test' => 'TEST A'
],
error:'No Error',
write_type:'JSON'
);
print "</body></html>";

View File

@@ -205,6 +205,9 @@ print "HOST: " . HOST_NAME . " => DB HOST: " . DB_CONFIG_NAME . " => " . Support
print "DS is: " . DIRECTORY_SEPARATOR . "<br>";
print "SERVER HOST: " . $_SERVER['HTTP_HOST'] . "<br>";
print "ECUID: " . $_SESSION['ECUID'] . "<br>";
print "ECUUID: " . $_SESSION['ECUUID'] . "<br>";
print "</body></html>";
# __END__

View File

@@ -116,7 +116,7 @@ $data = [
// log action
// no log if login
if (!$login->loginActionRun()) {
$cms->adbEditLog('Submit', $data, 'BINARY');
$login->writeLog('Submit', $data, $cms->adbGetActionSet(), 'BINARY');
}
//------------------------------ logging end

View File

@@ -69,6 +69,7 @@ declare(strict_types=1);
namespace CoreLibs\ACL;
use CoreLibs\Security\Password;
use CoreLibs\Create\Uids;
use CoreLibs\Convert\Json;
class Login
@@ -77,6 +78,8 @@ class Login
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 */
private string $login_user_id = '';
/** @var string source, either _GET or _POST or empty */
@@ -195,6 +198,12 @@ class Login
/** @var bool */
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
/** @var array<string,mixed> options */
private array $options = [];
@@ -381,6 +390,8 @@ class Login
$_SESSION['DEFAULT_ACL_LIST'] = $this->default_acl_list;
$_SESSION['DEFAULT_ACL_LIST_TYPE'] = $this->default_acl_list_type;
$this->loginSetEditLogWriteTypeAvailable();
// this will be deprecated
if ($this->options['auto_login'] === true) {
$this->loginMainCall();
@@ -759,7 +770,7 @@ class Login
}
// have to get the global stuff here for setting it later
// we have to get the themes in here too
$q = "SELECT eu.edit_user_id, eu.cuid, eu.username, eu.password, "
$q = "SELECT eu.edit_user_id, eu.cuid, eu.cuuid, eu.username, eu.password, "
. "eu.edit_group_id, "
. "eg.name AS edit_group_name, eu.admin, "
// additinal acl lists
@@ -892,6 +903,7 @@ class Login
// set class var and session var
$_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
$this->loginCheckPermissions();
if ($this->login_error == 0) {
@@ -1137,6 +1149,7 @@ class Login
$this->acl['group_name'] = $_SESSION['GROUP_NAME'];
// edit user cuid
$this->acl['ecuid'] = $_SESSION['ECUID'];
$this->acl['ecuuid'] = $_SESSION['ECUUID'];
// set additional acl
$this->acl['additional_acl'] = [
'user' => $_SESSION['USER_ADDITIONAL_ACL'],
@@ -1430,7 +1443,7 @@ class Login
$data = 'Illegal user for password change: ' . $this->pw_username;
}
// 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);
}
/**
@@ -1571,7 +1584,7 @@ class Login
$username = $res['username'];
}
} // 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
// now close DB connection
// $this->error_msg = $this->_login();
@@ -1727,6 +1740,8 @@ HTML;
}
}
// MARK: LOGGING
/**
* writes detailed data into the edit user log table (keep log what user does)
*
@@ -1736,7 +1751,7 @@ HTML;
* @param string $username login user username
* @return void has no return
*/
private function writeLog(
private function writeEditLog(
string $event,
string $data,
string|int $error = '',
@@ -1754,50 +1769,191 @@ HTML;
'_GET' => $_GET,
'_POST' => $_POST,
'_FILES' => $_FILES,
'error' => $this->login_error
'error' => $this->login_error,
'data' => $data,
];
$data_binary = $this->db->dbEscapeBytea((string)bzcompress(serialize($_data_binary)));
// SQL querie for log entry
$q = "INSERT INTO edit_log "
. "(username, password, euid, 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_yes, action_flag, action_menu, action_loaded, "
. "action_value, action_error) "
. "VALUES ('" . $this->db->dbEscapeString($username) . "', 'PASSWORD', "
. ($this->euid ? $this->euid : 'NULL') . ", "
. "NOW(), '" . $this->db->dbEscapeString($event) . "', "
. "'" . $this->db->dbEscapeString((string)$error) . "', "
. "'" . $this->db->dbEscapeString($data) . "', '" . $data_binary . "', "
. "'" . $this->page_name . "', ";
foreach (
[
'REMOTE_ADDR', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'SCRIPT_FILENAME',
'QUERY_STRING', 'SERVER_NAME', 'HTTP_HOST', 'HTTP_ACCEPT',
'HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_ENCODING'
] as $server_code
) {
if (array_key_exists($server_code, $_SERVER)) {
$q .= "'" . $this->db->dbEscapeString($_SERVER[$server_code]) . "', ";
} else {
$q .= "NULL, ";
}
$_action_set = [
'action' => $this->action,
'action_id' => $this->username,
'action_flag' => (string)$this->login_error,
'action_value' => (string)$this->permission_okay,
];
$this->writeLog($event, $_data_binary, $_action_set, $error, $username);
}
/**
* writes all action vars plus other info into edit_log table
* this is for public class
*
* 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
),
[
// row 1
empty($username) ? $_SESSION['USER_NAME'] ?? '' : $username,
!empty($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
$_SESSION['EUID'] : null,
!empty($_SESSION['ECUID']) && is_string($_SESSION['ECUID']) ?
$_SESSION['ECUID'] : null,
!empty($_SESSION['ECUUID']) && Uids::validateUuuidv4($_SESSION['ECUUID']) ?
$_SESSION['ECUUID'] : 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
// *************************************************************************
// MARK: LOGIN CALL
/**
* 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
@@ -1869,6 +2025,7 @@ HTML;
$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
// prepare
// pass on vars to Object vars
@@ -1949,6 +2106,8 @@ HTML;
$this->loginSetAcl();
}
// MARK: setters/getters
/**
* Returns current set login_html content
*
@@ -2119,6 +2278,7 @@ HTML;
// unset euid
$this->euid = null;
$this->ecuid = null;
$this->ecuuid = null;
// then prints the login screen again
$this->permission_okay = false;
}
@@ -2136,12 +2296,12 @@ HTML;
if (empty($this->euid)) {
return $this->permission_okay;
}
// euid must match ecuid
// euid must match ecuid and ecuuid
// bail for previous wrong page match, eg if method is called twice
if ($this->login_error == 103) {
return $this->permission_okay;
}
$q = "SELECT ep.filename, eu.cuid, "
$q = "SELECT ep.filename, eu.cuid, eu.cuuid, "
// base lock flags
. "eu.deleted, eu.enabled, eu.locked, "
// date based lock
@@ -2209,6 +2369,7 @@ HTML;
}
// 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
return $this->permission_okay;
}
@@ -2520,10 +2681,20 @@ HTML;
*
* @return string ECUID as string
*/
public function loginGetEcid(): 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__

View File

@@ -31,6 +31,7 @@ declare(strict_types=1);
namespace CoreLibs\Admin;
use CoreLibs\Create\Uids;
use CoreLibs\Convert\Json;
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
*
@@ -267,6 +289,7 @@ class Backend
* JSON, STRING/SERIEAL, BINARY/BZIP or ZLIB
* @param string|null $db_schema [default=null] override target schema
* @return void
* @deprecated Use $login->writeLog() and set action_set from ->adbGetActionSet()
*/
public function adbEditLog(
string $event = '',
@@ -335,17 +358,17 @@ class Backend
}
$q = <<<SQL
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,
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
) VALUES (
$1, NOW(), $2, $3, $4, $5,
$6, $7, $8, $9, $10, $11, $12,
$13, $14, $15, $16,
$17, $18, $19, $20, $21, $22,
$23, $24, $25
$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(
@@ -356,9 +379,15 @@ class Backend
),
[
// row 1
isset($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
'',
!empty($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
$_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,
'',
$data_write,
$data_binary,
(string)$this->page_name,
@@ -379,6 +408,7 @@ class Backend
// row 4
$this->action ?? '',
$this->action_id ?? '',
$this->action_sub_id ?? '',
$this->action_yes ?? '',
$this->action_flag ?? '',
$this->action_menu ?? '',