From cbd47fb0151ac8b04d1096e7de7437d59e8b766a Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 5 Dec 2024 14:59:49 +0900 Subject: [PATCH 01/28] edit log table update, Change all DB tests serial to identity for primary key --- 4dev/database/table/edit_log.sql | 4 +-- .../CoreLibsACLLogin_database_create_data.sql | 32 +++++++++---------- 4dev/tests/DB/CoreLibsDBIOTest.php | 3 +- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/4dev/database/table/edit_log.sql b/4dev/database/table/edit_log.sql index 0ebf2599..e1de32d3 100644 --- a/4dev/database/table/edit_log.sql +++ b/4dev/database/table/edit_log.sql @@ -10,10 +10,10 @@ CREATE TABLE edit_log ( 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 FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, - username VARCHAR, - password VARCHAR, ecuid VARCHAR, ecuuid UUID, + username VARCHAR, + password VARCHAR, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, ip VARCHAR, error TEXT, diff --git a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql index d3a8d0ea..3d4a54b4 100644 --- a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql +++ b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql @@ -321,7 +321,7 @@ CREATE TABLE edit_generic ( -- DROP 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, flag VARCHAR ) INHERITS (edit_generic) WITHOUT OIDS; @@ -336,7 +336,7 @@ CREATE TABLE edit_visible_group ( -- DROP 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, flag VARCHAR, order_number INT NOT NULL @@ -354,7 +354,7 @@ CREATE TABLE edit_menu_group ( -- DROP 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 FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE, filename VARCHAR, @@ -378,7 +378,7 @@ CREATE TABLE edit_page ( -- DROP 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, 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, @@ -430,7 +430,7 @@ CREATE TABLE edit_page_menu_group ( -- DROP 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, level SMALLINT, type VARCHAR, @@ -447,7 +447,7 @@ CREATE TABLE edit_access_right ( -- DROP 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, name VARCHAR, header_color VARCHAR, @@ -466,7 +466,7 @@ CREATE TABLE edit_scheme ( -- DROP 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, lang_default SMALLINT NOT NULL DEFAULT 0, long_name VARCHAR, @@ -485,7 +485,7 @@ CREATE TABLE edit_language ( -- DROP 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, 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, @@ -507,7 +507,7 @@ CREATE TABLE edit_group ( -- DROP 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, 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, @@ -530,7 +530,7 @@ CREATE TABLE edit_page_access ( -- DROP 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, 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, @@ -551,7 +551,7 @@ CREATE TABLE edit_page_content ( -- DROP 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 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, @@ -652,11 +652,11 @@ COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List st -- DROP 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 + FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, ecuid VARCHAR, ecuuid UUID, - FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, username VARCHAR, password VARCHAR, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, @@ -712,7 +712,7 @@ ALTER TABLE edit_log_overflow ADD CONSTRAINT edit_log_overflow_euid_fkey FOREIGN -- DROP TABLE edit_access; 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, protected SMALLINT DEFAULT 0, deleted SMALLINT DEFAULT 0, @@ -733,7 +733,7 @@ CREATE TABLE edit_access ( -- DROP 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, 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, @@ -754,7 +754,7 @@ CREATE TABLE edit_access_user ( -- DROP 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, 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, diff --git a/4dev/tests/DB/CoreLibsDBIOTest.php b/4dev/tests/DB/CoreLibsDBIOTest.php index ec8a28e1..19420a47 100644 --- a/4dev/tests/DB/CoreLibsDBIOTest.php +++ b/4dev/tests/DB/CoreLibsDBIOTest.php @@ -17,7 +17,7 @@ Table with Primary Key: table_with_primary_key Table without Primary Key: table_without_primary_key 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 row_int INT, row_numeric NUMERIC, @@ -160,7 +160,6 @@ final class CoreLibsDBIOTest extends TestCase // create the tables $db->dbExec( // primary key name is table + '_id' - // table_with_primary_key_id SERIAL PRIMARY KEY, << Date: Fri, 6 Dec 2024 14:54:09 +0900 Subject: [PATCH 02/28] Add logout button to class.test.php for logout test, ANY placeholder db test --- www/admin/class_test.db.query-placeholder.php | 15 +++++++++++ www/admin/class_test.php | 27 ++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/www/admin/class_test.db.query-placeholder.php b/www/admin/class_test.db.query-placeholder.php index f93c39eb..b2d729c4 100644 --- a/www/admin/class_test.db.query-placeholder.php +++ b/www/admin/class_test.db.query-placeholder.php @@ -115,6 +115,21 @@ echo "INSERT ALL COLUMN TYPES: " . "ERROR: " . $db->dbGetLastError(true) . "
"; echo "
"; +print "ANY call
"; +$query = <<dbReturnParams($query, [$query_value]))) { + print "Result: " . Support::prAr($res) . "
"; +} + +echo "
"; + // test connectors: = , <> () for query detection // convert placeholder tests diff --git a/www/admin/class_test.php b/www/admin/class_test.php index 8b88bfe6..4f576fb2 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -62,9 +62,30 @@ $backend = new CoreLibs\Admin\Backend( $backend->db->dbInfo(true); ob_end_flush(); -print ""; -print "TEST CLASS"; -print ""; +print << + +TEST CLASS + + + +
+ +
+HTML; // key: file name, value; name $test_files = [ From b0449997725c07854c7c3ffae6290a91717b21b7 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 6 Dec 2024 16:31:20 +0900 Subject: [PATCH 03/28] ACL Login query update to params and heredoc All queries are in Params and all SQL is in heredoc blocks Disable 1011 error entry, this is no longer used --- www/lib/CoreLibs/ACL/Login.php | 708 ++++++++++++++++++++------------- 1 file changed, 422 insertions(+), 286 deletions(-) diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 691b03d6..ae9e53d7 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -276,10 +276,10 @@ class Login 'flag' => 'e' ], // blowfish password wrong - '1011' => [ + /* '1011' => [ 'msg' => 'Login Failed - Wrong Username or Password', 'flag' => 'e' - ], + ], */ // fallback md5 password wrong '1012' => [ 'msg' => 'Login Failed - Wrong Username or Password', @@ -373,8 +373,16 @@ class Login ]; // read the current edit_access_right list into an array - $q = "SELECT level, type, name FROM edit_access_right " - . "WHERE level >= 0 ORDER BY level"; + $q = <<= 0 + ORDER BY + level + SQL; while (is_array($res = $this->db->dbReturn($q))) { // level to description format (numeric) $this->default_acl_list[$res['level']] = [ @@ -398,7 +406,7 @@ class Login } // ************************************************************************* - // **** PROTECTED INTERNAL + // **** MARK: PROTECTED INTERNAL // ************************************************************************* /** @@ -441,7 +449,7 @@ class Login } // ************************************************************************* - // **** PRIVATE INTERNAL + // **** MARK: PRIVATE INTERNAL // ************************************************************************* /** @@ -630,6 +638,8 @@ class Login return true; } + // MARK: validation checks + /** * Checks for all flags and sets error codes for each * In order: @@ -748,6 +758,8 @@ class Login return $login_id_ok; } + // MARK: login user action + /** * if user pressed login button this script is called, * but only if there is no preview euid set @@ -769,74 +781,96 @@ 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.cuuid, eu.username, eu.password, " - . "eu.edit_group_id, " - . "eg.name AS edit_group_name, eu.admin, " - // additinal acl lists - . "eu.additional_acl AS user_additional_acl, " - . "eg.additional_acl AS group_additional_acl, " - // login error + locked - . "eu.login_error_count, eu.login_error_date_last, " - . "eu.login_error_date_first, eu.strict, eu.locked, " - // date based lock - . "CASE WHEN (" - . "(eu.lock_until IS NULL " - . "OR (eu.lock_until IS NOT NULL AND NOW() >= eu.lock_until)) " - . "AND (eu.lock_after IS NULL " - . "OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after))" - . ") THEN 0::INT ELSE 1::INT END locked_period, " - // debug (legacy) - . "eu.debug, eu.db_debug, " - // enabled - . "eu.enabled, eu.deleted, " - // for checks only - . "eu.login_user_id, " - // login id validation - . "CASE WHEN (" - . "(eu.login_user_id_valid_from IS NULL " - . "OR (eu.login_user_id_valid_from IS NOT NULL AND NOW() >= eu.login_user_id_valid_from)) " - . "AND (eu.login_user_id_valid_until IS NULL " - . "OR (eu.login_user_id_valid_until IS NOT NULL AND NOW() <= eu.login_user_id_valid_until))" - . ") THEN 1::INT ELSE 0::INT END AS login_user_id_valid_date, " - // check if user must login - . "CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL " - . "AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL " - . "AND (eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE " - . "<= NOW()::DATE " - . "THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, " - . "eu.login_user_id_locked, " - // language - . "el.short_name AS locale, el.iso_name AS encoding, " - // levels - . "eareu.level AS user_level, eareu.type AS user_type, " - . "eareg.level AS group_level, eareg.type AS group_type, " - // colors - . "first.header_color AS first_header_color, " - . "second.header_color AS second_header_color, second.template " - . "FROM edit_user eu " - . "LEFT JOIN edit_scheme second ON " - . "(second.edit_scheme_id = eu.edit_scheme_id AND second.enabled = 1), " - . "edit_language el, edit_group eg, " - . "edit_access_right eareu, " - . "edit_access_right eareg, " - . "edit_scheme first " - . "WHERE first.edit_scheme_id = eg.edit_scheme_id " - . "AND eu.edit_group_id = eg.edit_group_id " - . "AND eu.edit_language_id = el.edit_language_id " - . "AND eu.edit_access_right_id = eareu.edit_access_right_id " - . "AND eg.edit_access_right_id = eareg.edit_access_right_id " - . "AND " - // either login_user_id OR password must be given - . (!empty($this->login_user_id && empty($this->username)) ? - // check with login id if set and NO username - "eu.login_user_id = " . $this->db->dbEscapeLiteral($this->login_user_id) . " " : - // password match is done in script, against old plain or new blowfish encypted - "LOWER(username) = " . $this->db->dbEscapeLiteral(strtolower($this->username)) . " " - ); + $q = <<= eu.lock_until) + ) + AND ( + eu.lock_after IS NULL + OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after) + ) + ) THEN 0::INT ELSE 1::INT END locked_period, + -- debug (legacy) + eu.debug, eu.db_debug, + -- enabled + eu.enabled, eu.deleted, + -- for checks only + eu.login_user_id, + -- login id validation + CASE WHEN ( + ( + eu.login_user_id_valid_from IS NULL + OR (eu.login_user_id_valid_from IS NOT NULL AND NOW() >= eu.login_user_id_valid_from) + ) + AND ( + eu.login_user_id_valid_until IS NULL + OR (eu.login_user_id_valid_until IS NOT NULL AND NOW() <= eu.login_user_id_valid_until) + ) + ) THEN 1::INT ELSE 0::INT END AS login_user_id_valid_date, + -- check if user must login + CASE WHEN + eu.login_user_id_revalidate_after IS NOT NULL + AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL + AND (eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE + <= NOW()::DATE + THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, + eu.login_user_id_locked, + -- language + el.short_name AS locale, el.iso_name AS encoding, + -- levels + eareu.level AS user_level, eareu.type AS user_type, + eareg.level AS group_level, eareg.type AS group_type, + -- colors + first.header_color AS first_header_color, + second.header_color AS second_header_color, second.template + FROM edit_user eu + LEFT JOIN edit_scheme second ON + (second.edit_scheme_id = eu.edit_scheme_id AND second.enabled = 1), + edit_language el, edit_group eg, + edit_access_right eareu, + edit_access_right eareg, + edit_scheme first + WHERE first.edit_scheme_id = eg.edit_scheme_id + AND eu.edit_group_id = eg.edit_group_id + AND eu.edit_language_id = el.edit_language_id + AND eu.edit_access_right_id = eareu.edit_access_right_id + AND eg.edit_access_right_id = eareg.edit_access_right_id + AND {SEARCH_QUERY} + SQL; + $params = []; + $replace_string = ''; + // either login_user_id OR password must be given + if (!empty($this->login_user_id && empty($this->username))) { + // check with login id if set and NO username + $replace_string = 'eu.login_user_id = $1'; + $params = [$this->login_user_id]; + } else { + // password match is done in script, against old plain or new blowfish encypted + $replace_string = 'LOWER(username) = $1'; + $params = [strtolower($this->username)]; + } + $q = str_replace( + '{SEARCH_QUERY}', + $replace_string, + $q + ); // reset any query data that might exist - $this->db->dbCacheReset($q); + $this->db->dbCacheReset($q, $params); // never cache return data - $res = $this->db->dbReturn($q, $this->db::NO_CACHE); + $res = $this->db->dbReturnParams($q, $params, $this->db::NO_CACHE); // query was not run successful if (!empty($this->db->dbGetLastError())) { $this->login_error = 1009; @@ -893,10 +927,15 @@ class Login // .' => HASH: '.(Password::passwordRehashCheck($res['password']) ? 'NEW NEEDED' : 'OK')); if (Password::passwordRehashCheck($res['password'])) { // update password hash to new one now - $q = "UPDATE edit_user " - . "SET password = '" . $this->db->dbEscapeString(Password::passwordSet($this->password)) - . "' WHERE edit_user_id = " . $res['edit_user_id']; - $this->db->dbExec($q); + $q = <<db->dbExecParams($q, [ + Password::passwordSet($this->password), + $res['edit_user_id'] + ]); } // normal user processing // set class var and session var @@ -911,14 +950,19 @@ class Login // check if user is okay $this->loginCheckPermissions(); if ($this->login_error == 0) { + // set the dit group id + $edit_group_id = $res["edit_group_id"]; + // update last revalidate flag if ( !empty($res['login_user_id']) && !empty($this->username) && !empty($this->password) ) { - $q = "UPDATE edit_user SET " - . "login_user_id_last_revalidate = NOW() " - . "WHERE edit_user_id = " . $this->euid; - $this->db->dbExec($q); + $q = <<db->dbExecParams($q, [$this->euid]); } $locale = $res['locale'] ?? 'en'; $encoding = $res['encoding'] ?? 'UTF-8'; @@ -963,28 +1007,37 @@ class Login // Check\Colors::validateColor() // reset any login error count for this user if ($res['login_error_count'] > 0) { - $q = "UPDATE edit_user " - . "SET login_error_count = 0, login_error_date_last = NULL, " - . "login_error_date_first = NULL " - . "WHERE edit_user_id = " . $res['edit_user_id']; - $this->db->dbExec($q); + $q = <<db->dbExecParams($q, [$this->euid]); } $edit_page_ids = []; $pages = []; $pages_acl = []; // set pages access - $q = "SELECT ep.edit_page_id, ep.cuid, epca.cuid AS content_alias_uid, " - . "ep.hostname, ep.filename, ep.name AS edit_page_name, " - . "ep.order_number AS edit_page_order, ep.menu, " - . "ep.popup, ep.popup_x, ep.popup_y, ep.online, ear.level, ear.type " - . "FROM edit_page ep " - . "LEFT JOIN edit_page epca ON (epca.edit_page_id = ep.content_alias_edit_page_id)" - . ", edit_page_access epa, edit_access_right ear " - . "WHERE ep.edit_page_id = epa.edit_page_id " - . "AND ear.edit_access_right_id = epa.edit_access_right_id " - . "AND epa.enabled = 1 AND epa.edit_group_id = " . $res["edit_group_id"] . " " - . "ORDER BY ep.order_number"; - while (is_array($res = $this->db->dbReturn($q))) { + $q = <<db->dbReturnParams($q, [$edit_group_id]))) { // page id array for sub data readout $edit_page_ids[$res['edit_page_id']] = $res['cuid']; // create the array for pages @@ -1010,21 +1063,30 @@ class Login // make reference filename -> level $pages_acl[$res['filename']] = $res['level']; } // for each page + // edit page id params + $params = ['{' . join(',', array_keys($edit_page_ids)) . '}']; // get the visible groups for all pages and write them to the pages - $q = "SELECT epvg.edit_page_id, name, flag " - . "FROM edit_visible_group evp, edit_page_visible_group epvg " - . "WHERE evp.edit_visible_group_id = epvg.edit_visible_group_id " - . "AND epvg.edit_page_id IN (" . join(', ', array_keys($edit_page_ids)) . ") " - . "ORDER BY epvg.edit_page_id"; - while (is_array($res = $this->db->dbReturn($q))) { + $q = <<db->dbReturnParams($q, $params))) { $pages[$edit_page_ids[$res['edit_page_id']]]['visible'][$res['name']] = $res['flag']; } // get the same for the query strings - $q = "SELECT eqs.edit_page_id, name, value, dynamic FROM edit_query_string eqs " - . "WHERE enabled = 1 AND edit_page_id " - . "IN (" . join(', ', array_keys($edit_page_ids)) . ") " - . "ORDER BY eqs.edit_page_id"; - while (is_array($res = $this->db->dbReturn($q))) { + $q = <<db->dbReturnParams($q, $params))) { $pages[$edit_page_ids[$res['edit_page_id']]]['query'][] = [ 'name' => $res['name'], 'value' => $res['value'], @@ -1032,13 +1094,17 @@ class Login ]; } // get the page content and add them to the page - $q = "SELECT epc.edit_page_id, epc.name, epc.uid, epc.order_number, " - . "epc.online, ear.level, ear.type " - . "FROM edit_page_content epc, edit_access_right ear " - . "WHERE epc.edit_access_right_id = ear.edit_access_right_id AND " - . "epc.edit_page_id IN (" . join(', ', array_keys($edit_page_ids)) . ") " - . "ORDER BY epc.order_number"; - while (is_array($res = $this->db->dbReturn($q))) { + $q = <<db->dbReturnParams($q, $params))) { $pages[$edit_page_ids[$res['edit_page_id']]]['content'][$res['uid']] = [ 'name' => $res['name'], 'uid' => $res['uid'], @@ -1055,28 +1121,36 @@ class Login 'PAGES_ACL_LEVEL' => $pages_acl, ]); // load the edit_access user rights - $q = "SELECT ea.edit_access_id, level, type, ea.name, " - . "ea.color, ea.uid, edit_default, ea.additional_acl " - . "FROM edit_access_user eau, edit_access_right ear, edit_access ea " - . "WHERE eau.edit_access_id = ea.edit_access_id " - . "AND eau.edit_access_right_id = ear.edit_access_right_id " - . "AND eau.enabled = 1 AND edit_user_id = " . $this->euid . " " - . "ORDER BY ea.name"; + $q = <<db->dbReturn($q))) { + $unit_uid_kookup = []; + while (is_array($res = $this->db->dbReturnParams($q, [$this->euid]))) { // read edit access data fields and drop them into the unit access array - $q_sub = "SELECT name, value " - . "FROM edit_access_data " - . "WHERE enabled = 1 AND edit_access_id = " . $res['edit_access_id']; + $q_sub = <<db->dbReturn($q_sub))) { + while (is_array($res_sub = $this->db->dbReturnParams($q_sub, [$res['edit_access_id']]))) { $ea_data[$res_sub['name']] = $res_sub['value']; } + $unit_cuid_lookup[$res['edit_access_id']] = $res['cuid']; // build master unit array - $unit_access[$res['edit_access_id']] = [ + $unit_access[$res['cuid']] = [ 'id' => (int)$res['edit_access_id'], 'acl_level' => $res['level'], 'acl_type' => $res['type'], @@ -1090,14 +1164,16 @@ class Login // set the default unit if ($res['edit_default']) { $this->session->set('UNIT_DEFAULT', (int)$res['edit_access_id']); + $this->session->set('UNIT_DEFAULT_CUID', (int)$res['cuid']); } - $unit_uid[$res['uid']] = (int)$res['edit_access_id']; + $unit_uid_kookup[$res['uid']] = (int)$res['edit_access_id']; // sub arrays for simple access array_push($eauid, $res['edit_access_id']); $unit_acl[$res['edit_access_id']] = $res['level']; } $this->session->setMany([ - 'UNIT_UID' => $unit_uid, + 'UNIT_UID' => $unit_uid_kookup, + 'UNIT_CUID' => $unit_cuid_lookup, 'UNIT' => $unit_access, 'UNIT_ACL_LEVEL' => $unit_acl, 'EAID' => $eauid, @@ -1110,11 +1186,18 @@ class Login $login_error_date_first = ", login_error_date_first = NOW()"; } // update login error count for this user - $q = "UPDATE edit_user " - . "SET login_error_count = login_error_count + 1, " - . "login_error_date_last = NOW() " . $login_error_date_first . " " - . "WHERE edit_user_id = " . $res['edit_user_id']; - $this->db->dbExec($q); + $q = <<db->dbExecParams( + str_replace('{LOGIN_ERROR_SQL}', $login_error_date_first, $q), + [$res['edit_user_id']] + ); // totally lock the user if error max is reached if ( $this->max_login_error_count != -1 && @@ -1124,7 +1207,12 @@ class Login // if strict is set, lock this user // this needs manual unlocking by an admin user if ($res['strict'] && !in_array($this->username, $this->lock_deny_users)) { - $q = "UPDATE edit_user SET locked = 1 WHERE edit_user_id = " . $res['edit_user_id']; + $q = <<debug('ACL', $this->print_ar($this->acl)); } + // MARK: lgin set locale + /** * set locale * if invalid, set to empty string @@ -1323,6 +1415,8 @@ class Login ]; } + // MARK: password handling + /** * checks if the password is in a valid format * @@ -1461,6 +1555,8 @@ class Login $this->writeEditLog($event, $data, $this->login_error, $this->pw_username); } + // MARK: set HTML login page + /** * creates the login html part if no permission (error) is set * this does not print anything yet @@ -1570,6 +1666,8 @@ class Login return $html_string; } + // MARK: logout call + /** * last function called, writes log and prints out error msg and * exists script if permission 0 @@ -1593,9 +1691,13 @@ class Login // prepare for log if ($this->euid) { // get user from user table - $q = "SELECT username FROM edit_user WHERE edit_user_id = " . $this->euid; + $q = <<db->dbReturnRow($q))) { + if (is_array($res = $this->db->dbReturnRowParams($q, [$this->euid]))) { $username = $res['username']; } } // if euid is set, get username (or try) @@ -1610,6 +1712,8 @@ class Login } } + // MARK: set template for login page + /** * checks if there are external templates, if not uses internal fallback ones * @@ -1967,7 +2071,7 @@ HTML; // **** PUBLIC INTERNAL // ************************************************************************* - // MARK: LOGIN CALL + // MARK: PUBLIC LOGIN CALL /** * Main call that needs to be run to actaully check for login @@ -2153,6 +2257,108 @@ HTML; return $this->login_is_ajax_page; } + /** + * Returns current set loginUserId or empty if unset + * + * @return string loginUserId or empty string for not set + */ + public function loginGetLoginUserId(): string + { + return $this->login_user_id; + } + + /** + * Returns GET/POST for where the loginUserId was set + * + * @return string GET or POST or empty string for not set + */ + public function loginGetLoginUserIdSource(): string + { + return $this->login_user_id_source; + } + + /** + * Returns unclear login user id state. If true then illegal characters + * where present in the loginUserId parameter + * + * @return bool False for clear, True if illegal characters found + */ + public function loginGetLoginUserIdUnclean(): bool + { + return $this->login_user_id_unclear; + } + + /** + * Return locale settings with + * locale + * domain + * encoding + * path + * + * empty string if not set + * + * @return array Locale settings + */ + public function loginGetLocale(): array + { + return $this->locale; + } + + /** + * return header color or null for not set + * + * @return string|null Header color in RGB hex with leading sharp + */ + public function loginGetHeaderColor(): ?string + { + return $this->session->get('HEADER_COLOR'); + } + + /** + * Return the current loaded list of pages the user can access + * + * @return array + */ + public function loginGetPages(): array + { + + return $this->session->get('PAGES'); + } + + // MARK: logged in uid(pk)/cuid/eccuid + + /** + * Get the current set EUID (edit user id) + * + * @return string EUID as string + */ + public function loginGetEuid(): string + { + 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; + } + + // MARK: get error messages + /** * returns the last set error code * @@ -2199,6 +2405,8 @@ HTML; return $string; } + // MARK: password checks + /** * Sets the minium length and checks on valid. * Current max length is 255 characters @@ -2251,6 +2459,8 @@ HTML; return $value; } + // MARK: max login count + /** * Set the maximum login errors a user can have before getting locked * if the user has the strict lock setting turned on @@ -2277,6 +2487,8 @@ HTML; return $this->max_login_error_count; } + // MARK: LGOUT USER + /** * if a user pressed on logout, destroyes session and unsets all global vars * @@ -2298,6 +2510,8 @@ HTML; $this->permission_okay = false; } + // MARK: logged in user permssion check + /** * for every page the user access this script checks if he is allowed to do so * @@ -2316,39 +2530,53 @@ HTML; if ($this->login_error == 103) { return $this->permission_okay; } - $q = "SELECT ep.filename, eu.cuid, eu.cuuid, " - // base lock flags - . "eu.deleted, eu.enabled, eu.locked, " - // date based lock - . "CASE WHEN (" - . "(eu.lock_until IS NULL " - . "OR (eu.lock_until IS NOT NULL AND NOW() >= eu.lock_until)) " - . "AND (eu.lock_after IS NULL " - . "OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after))" - . ") THEN 0::INT ELSE 1::INT END locked_period, " - // login id validation - . "login_user_id, " - . "CASE WHEN (" - . "(eu.login_user_id_valid_from IS NULL " - . "OR (eu.login_user_id_valid_from IS NOT NULL AND NOW() >= eu.login_user_id_valid_from)) " - . "AND (eu.login_user_id_valid_until IS NULL " - . "OR (eu.login_user_id_valid_until IS NOT NULL AND NOW() <= eu.login_user_id_valid_until))" - . ") THEN 1::INT ELSE 0::INT END AS login_user_id_valid_date, " - // check if user must login - . "CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL " - . "AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL " - . "AND eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after <= NOW()::DATE " - . "THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, " - . "eu.login_user_id_locked " - // - . "FROM edit_page ep, edit_page_access epa, edit_group eg, edit_user eu " - . "WHERE ep.edit_page_id = epa.edit_page_id " - . "AND eg.edit_group_id = epa.edit_group_id " - . "AND eg.edit_group_id = eu.edit_group_id " - . "AND eu.edit_user_id = " . $this->euid . " " - . "AND ep.filename = '" . $this->page_name . "' " - . "AND eg.enabled = 1 AND epa.enabled = 1"; - $res = $this->db->dbReturnRow($q); + $q = <<= eu.lock_until) + ) + AND ( + eu.lock_after IS NULL + OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after) + ) + ) THEN 0::INT ELSE 1::INT END locked_period, + -- login id validation + login_user_id, + CASE WHEN ( + ( + eu.login_user_id_valid_from IS NULL + OR (eu.login_user_id_valid_from IS NOT NULL AND NOW() >= eu.login_user_id_valid_from) + ) + AND ( + eu.login_user_id_valid_until IS NULL + OR (eu.login_user_id_valid_until IS NOT NULL AND NOW() <= eu.login_user_id_valid_until) + ) + ) THEN 1::INT ELSE 0::INT END AS login_user_id_valid_date, + -- check if user must login + CASE WHEN + eu.login_user_id_revalidate_after IS NOT NULL + AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL + AND eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after <= NOW()::DATE + THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, + eu.login_user_id_locked + -- + FROM + edit_page ep, edit_page_access epa, edit_group eg, edit_user eu + WHERE + ep.edit_page_id = epa.edit_page_id + AND eg.edit_group_id = epa.edit_group_id + AND eg.edit_group_id = eu.edit_group_id + AND eg.enabled = 1 AND epa.enabled = 1 + AND eu.edit_user_id = $1 + AND ep.filename = $2 + SQL; + $res = $this->db->dbReturnRowParams($q, [$this->euid, $this->page_name]); if (!is_array($res)) { $this->login_error = 109; return $this->permission_okay; @@ -2403,6 +2631,8 @@ HTML; return $this->permission_okay; } + // MARK: ACL acess check + /** * Check if source (page, base) is matching to the given min access string * min access string must be valid access level string (eg read, mod, write) @@ -2503,6 +2733,8 @@ HTML; return (int)$this->default_acl_list_type[$type]; } + // MARK: edit access helpers + /** * checks if this edit access id is valid * @@ -2563,6 +2795,21 @@ HTML; return $_SESSION['UNIT'][$edit_access_id]['data'][$data_key]; } + /** + * old name for loginGetEditAccessData + * + * @deprecated Use $login->loginGetEditAccessData() + * @param int $edit_access_id + * @param string|int $data_key + * @return bool|string + */ + public function loginSetEditAccessData( + int $edit_access_id, + string|int $data_key + ): bool|string { + return $this->loginGetEditAccessData($edit_access_id, $data_key); + } + /** * Return edit access primary key id from edit access uid * false on not found @@ -2591,6 +2838,8 @@ HTML; return false; } + // MARK: various basic login id checks + /** * Returns true if login button was pressed * @@ -2600,119 +2849,6 @@ HTML; { return empty($this->login) ? false : true; } - - /** - * Returns current set loginUserId or empty if unset - * - * @return string loginUserId or empty string for not set - */ - public function loginGetLoginUserId(): string - { - return $this->login_user_id; - } - - /** - * Returns GET/POST for where the loginUserId was set - * - * @return string GET or POST or empty string for not set - */ - public function loginGetLoginUserIdSource(): string - { - return $this->login_user_id_source; - } - - /** - * Returns unclear login user id state. If true then illegal characters - * where present in the loginUserId parameter - * - * @return bool False for clear, True if illegal characters found - */ - public function loginGetLoginUserIdUnclean(): bool - { - return $this->login_user_id_unclear; - } - - /** - * old name for loginGetEditAccessData - * - * @deprecated Use $login->loginGetEditAccessData() - * @param int $edit_access_id - * @param string|int $data_key - * @return bool|string - */ - public function loginSetEditAccessData( - int $edit_access_id, - string|int $data_key - ): bool|string { - return $this->loginGetEditAccessData($edit_access_id, $data_key); - } - - /** - * Return locale settings with - * locale - * domain - * encoding - * path - * - * empty string if not set - * - * @return array Locale settings - */ - public function loginGetLocale(): array - { - return $this->locale; - } - - /** - * return header color or null for not set - * - * @return string|null Header color in RGB hex with leading sharp - */ - public function loginGetHeaderColor(): ?string - { - return $this->session->get('HEADER_COLOR'); - } - - /** - * Return the current loaded list of pages the user can access - * - * @return array - */ - public function loginGetPages(): array - { - - return $this->session->get('PAGES'); - } - - /** - * Get the current set EUID (edit user id) - * - * @return string EUID as string - */ - public function loginGetEuid(): string - { - 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__ From a84ab86e31a7085b613b99ae79f48f4bb25984b0 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 6 Dec 2024 18:07:06 +0900 Subject: [PATCH 04/28] Various fixes for ACL Login methods with deprecated calls make all calls that go through primary keys as deprecated create CUID calls for all of them Update phpunit tests with new cuid tests, keep old deprecated tests --- 4dev/tests/ACL/CoreLibsACLLoginTest.php | 105 ++++++++++++++-- www/admin/class_test.login.php | 76 ++++++++++-- www/admin/class_test.php | 51 ++------ www/lib/CoreLibs/ACL/Login.php | 157 ++++++++++++++++-------- 4 files changed, 278 insertions(+), 111 deletions(-) diff --git a/4dev/tests/ACL/CoreLibsACLLoginTest.php b/4dev/tests/ACL/CoreLibsACLLoginTest.php index 92d3d978..a60f6f62 100644 --- a/4dev/tests/ACL/CoreLibsACLLoginTest.php +++ b/4dev/tests/ACL/CoreLibsACLLoginTest.php @@ -22,8 +22,12 @@ Not yet covered tests: */ final class CoreLibsACLLoginTest extends TestCase { - private static $db; - private static $log; + private static \CoreLibs\DB\IO $db; + 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 @@ -108,14 +112,40 @@ final class CoreLibsACLLoginTest extends TestCase self::$db->dbSetMaxQueryCall(-1); // insert additional content for testing (locked user, etc) $queries = [ - "INSERT INTO edit_access_data " - . "(edit_access_id, name, value, enabled) VALUES " - . "((SELECT edit_access_id FROM edit_access WHERE uid = 'AdminAccess'), " - . "'test', 'value', 1)" + <<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 // must set @@ -253,6 +283,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -273,10 +304,11 @@ final class CoreLibsACLLoginTest extends TestCase 'USER_ADDITIONAL_ACL' => [], 'GROUP_ADDITIONAL_ACL' => [], 'UNIT_UID' => [ - 'AdminAccess' => 1, + 'AdminAccess' => '123456789012', ], 'UNIT' => [ - 1 => [ + '123456789012' => [ + 'id' => 1, 'acl_level' => 80, 'name' => 'Admin Access', 'uid' => 'AdminAccess', @@ -297,6 +329,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -416,6 +449,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_deleted' => true @@ -441,6 +475,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_enabled' => true @@ -466,6 +501,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_locked' => true @@ -491,6 +527,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_get_locked' => true, @@ -515,6 +552,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_locked_period_until' => 'on' @@ -540,6 +578,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -559,6 +598,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -569,6 +609,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_locked_period_after' => 'on' @@ -594,6 +635,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_locked_period_until' => 'on', @@ -620,6 +662,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_login_user_id_locked' => true @@ -645,6 +688,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -663,6 +707,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -673,6 +718,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -692,6 +738,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -702,6 +749,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -721,6 +769,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -731,6 +780,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -750,6 +800,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -781,6 +832,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -804,6 +856,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -814,6 +867,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -837,6 +891,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -847,6 +902,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_login_user_id_revalidate_after' => 'on', @@ -873,6 +929,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -893,6 +950,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -903,6 +961,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_login_user_id_valid_from' => 'on', @@ -929,6 +988,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -949,6 +1009,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -959,6 +1020,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_login_user_id_valid_until' => 'on', @@ -985,6 +1047,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'base_access' => 'list', 'page_access' => 'list', 'test_login_user_id_valid_from' => 'on', @@ -1012,6 +1075,7 @@ final class CoreLibsACLLoginTest extends TestCase [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, + 'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'edit_access_uid' => 'AdminAccess', 'edit_access_data' => 'test', 'base_access' => 'list', @@ -1042,6 +1106,7 @@ final class CoreLibsACLLoginTest extends TestCase 'admin_flag' => true, 'check_access' => true, 'check_access_id' => 1, + 'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST', 'check_access_data' => 'value', 'base_access' => true, 'page_access' => true, @@ -1369,6 +1434,9 @@ final class CoreLibsACLLoginTest extends TestCase // run test try { + // preset, we cannot set that in the provider + $expected['check_access_cuid'] = self::$edit_access_cuid; + $mock_settings['edit_access_cuid'] = self::$edit_access_cuid; // if ajax call // check if parameter, or globals (old type) // else normal call @@ -1427,6 +1495,25 @@ final class CoreLibsACLLoginTest extends TestCase $login_mock->loginCheckAccessPage($mock_settings['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' + ); + // Deprecated // - loginCheckEditAccess $this->assertEquals( $expected['check_access'], @@ -1449,7 +1536,7 @@ final class CoreLibsACLLoginTest extends TestCase $this->assertEquals( $expected['check_access_data'], $login_mock->loginGetEditAccessData( - $mock_settings['edit_access_id'], + $mock_settings['edit_access_uid'], $mock_settings['edit_access_data'] ), 'Assert check access id data value valid' diff --git a/www/admin/class_test.login.php b/www/admin/class_test.login.php index 3d1327d5..bdf414b5 100644 --- a/www/admin/class_test.login.php +++ b/www/admin/class_test.login.php @@ -17,6 +17,9 @@ require 'config.php'; // define log file id $LOG_FILE_ID = 'classTest-login'; $SET_SESSION_NAME = EDIT_SESSION_NAME; + +use CoreLibs\Debug\Support; + // init login & backend class $session = new CoreLibs\Create\Session($SET_SESSION_NAME); $log = new CoreLibs\Logging\Logging([ @@ -43,24 +46,81 @@ ob_end_flush(); $login->loginMainCall(); $PAGE_NAME = 'TEST CLASS: LOGIN'; -print ""; -print "" . $PAGE_NAME . ""; -print ""; -print ''; -print '

' . $PAGE_NAME . '

'; +print str_replace( + '{PAGE_NAME}', + $PAGE_NAME, +<< + +{PAGE_NAME} + + + +

{PAGE_NAME}

+HTML +); + +// button logout +print << +function loginLogout() +{ + const form = document.createElement('form'); + form.method = 'post'; + const hiddenField = document.createElement('input'); + hiddenField.type = 'hidden'; + hiddenField.name = 'login_logout'; + hiddenField.value = 'Logout'; + form.appendChild(hiddenField); + document.body.appendChild(form); + form.submit(); +} + +
+ +
+HTML; +// string logout +print << +
+Logout + +
+ +HTML; echo "CHECK PERMISSION: " . ($login->loginCheckPermissions() ? 'OK' : 'BAD') . "
"; echo "IS ADMIN: " . ($login->loginIsAdmin() ? 'OK' : 'BAD') . "
"; echo "MIN ACCESS BASE: " . ($login->loginCheckAccessBase('admin') ? 'OK' : 'BAD') . "
"; echo "MIN ACCESS PAGE: " . ($login->loginCheckAccessPage('admin') ? 'OK' : 'BAD') . "
"; -echo "ACL: " . \CoreLibs\Debug\Support::printAr($login->loginGetAcl()) . "
"; -echo "ACL (MIN): " . \CoreLibs\Debug\Support::printAr($login->loginGetAcl()['min'] ?? []) . "
"; -echo "LOCALE: " . \CoreLibs\Debug\Support::printAr($login->loginGetLocale()) . "
"; +echo "ACL: " . Support::printAr($login->loginGetAcl()) . "
"; +echo "ACL (MIN): " . Support::printAr($login->loginGetAcl()['min'] ?? []) . "
"; +echo "LOCALE: " . Support::printAr($login->loginGetLocale()) . "
"; echo "ECUID: " . $login->loginGetEcuid() . "
"; echo "ECUUID: " . $login->loginGetEcuuid() . "
"; +echo "
"; +// set + check edit access id +$edit_access_cuid = 'buRW8Gu2Lkkf'; +if (isset($login->loginGetAcl()['unit'])) { + print "EDIT ACCESS CUID: " . $edit_access_cuid . "
"; + print "ACL UNIT: " . print_r(array_keys($login->loginGetAcl()['unit']), true) . "
"; + print "ACCESS CHECK: " . Support::prBl($login->loginCheckEditAccessCuid($edit_access_cuid)) . "
"; + if ($login->loginCheckEditAccessCuid($edit_access_cuid)) { + print "Set new:" . $edit_access_cuid . "
"; + } else { + print "Load default unit id: " . $login->loginGetAcl()['unit_id'] . "
"; + } +} else { + print "Something went wrong with the login
"; +} + +echo "
"; +print "SESSION: " . Support::printAr($_SESSION) . "
"; + $login->writeLog( 'TEST LOG', [ diff --git a/www/admin/class_test.php b/www/admin/class_test.php index 4f576fb2..7bff5bb9 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -149,33 +149,20 @@ foreach ($test_files as $file => $name) { print ''; } + +print "
"; +print "ECUID: " . $session->get('ECUID') . "
"; +print "ECUUID: " . $session->get('ECUUID') . "
"; + print "
"; -print "L: " . Support::dumpVar($locale) . "
"; +print "LOCALE: " . Support::dumpVar($locale) . "
"; // print all _ENV vars set print "
READ _ENV ARRAY:
"; print Support::dumpVar(array_map('htmlentities', $_ENV)); -// set + check edit access id -$edit_access_id = 3; -if (isset($login->loginGetAcl()['unit'])) { - print "ACL UNIT: " . print_r(array_keys($login->loginGetAcl()['unit']), true) . "
"; - print "ACCESS CHECK: " . (string)$login->loginCheckEditAccess($edit_access_id) . "
"; - if ($login->loginCheckEditAccess($edit_access_id)) { - $backend->edit_access_id = $edit_access_id; - } else { - $backend->edit_access_id = $login->loginGetAcl()['unit_id']; - } -} else { - print "Something went wrong with the login
"; -} // $backend->log->debug('SESSION', \CoreLibs\Debug\Support::dumpVar($_SESSION)); -print '
'; -print 'Logout'; -print ''; -print '
'; - +print "
"; print "Log Level: " . $backend->log->getLoggingLevel()->getName() . "
"; print "Log ID: " . $backend->log->getLogFileId() . "
"; print "Log Date: " . $backend->log->getLogDate() . "
"; @@ -197,26 +184,7 @@ foreach ( $log->debug('SOME MARK', 'Some error output'); -// INTERNAL SET -print "EDIT ACCESS ID: " . $backend->edit_access_id . "
"; -// print "ACL:
".$backend->print_ar($login->loginGetAcl())."
"; -// $log->debug('ACL', "ACL: " . \CoreLibs\Debug\Support::dumpVar($login->loginGetAcl())); -// print "DEFAULT ACL:
".$backend->print_ar($login->default_acl_list)."
"; -// print "DEFAULT ACL:
".$backend->print_ar($login->default_acl_list)."
"; -// $result = array_flip( -// array_filter( -// array_flip($login->default_acl_list), -// function ($key) { -// if (is_numeric($key)) { -// return $key; -// } -// } -// ) -// ); -// print "DEFAULT ACL:
".$backend->print_ar($result)."
"; -// DEPRICATED CALL -// $backend->adbSetACL($login->loginGetAcl()); - +print "
"; print "THIS HOST: " . HOST_NAME . ", with PROTOCOL: " . HOST_PROTOCOL . " is running SSL: " . HOST_SSL . "
"; print "DIR: " . DIR . "
"; print "BASE: " . BASE . "
"; @@ -226,9 +194,6 @@ print "HOST: " . HOST_NAME . " => DB HOST: " . DB_CONFIG_NAME . " => " . Support print "DS is: " . DIRECTORY_SEPARATOR . "
"; print "SERVER HOST: " . $_SERVER['HTTP_HOST'] . "
"; -print "ECUID: " . $session->get('ECUID') . "
"; -print "ECUUID: " . $session->get('ECUUID') . "
"; - print ""; # __END__ diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index ae9e53d7..a39dbaa8 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -1132,11 +1132,14 @@ class Login AND eau.enabled = 1 AND edit_user_id = $1 ORDER BY ea.name SQL; - $unit_access = []; + $unit_access_cuid = []; + // legacy + $unit_access_eaid = []; $unit_cuid_lookup = []; - $eauid = []; + $eaid = []; + $eacuid = []; $unit_acl = []; - $unit_uid_kookup = []; + $unit_uid_lookup = []; while (is_array($res = $this->db->dbReturnParams($q, [$this->euid]))) { // read edit access data fields and drop them into the unit access array $q_sub = <<db->dbReturnParams($q_sub, [$res['edit_access_id']]))) { $ea_data[$res_sub['name']] = $res_sub['value']; } - $unit_cuid_lookup[$res['edit_access_id']] = $res['cuid']; // build master unit array - $unit_access[$res['cuid']] = [ - 'id' => (int)$res['edit_access_id'], + $unit_access_cuid[$res['cuid']] = [ + 'id' => (int)$res['edit_access_id'], // DEPRECATED 'acl_level' => $res['level'], 'acl_type' => $res['type'], 'name' => $res['name'], @@ -1161,22 +1163,29 @@ class Login 'additional_acl' => Json::jsonConvertToArray($res['additional_acl']), 'data' => $ea_data ]; + $unit_access_eaid[$res['edit_access_id']] = [ + 'cuid' => $res['cuid'], + ]; // set the default unit if ($res['edit_default']) { - $this->session->set('UNIT_DEFAULT', (int)$res['edit_access_id']); - $this->session->set('UNIT_DEFAULT_CUID', (int)$res['cuid']); + $this->session->set('UNIT_DEFAULT_EAID', (int)$res['edit_access_id']); // DEPRECATED + $this->session->set('UNIT_DEFAULT_EACUID', (int)$res['cuid']); } - $unit_uid_kookup[$res['uid']] = (int)$res['edit_access_id']; + $unit_uid_lookup[$res['uid']] = $res['edit_access_id']; // DEPRECATED + $unit_cuid_lookup[$res['uid']] = $res['cuid']; // sub arrays for simple access - array_push($eauid, $res['edit_access_id']); - $unit_acl[$res['edit_access_id']] = $res['level']; + array_push($eaid, $res['edit_access_id']); + array_push($eacuid, $res['cuid']); + $unit_acl[$res['cuid']] = $res['level']; } $this->session->setMany([ - 'UNIT_UID' => $unit_uid_kookup, + 'UNIT_UID' => $unit_uid_lookup, // DEPRECATED 'UNIT_CUID' => $unit_cuid_lookup, - 'UNIT' => $unit_access, + 'UNIT' => $unit_access_cuid, + 'UNIT_LEGACY' => $unit_access_eaid, // DEPRECATED 'UNIT_ACL_LEVEL' => $unit_acl, - 'EAID' => $eauid, + 'EAID' => $eaid, // DEPRECATED + 'EACUID' => $eacuid, ]); } // user has permission to THIS page } // user was not enabled or other login error @@ -1308,32 +1317,35 @@ class Login $this->acl['unit_name'] = null; $this->acl['unit_uid'] = null; $this->acl['unit'] = []; + $this->acl['unit_legacy'] = []; $this->acl['unit_detail'] = []; // PER ACCOUNT (UNIT/edit access)-> - foreach ($_SESSION['UNIT'] as $ea_id => $unit) { + foreach ($_SESSION['UNIT'] as $ea_cuid => $unit) { // if admin flag is set, all units are set to 100 if (!empty($this->acl['admin'])) { - $this->acl['unit'][$ea_id] = $this->acl['base']; + $this->acl['unit'][$ea_cuid] = $this->acl['base']; } else { if ($unit['acl_level'] != -1) { - $this->acl['unit'][$ea_id] = $unit['acl_level']; + $this->acl['unit'][$ea_cuid] = $unit['acl_level']; } else { - $this->acl['unit'][$ea_id] = $this->acl['base']; + $this->acl['unit'][$ea_cuid] = $this->acl['base']; } } + // legacy + $this->acl['unit_legacy'][$unit['id']] = $this->acl['unit'][$ea_cuid]; // detail name/level set - $this->acl['unit_detail'][$ea_id] = [ + $this->acl['unit_detail'][$ea_cuid] = [ 'name' => $unit['name'], 'uid' => $unit['uid'], - 'level' => $this->default_acl_list[$this->acl['unit'][$ea_id]]['name'] ?? -1, + 'level' => $this->default_acl_list[$this->acl['unit'][$ea_cuid]]['name'] ?? -1, 'default' => $unit['default'], 'data' => $unit['data'], 'additional_acl' => $unit['additional_acl'] ]; // set default if (!empty($unit['default'])) { - $this->acl['unit_id'] = $unit['id']; + $this->acl['unit_cuid'] = $ea_cuid; $this->acl['unit_name'] = $unit['name']; $this->acl['unit_uid'] = $unit['uid']; } @@ -2741,18 +2753,60 @@ HTML; * @param int|null $edit_access_id access id pk to check * @return bool true/false: if the edit access is not * in the valid list: false + * @deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessCuid() */ public function loginCheckEditAccess(?int $edit_access_id): bool { if ($edit_access_id === null) { return false; } - if (array_key_exists($edit_access_id, $this->acl['unit'])) { + if (array_key_exists($edit_access_id, $this->acl['unit_legacy'])) { return true; } return false; } + /** + * check if this edit access cuid is valid + * + * @param string|null $cuid + * @return bool + */ + public function loginCheckEditAccessCuid(?string $cuid): bool + { + if ($cuid === null) { + return false; + } + if (array_key_exists($cuid, $this->acl['unit'])) { + return true; + } + return false; + } + + /** + * checks that the given edit access id is valid for this user + * return null if nothing set, or the edit access id + * + * @param string|null $cuid edit access cuid to check + * @return string|null same edit access cuid if ok + * or the default edit access id + * if given one is not valid + */ + public function loginCheckEditAccessValidCuid(?string $cuid): ?string + { + if ( + $cuid !== null && + is_array($this->session->get('UNIT')) && + !array_key_exists($cuid, $this->session->get('UNIT')) + ) { + $cuid = null; + if (!empty($this->session->get('UNIT_DEFAULT_EACUID'))) { + $cuid = $this->session->get('UNIT_DEFAULT_EACUID'); + } + } + return $cuid; + } + /** * checks that the given edit access id is valid for this user * return null if nothing set, or the edit access id @@ -2761,53 +2815,39 @@ HTML; * @return int|null same edit access id if ok * or the default edit access id * if given one is not valid + * @deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessValidCuid() */ public function loginCheckEditAccessId(?int $edit_access_id): ?int { if ( $edit_access_id !== null && - is_array($this->session->get('UNIT')) && - !array_key_exists($edit_access_id, $this->session->get('UNIT')) + is_array($this->session->get('UNIT_LEGACY')) && + !array_key_exists($edit_access_id, $this->session->get('UNIT_LEGACY')) ) { $edit_access_id = null; - if (is_numeric($this->session->get('UNIT_DEFAULT'))) { - $edit_access_id = (int)$this->session->get('UNIT_DEFAULT'); + if (!empty($this->session->get('UNIT_DEFAULT_EAID'))) { + $edit_access_id = (int)$this->session->get('UNIT_DEFAULT_EAID'); } } return $edit_access_id; } /** - * return a set entry from the UNIT session for an edit access_id + * return a set entry from the UNIT session for an edit access cuid * if not found return false * - * @param int $edit_access_id edit access id - * @param string|int $data_key key value to search for - * @return bool|string false for not found or string for found data + * @param string $cuid edit access cuid + * @param string|int $data_key key value to search for + * @return false|string false for not found or string for found data */ public function loginGetEditAccessData( - int $edit_access_id, + string $cuid, string|int $data_key - ): bool|string { - if (!isset($_SESSION['UNIT'][$edit_access_id]['data'][$data_key])) { + ): false|string { + if (!isset($_SESSION['UNIT'][$cuid]['data'][$data_key])) { return false; } - return $_SESSION['UNIT'][$edit_access_id]['data'][$data_key]; - } - - /** - * old name for loginGetEditAccessData - * - * @deprecated Use $login->loginGetEditAccessData() - * @param int $edit_access_id - * @param string|int $data_key - * @return bool|string - */ - public function loginSetEditAccessData( - int $edit_access_id, - string|int $data_key - ): bool|string { - return $this->loginGetEditAccessData($edit_access_id, $data_key); + return $_SESSION['UNIT'][$cuid]['data'][$data_key]; } /** @@ -2815,9 +2855,10 @@ HTML; * false on not found * * @param string $uid Edit Access UID to look for - * @return int|bool Either primary key in int or false in bool for not found + * @return int|false Either primary key in int or false in bool for not found + * @deprecated use loginGetEditAccessCuidFromUid */ - public function loginGetEditAccessIdFromUid(string $uid): int|bool + public function loginGetEditAccessIdFromUid(string $uid): int|false { if (!isset($_SESSION['UNIT_UID'][$uid])) { return false; @@ -2825,6 +2866,20 @@ HTML; return (int)$_SESSION['UNIT_UID'][$uid]; } + /** + * Get the edit access UID from the edit access CUID + * + * @param string $uid + * @return int|false + */ + public function loginGetEditAccessCuidFromUid(string $uid): int|false + { + if (!isset($_SESSION['UNIT_CUID'][$uid])) { + return false; + } + return (int)$_SESSION['UNIT_CUID'][$uid]; + } + /** * Check if admin flag is set * From fe50a988a0f4f9f659b4efd50e381c491e6c3fec Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 6 Dec 2024 20:11:28 +0900 Subject: [PATCH 05/28] Switch session ACL Login user load check to cuuid Update tests too for using edit user cuuid instead of the primary key --- 4dev/tests/ACL/CoreLibsACLLoginTest.php | 29 ++++-- www/admin/class_test.login.php | 4 +- www/lib/CoreLibs/ACL/Login.php | 114 +++++++++++++----------- 3 files changed, 86 insertions(+), 61 deletions(-) diff --git a/4dev/tests/ACL/CoreLibsACLLoginTest.php b/4dev/tests/ACL/CoreLibsACLLoginTest.php index a60f6f62..17f78545 100644 --- a/4dev/tests/ACL/CoreLibsACLLoginTest.php +++ b/4dev/tests/ACL/CoreLibsACLLoginTest.php @@ -265,7 +265,7 @@ final class CoreLibsACLLoginTest extends TestCase 'ajax_post_action' => 'login', ], ], - 'load, session euid set only, php error' => [ + 'load, session ecuuid set only, php error' => [ [ 'page_name' => 'edit_users.php', ], @@ -273,8 +273,8 @@ final class CoreLibsACLLoginTest extends TestCase [], [ 'EUID' => 1, - 'ECUID' => 'abc', - 'ECUUID' => '1233456-1234-1234-1234-123456789012', + 'EUCUID' => 'abc', + 'EUCUUID' => '1233456-1234-1234-1234-123456789012', ], 2, [], @@ -293,8 +293,8 @@ final class CoreLibsACLLoginTest extends TestCase [], [ 'EUID' => 1, - 'ECUID' => 'abc', - 'ECUUID' => '1233456-1234-1234-1234-123456789012', + 'EUCUID' => 'abc', + 'EUCUUID' => 'SET_EUCUUID_IN_TEST', 'USER_NAME' => '', 'GROUP_NAME' => '', 'ADMIN' => 1, @@ -1176,6 +1176,11 @@ final class CoreLibsACLLoginTest extends TestCase $_POST[$post_var] = $post_value; } + // set ingoing session cuuid if requested + if (isset($session['EUCUUID']) && $session['EUCUUID'] == 'SET_EUCUUID_IN_TEST') { + $session['EUCUUID'] = self::$edit_user_cuuid; + } + // set _SESSION data foreach ($session as $session_var => $session_value) { $_SESSION[$session_var] = $session_value; @@ -1435,8 +1440,18 @@ final class CoreLibsACLLoginTest extends TestCase // run test try { // preset, we cannot set that in the provider - $expected['check_access_cuid'] = self::$edit_access_cuid; - $mock_settings['edit_access_cuid'] = self::$edit_access_cuid; + 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 // check if parameter, or globals (old type) // else normal call diff --git a/www/admin/class_test.login.php b/www/admin/class_test.login.php index bdf414b5..e5bbc6d4 100644 --- a/www/admin/class_test.login.php +++ b/www/admin/class_test.login.php @@ -99,8 +99,8 @@ echo "ACL: " . Support::printAr($login->loginGetAcl()) . "
"; echo "ACL (MIN): " . Support::printAr($login->loginGetAcl()['min'] ?? []) . "
"; echo "LOCALE: " . Support::printAr($login->loginGetLocale()) . "
"; -echo "ECUID: " . $login->loginGetEcuid() . "
"; -echo "ECUUID: " . $login->loginGetEcuuid() . "
"; +echo "ECUID: " . $login->loginGetEuCuid() . "
"; +echo "ECUUID: " . $login->loginGetEuCuuid() . "
"; echo "
"; // set + check edit access id diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index a39dbaa8..1421f04c 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -75,18 +75,18 @@ use CoreLibs\Convert\Json; class Login { /** @var ?int the user id var*/ - private ?int $euid; + private ?int $edit_user_id; /** @var ?string the user cuid (note will be super seeded with uuid v4 later) */ - private ?string $ecuid; + private ?string $edit_user_cuid; /** @var ?string UUIDv4, will superseed the ecuid and replace euid as login id */ - private ?string $ecuuid; + private ?string $edit_user_cuuid; /** @var string _GET/_POST loginUserId parameter for non password login */ private string $login_user_id = ''; /** @var string source, either _GET or _POST or empty */ private string $login_user_id_source = ''; /** @var bool set to true if illegal characters where found in the login user id string */ private bool $login_user_id_unclear = false; - // is set to one if login okay, or EUID is set and user is okay to access this page + // is set to one if login okay, or EUCUUID is set and user is okay to access this page /** @var bool */ private bool $permission_okay = false; /** @var string pressed login */ @@ -262,7 +262,7 @@ class Login ], // actually obsolete '100' => [ - 'msg' => '[EUID] came in as GET/POST!', + 'msg' => '[EUCUUID] came in as GET/POST!', 'flag' => 'e', ], // query errors @@ -769,7 +769,7 @@ class Login private function loginLoginUser(): void { // if pressed login at least and is not yet loggined in - if ($this->euid || (!$this->login && !$this->login_user_id)) { + if ($this->edit_user_cuuid || (!$this->login && !$this->login_user_id)) { return; } // if not username AND password where given @@ -939,13 +939,13 @@ class Login } // normal user processing // set class var and session var - $this->euid = (int)$res['edit_user_id']; - $this->ecuid = (string)$res['cuid']; - $this->ecuuid = (string)$res['cuuid']; + $this->edit_user_id = (int)$res['edit_user_id']; + $this->edit_user_cuid = (string)$res['cuid']; + $this->edit_user_cuuid = (string)$res['cuuid']; $this->session->setMany([ - 'EUID' => $this->euid, - 'ECUID' => $this->ecuid, - 'ECUUID' => $this->ecuuid, + 'EUID' => $this->edit_user_id, // DEPRECATED + 'EUCUID' => $this->edit_user_cuid, + 'EUCUUID' => $this->edit_user_cuuid, ]); // check if user is okay $this->loginCheckPermissions(); @@ -962,7 +962,7 @@ class Login SET login_user_id_last_revalidate = NOW() WHERE edit_user_id = $1 SQL; - $this->db->dbExecParams($q, [$this->euid]); + $this->db->dbExecParams($q, [$this->edit_user_id]); } $locale = $res['locale'] ?? 'en'; $encoding = $res['encoding'] ?? 'UTF-8'; @@ -1014,7 +1014,7 @@ class Login login_error_date_first = NULL WHERE edit_user_id = $1 SQL; - $this->db->dbExecParams($q, [$this->euid]); + $this->db->dbExecParams($q, [$this->edit_user_id]); } $edit_page_ids = []; $pages = []; @@ -1022,7 +1022,7 @@ class Login // set pages access $q = << $res['edit_page_id'], 'cuid' => $res['cuid'], + 'cuuid' => $res['cuuid'], // for reference of content data on a differen page 'content_alias_uid' => $res['content_alias_uid'], 'hostname' => $res['hostname'], @@ -1096,7 +1097,7 @@ class Login // get the page content and add them to the page $q = << $res['name'], 'uid' => $res['uid'], + 'cuid' => $res['cuid'], + 'cuuid' => $res['cuuid'], 'online' => $res['online'], 'order' => $res['order_number'], // access name and level @@ -1123,7 +1126,7 @@ class Login // load the edit_access user rights $q = <<db->dbReturnParams($q, [$this->euid]))) { + while (is_array($res = $this->db->dbReturnParams($q, [$this->edit_user_id]))) { // read edit access data fields and drop them into the unit access array $q_sub = << (int)$res['edit_access_id'], // DEPRECATED + 'cuuid' => $res['cuuid'], 'acl_level' => $res['level'], 'acl_type' => $res['type'], 'name' => $res['name'], @@ -1262,8 +1266,8 @@ class Login $this->acl['user_name'] = $_SESSION['USER_NAME']; $this->acl['group_name'] = $_SESSION['GROUP_NAME']; // edit user cuid - $this->acl['ecuid'] = $_SESSION['ECUID']; - $this->acl['ecuuid'] = $_SESSION['ECUUID']; + $this->acl['eucuid'] = $_SESSION['EUCUID']; + $this->acl['eucuuid'] = $_SESSION['EUCUUID']; // set additional acl $this->acl['additional_acl'] = [ 'user' => $_SESSION['USER_ADDITIONAL_ACL'], @@ -1701,15 +1705,15 @@ class Login $event = 'No Permission'; } // prepare for log - if ($this->euid) { + if ($this->edit_user_cuuid) { // get user from user table $q = <<db->dbReturnRowParams($q, [$this->euid]))) { + if (is_array($res = $this->db->dbReturnRowParams($q, [$this->edit_user_cuuid]))) { $username = $res['username']; } } // if euid is set, get username (or try) @@ -2023,10 +2027,10 @@ HTML; empty($username) ? $this->session->get('USER_NAME') ?? '' : $username, is_numeric($this->session->get('EUID')) ? $this->session->get('EUID') : null, - is_string($this->session->get('ECUID')) ? - $this->session->get('ECUID') : null, - !empty($this->session->get('ECUUID')) && Uids::validateUuuidv4($this->session->get('ECUUID')) ? - $this->session->get('ECUUID') : null, + is_string($this->session->get('EUCUID')) ? + $this->session->get('EUCUID') : null, + !empty($this->session->get('EUCUUID')) && Uids::validateUuuidv4($this->session->get('EUCUUID')) ? + $this->session->get('EUCUUID') : null, (string)$event, (string)$error, $data_write, @@ -2153,10 +2157,8 @@ HTML; } } // if there is none, there is none, saves me POST/GET check - $this->euid = (int)($this->session->get('EUID') ?? 0); - // TODO: allow load from cuid - // $this->ecuid = (string)($this->session->get('ECUID') ?? ''); - // $this->ecuuid = (string)($this->session->get('ECUUID') ?? ''); + // $this->euid = (int)($this->session->get('EUID') ?? 0); + $this->edit_user_cuuid = (string)($this->session->get('EUCUUID') ?? ''); // get login vars, are so, can't be changed // prepare // pass on vars to Object vars @@ -2337,7 +2339,7 @@ HTML; return $this->session->get('PAGES'); } - // MARK: logged in uid(pk)/cuid/eccuid + // MARK: logged in uid(pk)/cuid/ecuuid /** * Get the current set EUID (edit user id) @@ -2346,27 +2348,33 @@ HTML; */ public function loginGetEuid(): string { - return (string)$this->euid; + return (string)$this->edit_user_id; } /** - * Get the current set ECUID (edit user cuid) + * Get the current set EUCUID (edit user cuid) * - * @return string ECUID as string + * @return string EUCUID as string */ - public function loginGetEcuid(): string + public function loginGetEuCuid(): string { - return (string)$this->ecuid; + return (string)$this->edit_user_cuid; } /** - * Get the current set ECUUID (edit user cuuid) + * Get the current set EUCUUID (edit user cuuid) * - * @return string ECUUID as string + * @return string EUCUUID as string + * @deprecated Wrong name, use ->loginGetEuCuuid */ public function loginGetEcuuid(): string { - return (string)$this->ecuuid; + return (string)$this->edit_user_cuuid; + } + + public function loginGetEuCuuid(): string + { + return (string)$this->edit_user_cuuid; } // MARK: get error messages @@ -2515,9 +2523,9 @@ HTML; // unset session vars set/used in this login $this->session->sessionDestroy(); // unset euid - $this->euid = null; - $this->ecuid = null; - $this->ecuuid = null; + $this->edit_user_id = null; + $this->edit_user_cuid = null; + $this->edit_user_cuuid = null; // then prints the login screen again $this->permission_okay = false; } @@ -2534,7 +2542,7 @@ HTML; // start with not allowed $this->permission_okay = false; // bail for no euid (no login) - if (empty($this->euid)) { + if (empty($this->edit_user_cuuid)) { return $this->permission_okay; } // euid must match ecuid and ecuuid @@ -2544,7 +2552,7 @@ HTML; } $q = <<db->dbReturnRowParams($q, [$this->euid, $this->page_name]); + $res = $this->db->dbReturnRowParams($q, [$this->edit_user_cuuid, $this->page_name]); if (!is_array($res)) { $this->login_error = 109; return $this->permission_okay; @@ -2622,12 +2630,14 @@ HTML; } else { $this->login_error = 103; } - // set ECUID - $this->ecuid = (string)$res['cuid']; - $this->ecuuid = (string)$res['cuuid']; + // set all the internal vars + $this->edit_user_id = (int)$res['edit_user_id']; + $this->edit_user_cuid = (string)$res['cuid']; + $this->edit_user_cuuid = (string)$res['cuuid']; $this->session->setMany([ - 'ECUID' => $this->ecuid, - 'ECUUID' => $this->ecuuid, + 'EUID' => $this->edit_user_id, // DEPRECATED + 'EUCUID' => $this->edit_user_cuid, + 'EUCUUID' => $this->edit_user_cuuid, ]); // if called from public, so we can check if the permissions are ok return $this->permission_okay; From a56cbd8e97e05e61ece68bad8540056600b4e172 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 9 Dec 2024 16:20:21 +0900 Subject: [PATCH 06/28] ACL Login update layout to work with PC/Smartphone Updated login template and password change template block --- www/lib/CoreLibs/ACL/Login.php | 342 ++++++++++++++++++++++++--------- 1 file changed, 249 insertions(+), 93 deletions(-) diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 1421f04c..be730a9d 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -180,6 +180,7 @@ class Login private array $login_template = [ 'strings' => [], 'password_change' => '', + 'password_forgot' => '', 'template' => '' ]; @@ -232,13 +233,12 @@ class Login * @param \CoreLibs\Logging\Logging $log Logging class * @param \CoreLibs\Create\Session $session Session interface class * @param array $options Login ACL settings - * $auto_login [default true] DEPRECATED, moved into options */ public function __construct( \CoreLibs\DB\IO $db, \CoreLibs\Logging\Logging $log, \CoreLibs\Create\Session $session, - array $options = [] + array $options = [], ) { // attach db class $this->db = $db; @@ -1601,6 +1601,10 @@ class Login $this->login_template['strings']['LANGUAGE'] = $locales['lang'] ?? 'en'; // if password change is okay + // TODO: this should be a "forgot" password + // -> input email + // -> send to user with link + token + // -> validate token -> show change password if ($this->password_change) { $html_string_password_change = $this->login_template['password_change']; @@ -1617,14 +1621,14 @@ class Login // print error messagae if ($this->login_error) { $html_string_password_change = str_replace( - '{ERROR_MSG}', - $this->loginGetErrorMsg($this->login_error) . '
', + ['{ERROR_VISIBLE}', '{ERROR_MSG}'], + ['login-visible', $this->loginGetErrorMsg($this->login_error)], $html_string_password_change ); } else { $html_string_password_change = str_replace( - '{ERROR_MSG}', - '
', + ['{ERROR_VISIBLE}', '{ERROR_MSG}'], + ['login-hidden', ''], $html_string_password_change ); } @@ -1632,8 +1636,11 @@ class Login if ($this->change_password && !$this->password_change_ok) { $html_string_password_change = str_replace( '{PASSWORD_CHANGE_SHOW}', - '', + << + ShowHideDiv('login_pw_change_div'); + + HTML, $html_string_password_change ); } else { @@ -1660,18 +1667,22 @@ class Login // print error messagae if ($this->login_error) { $html_string = str_replace( - '{ERROR_MSG}', - $this->loginGetErrorMsg($this->login_error) . '
', + ['{ERROR_VISIBLE}', '{ERROR_MSG}'], + ['login-visible', $this->loginGetErrorMsg($this->login_error)], $html_string ); } elseif ($this->password_change_ok && $this->password_change) { $html_string = str_replace( - '{ERROR_MSG}', - $this->loginGetErrorMsg(300) . '
', + ['{ERROR_VISIBLE}', '{ERROR_MSG}'], + ['login-visible', $this->loginGetErrorMsg(300)], $html_string ); } else { - $html_string = str_replace('{ERROR_MSG}', '
', $html_string); + $html_string = str_replace( + ['{ERROR_VISIBLE}', '{ERROR_MSG}'], + ['login-hidden', ''], + $html_string + ); } // create the replace array context @@ -1756,39 +1767,93 @@ class Login 'NEW_PASSWORD' => $this->l->__('New Password'), 'NEW_PASSWORD_CONFIRM' => $this->l->__('New Password confirm'), 'CLOSE' => $this->l->__('Close'), - 'JS_SHOW_HIDE' => "function ShowHideDiv(id) { " - . "element = document.getElementById(id); " - . "if (element.className == 'visible' || !element.className) element.className = 'hidden'; " - . "else element.className = 'visible'; }", - 'PASSWORD_CHANGE_BUTTON' => '' + 'JS_SHOW_HIDE' => << str_replace( + '{PASSWORD_CHANGE_BUTTON_VALUE}', + $strings['PASSWORD_CHANGE_BUTTON_VALUE'], + // phpcs:disable Generic.Files.LineLength + << + HTML + // phpcs:enable Generic.Files.LineLength + ), ]); - // TODO: submit or JS to set target page as ajax call - // NOTE: for the HTML block I ignore line lengths - // phpcs:disable + // phpcs:disable Generic.Files.LineLength $this->login_template['password_change'] = << - - - - - - - - - - - - -

{TITLE_PASSWORD_CHANGE}

{ERROR_MSG}
{USERNAME}
{OLD_PASSWORD}
{NEW_PASSWORD}
{NEW_PASSWORD_CONFIRM}
-
+ {PASSWORD_CHANGE_SHOW} HTML; - // phpcs:enable + // phpcs:enable Generic.Files.LineLength } if ($this->password_forgot) { + // TODO: create a password forget request flow } if (!$this->password_change && !$this->password_forgot) { $strings = array_merge($strings, [ @@ -1807,71 +1872,162 @@ HTML; } // now check templates - // TODO: submit or JS to set target page as ajax call if (!$this->login_template['template']) { + // phpcs:disable Generic.Files.LineLength $this->login_template['template'] = << -{HTML_TITLE} - - + {LOGOUT_TARGET} -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -
-

{TITLE}

-
 
- {ERROR_MSG} -
{USERNAME}
{PASSWORD}
- - {PASSWORD_CHANGE_BUTTON} -
-

-
 
-{PASSWORD_CHANGE_DIV} +
HTML; + // phpcs:enable Generic.Files.LineLength } } @@ -2167,11 +2323,11 @@ HTML; $this->password = $_POST['login_password'] ?? ''; $this->logout = $_POST['login_logout'] ?? ''; // password change vars - $this->change_password = $_POST['change_password'] ?? ''; - $this->pw_username = $_POST['pw_username'] ?? ''; - $this->pw_old_password = $_POST['pw_old_password'] ?? ''; - $this->pw_new_password = $_POST['pw_new_password'] ?? ''; - $this->pw_new_password_confirm = $_POST['pw_new_password_confirm'] ?? ''; + $this->change_password = $_POST['login_change_password'] ?? ''; + $this->pw_username = $_POST['login_pw_username'] ?? ''; + $this->pw_old_password = $_POST['login_pw_old_password'] ?? ''; + $this->pw_new_password = $_POST['login_pw_new_password'] ?? ''; + $this->pw_new_password_confirm = $_POST['login_pw_new_password_confirm'] ?? ''; // disallow user list for password change $this->pw_change_deny_users = ['admin']; // max login counts before error reporting From 65715ea9c37262e364f06f26ee67b47add5df0f6 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 9 Dec 2024 19:01:35 +0900 Subject: [PATCH 07/28] Add Array function to return only array entries based on matching key A simple key based array filter --- .../CoreLibsCombinedArrayHandlerTest.php | 85 +++++++++++++++++++ www/admin/class_test.array.php | 13 +++ www/lib/CoreLibs/Combined/ArrayHandler.php | 24 ++++++ 3 files changed, 122 insertions(+) diff --git a/4dev/tests/Combined/CoreLibsCombinedArrayHandlerTest.php b/4dev/tests/Combined/CoreLibsCombinedArrayHandlerTest.php index 8dc5729a..d320b845 100644 --- a/4dev/tests/Combined/CoreLibsCombinedArrayHandlerTest.php +++ b/4dev/tests/Combined/CoreLibsCombinedArrayHandlerTest.php @@ -1201,6 +1201,91 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase '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__ diff --git a/www/admin/class_test.array.php b/www/admin/class_test.array.php index 85dbfc0a..bb64ea46 100644 --- a/www/admin/class_test.array.php +++ b/www/admin/class_test.array.php @@ -250,6 +250,19 @@ foreach (array_keys($array) as $search) { } print "Key not exists: " . DgS::printAr(ArrayHandler::arrayGetNextKey($array, 'z')) . "
"; +print "
"; +$keys = ['b', 'c', 'f']; +print "Return only: " . DgS::printAr($keys) . ": " + . DgS::printAr(ArrayHandler::arrayReturnMatchingKeyOnly($array, $keys)) . "
"; + +$out = array_filter($array, fn($key) => in_array($key, $keys), ARRAY_FILTER_USE_KEY); +print "array filter: " . DgS::printAr($keys) . ": " . DgS::printAr($out) . "
"; +$out = array_intersect_key( + $array, + array_flip($keys) +); +print "array intersect key: " . DgS::printAr($keys) . ": " . DgS::printAr($out) . "
"; + print ""; // __END__ diff --git a/www/lib/CoreLibs/Combined/ArrayHandler.php b/www/lib/CoreLibs/Combined/ArrayHandler.php index b1002949..38727c1f 100644 --- a/www/lib/CoreLibs/Combined/ArrayHandler.php +++ b/www/lib/CoreLibs/Combined/ArrayHandler.php @@ -525,6 +525,30 @@ class ArrayHandler { return array_diff($array, $remove); } + + /** + * From the array with key -> anything values return only the matching entries from key list + * key list is a list[string] + * if key list is empty, return array as is + * + * @param array $array + * @param array $key_list + * @return array + */ + 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__ From eeca13819215cee19fdd8994a50a0b151974ba26 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 9 Dec 2024 19:20:57 +0900 Subject: [PATCH 08/28] Remove the debug/db_debug flag from the edit user edit interface These settings are deprecated and do nothing. keep the DB default values for now, but update table create to remove them. They are set to 0 --- 4dev/database/table/edit_user.sql | 5 ---- www/lib/CoreLibs/Admin/EditBase.php | 2 -- .../Output/Form/TableArrays/EditUsers.php | 24 ------------------- 3 files changed, 31 deletions(-) diff --git a/4dev/database/table/edit_user.sql b/4dev/database/table/edit_user.sql index abae29c6..2b3c58d7 100644 --- a/4dev/database/table/edit_user.sql +++ b/4dev/database/table/edit_user.sql @@ -35,9 +35,6 @@ CREATE TABLE edit_user ( strict SMALLINT DEFAULT 0, locked SMALLINT 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 admin SMALLINT NOT NULL DEFAULT 0, -- last login log @@ -76,8 +73,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.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.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.last_login IS 'Last succesfull login tiemstamp'; COMMENT ON COLUMN edit_user.login_error_count IS 'Number of failed logins, reset on successful login'; diff --git a/www/lib/CoreLibs/Admin/EditBase.php b/www/lib/CoreLibs/Admin/EditBase.php index f740b53a..27d4a162 100644 --- a/www/lib/CoreLibs/Admin/EditBase.php +++ b/www/lib/CoreLibs/Admin/EditBase.php @@ -415,8 +415,6 @@ class EditBase $elements[] = $this->form->formCreateElement('lock_until'); $elements[] = $this->form->formCreateElement('lock_after'); $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_scheme_id'); $elements[] = $this->form->formCreateElementListTable('edit_access_user'); diff --git a/www/lib/CoreLibs/Output/Form/TableArrays/EditUsers.php b/www/lib/CoreLibs/Output/Form/TableArrays/EditUsers.php index 6d87753a..c51dbd77 100644 --- a/www/lib/CoreLibs/Output/Form/TableArrays/EditUsers.php +++ b/www/lib/CoreLibs/Output/Form/TableArrays/EditUsers.php @@ -135,30 +135,6 @@ class EditUsers implements Interface\TableArraysInterface 'min_edit_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' => [ 'value' => $_POST['email'] ?? '', 'output_name' => 'E-Mail', From 10c320f60c055fb1160147ce0da331f39c92c02c Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 9 Dec 2024 19:33:53 +0900 Subject: [PATCH 09/28] Rename all ACL Login session vars to LOGIN_, remove debug enties All ACL\Login loaded _SESSION vars are now prefixd with LOGIN_ only the language one stay as "DEFAULT_" Removed DEBUG_ALL/DB_DEBUG as they are now fully removed from everywhere - removed the edit user entries - removed from the edit user table The LANG direct loaded language entries is removed too. We only use locale and encoding. No more LOCALE_PATH and DEFAULT_DOMAIN _SESSION are set during the option set --- 4dev/tests/ACL/CoreLibsACLLoginTest.php | 55 +-- .../CoreLibsLanguageGetLocaleTest.php | 335 ------------------ www/admin/class_test.php | 4 +- www/lib/CoreLibs/ACL/Login.php | 191 +++++----- 4 files changed, 128 insertions(+), 457 deletions(-) diff --git a/4dev/tests/ACL/CoreLibsACLLoginTest.php b/4dev/tests/ACL/CoreLibsACLLoginTest.php index 17f78545..de57645f 100644 --- a/4dev/tests/ACL/CoreLibsACLLoginTest.php +++ b/4dev/tests/ACL/CoreLibsACLLoginTest.php @@ -265,21 +265,21 @@ final class CoreLibsACLLoginTest extends TestCase 'ajax_post_action' => 'login', ], ], - 'load, session ecuuid set only, php error' => [ + 'load, session eucuuid set only, php error' => [ [ 'page_name' => 'edit_users.php', ], [], [], [ - 'EUID' => 1, - 'EUCUID' => 'abc', - 'EUCUUID' => '1233456-1234-1234-1234-123456789012', + 'LOGIN_EUID' => 1, + 'LOGIN_EUCUID' => 'abc', + 'LOGIN_EUCUUID' => '1233456-1234-1234-1234-123456789012', ], 2, [], ], - 'load, session euid set, all set' => [ + 'load, session eucuuid set, all set' => [ [ 'page_name' => 'edit_users.php', 'edit_access_id' => 1, @@ -292,21 +292,21 @@ final class CoreLibsACLLoginTest extends TestCase [], [], [ - 'EUID' => 1, - 'EUCUID' => 'abc', - 'EUCUUID' => 'SET_EUCUUID_IN_TEST', - 'USER_NAME' => '', - 'GROUP_NAME' => '', - 'ADMIN' => 1, - 'GROUP_ACL_LEVEL' => -1, - 'PAGES_ACL_LEVEL' => [], - 'USER_ACL_LEVEL' => -1, - 'USER_ADDITIONAL_ACL' => [], - 'GROUP_ADDITIONAL_ACL' => [], - 'UNIT_UID' => [ + 'LOGIN_EUID' => 1, + 'LOGIN_EUCUID' => 'abc', + 'LOGIN_EUCUUID' => 'SET_EUCUUID_IN_TEST', + 'LOGIN_USER_NAME' => '', + 'LOGIN_GROUP_NAME' => '', + 'LOGIN_ADMIN' => 1, + 'LOGIN_GROUP_ACL_LEVEL' => -1, + 'LOGIN_PAGES_ACL_LEVEL' => [], + 'LOGIN_USER_ACL_LEVEL' => -1, + 'LOGIN_USER_ADDITIONAL_ACL' => [], + 'LOGIN_GROUP_ADDITIONAL_ACL' => [], + 'LOGIN_UNIT_UID' => [ 'AdminAccess' => '123456789012', ], - 'UNIT' => [ + 'LOGIN_UNIT' => [ '123456789012' => [ 'id' => 1, 'acl_level' => 80, @@ -320,8 +320,8 @@ final class CoreLibsACLLoginTest extends TestCase 'additional_acl' => [] ], ], - // 'UNIT_DEFAULT' => '', - // 'DEFAULT_ACL_LIST' => [], + // 'LOGIN_UNIT_DEFAULT' => '', + // 'LOGIN_DEFAULT_ACL_LIST' => [], ], 0, [ @@ -1177,8 +1177,8 @@ final class CoreLibsACLLoginTest extends TestCase } // set ingoing session cuuid if requested - if (isset($session['EUCUUID']) && $session['EUCUUID'] == 'SET_EUCUUID_IN_TEST') { - $session['EUCUUID'] = self::$edit_user_cuuid; + if (isset($session['LOGIN_EUCUUID']) && $session['LOGIN_EUCUUID'] == 'SET_EUCUUID_IN_TEST') { + $session['LOGIN_EUCUUID'] = self::$edit_user_cuuid; } // set _SESSION data @@ -1582,11 +1582,12 @@ final class CoreLibsACLLoginTest extends TestCase // - loginCheckPermissions // - loginGetPermissionOkay } catch (\Exception $e) { - // print "[E]: " . $e->getCode() . ", ERROR: " . $login_mock->loginGetLastErrorCode() . "/" - // . ($expected['login_error'] ?? 0) . "\n"; - // print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n"; - // print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n"; - // print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n"; + /* print "[E]: " . $e->getCode() . ", ERROR: " . $login_mock->loginGetLastErrorCode() . "/" + . ($expected['login_error'] ?? 0) . "\n"; + print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n"; + print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\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 ( $e->getCode() == 100 || diff --git a/4dev/tests/Language/CoreLibsLanguageGetLocaleTest.php b/4dev/tests/Language/CoreLibsLanguageGetLocaleTest.php index fa053413..afd71a4d 100644 --- a/4dev/tests/Language/CoreLibsLanguageGetLocaleTest.php +++ b/4dev/tests/Language/CoreLibsLanguageGetLocaleTest.php @@ -21,341 +21,6 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase . 'includes' . 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 - */ - /* 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 $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 * diff --git a/www/admin/class_test.php b/www/admin/class_test.php index 7bff5bb9..138496ef 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -151,8 +151,8 @@ foreach ($test_files as $file => $name) { print "
"; -print "ECUID: " . $session->get('ECUID') . "
"; -print "ECUUID: " . $session->get('ECUUID') . "
"; +print "ECUID: " . $session->get('LOGIN_EUCUID') . "
"; +print "ECUUID: " . $session->get('LOGIN_EUCUUID') . "
"; print "
"; print "LOCALE: " . Support::dumpVar($locale) . "
"; diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index be730a9d..db6fb705 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -14,13 +14,14 @@ * will be a class one day * * descrption of session_vars -* DEBUG_ALL - set to one, prints out error_msg var at end of php execution -* DB_DEBUG - prints out database debugs (query, etc) -* GROUP_LEVEL - the level he can access (numeric) -* USER_NAME - login name from user -* LANG - lang to show edit interface (not yet used) +* TODO: Update session var info +* [DEPRECATED] DEBUG_ALL - set to one, prints out error_msg var at end of php execution +* [DEPRECATED] DB_DEBUG - prints out database debugs (query, etc) +* [REMOVED] LOGIN_GROUP_LEVEL - the level he can access (numeric) +* LOGIN_USER_NAME - login name from user +* [DEPRECATED] LANG - lang to show edit interface (not yet used) * DEFAULT_CHARSET - in connection with LANG (not yet used) -* PAGES - array of hashes +* LOGIN_PAGES - array of hashes * edit_page_id - ID from the edit_pages table * filename - name of the file * page_name - name in menu @@ -262,7 +263,7 @@ class Login ], // actually obsolete '100' => [ - 'msg' => '[EUCUUID] came in as GET/POST!', + 'msg' => '[EUCUUID] set from GET/POST!', 'flag' => 'e', ], // query errors @@ -393,8 +394,8 @@ class Login } // write that into the session $this->session->setMany([ - 'DEFAULT_ACL_LIST' => $this->default_acl_list, - 'DEFAULT_ACL_LIST_TYPE' => $this->default_acl_list_type, + 'LOGIN_DEFAULT_ACL_LIST' => $this->default_acl_list, + 'LOGIN_DEFAULT_ACL_LIST_TYPE' => $this->default_acl_list_type, ]); $this->loginSetEditLogWriteTypeAvailable(); @@ -587,7 +588,6 @@ class Login // set path $options['locale_path'] = BASE . INCLUDES . LOCALE; } - $this->session->set('LOCALE_PATH', $options['locale_path']); // LANG: LOCALE if (empty($options['site_locale'])) { trigger_error( @@ -622,7 +622,6 @@ class Login $options['set_domain'] = str_replace(DIRECTORY_SEPARATOR, '', CONTENT_PATH); } } - $this->session->set('DEFAULT_DOMAIN', $options['site_domain']); // LANG: ENCODING if (empty($options['site_encoding'])) { trigger_error( @@ -943,9 +942,9 @@ class Login $this->edit_user_cuid = (string)$res['cuid']; $this->edit_user_cuuid = (string)$res['cuuid']; $this->session->setMany([ - 'EUID' => $this->edit_user_id, // DEPRECATED - 'EUCUID' => $this->edit_user_cuid, - 'EUCUUID' => $this->edit_user_cuuid, + 'LOGIN_EUID' => $this->edit_user_id, // DEPRECATED + 'LOGIN_EUCUID' => $this->edit_user_cuid, + 'LOGIN_EUCUUID' => $this->edit_user_cuuid, ]); // check if user is okay $this->loginCheckPermissions(); @@ -968,35 +967,36 @@ class Login $encoding = $res['encoding'] ?? 'UTF-8'; $this->session->setMany([ // now set all session vars and read page permissions - 'DEBUG_ALL' => $this->db->dbBoolean($res['debug']), - 'DB_DEBUG' => $this->db->dbBoolean($res['db_debug']), + // DEBUG flag is deprecated + // 'DEBUG_ALL' => $this->db->dbBoolean($res['debug']), + // 'DB_DEBUG' => $this->db->dbBoolean($res['db_debug']), // general info for user logged in - 'USER_NAME' => $res['username'], - 'ADMIN' => $res['admin'], - 'GROUP_NAME' => $res['edit_group_name'], - 'USER_ACL_LEVEL' => $res['user_level'], - 'USER_ACL_TYPE' => $res['user_type'], - 'USER_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['user_additional_acl']), - 'GROUP_ACL_LEVEL' => $res['group_level'], - 'GROUP_ACL_TYPE' => $res['group_type'], - 'GROUP_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['group_additional_acl']), + 'LOGIN_USER_NAME' => $res['username'], + 'LOGIN_ADMIN' => $res['admin'], + 'LOGIN_GROUP_NAME' => $res['edit_group_name'], + 'LOGIN_USER_ACL_LEVEL' => $res['user_level'], + 'LOGIN_USER_ACL_TYPE' => $res['user_type'], + 'LOGIN_USER_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['user_additional_acl']), + 'LOGIN_GROUP_ACL_LEVEL' => $res['group_level'], + 'LOGIN_GROUP_ACL_TYPE' => $res['group_type'], + 'LOGIN_GROUP_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['group_additional_acl']), // deprecated TEMPLATE setting - 'TEMPLATE' => $res['template'] ? $res['template'] : '', - 'HEADER_COLOR' => !empty($res['second_header_color']) ? + // 'TEMPLATE' => $res['template'] ? $res['template'] : '', + 'LOGIN_HEADER_COLOR' => !empty($res['second_header_color']) ? $res['second_header_color'] : $res['first_header_color'], // LANGUAGE/LOCALE/ENCODING: - 'LANG' => $locale, + // 'LOGIN_LANG' => $locale, 'DEFAULT_CHARSET' => $encoding, 'DEFAULT_LOCALE' => $locale . '.' . strtoupper($encoding), 'DEFAULT_LANG' => $locale . '_' . strtolower(str_replace('-', '', $encoding)) ]); // missing # before, this is for legacy data, will be deprecated if ( - !empty($this->session->get('HEADER_COLOR')) && - preg_match("/^[\dA-Fa-f]{6,8}$/", $this->session->get('HEADER_COLOR')) + !empty($this->session->get('LOGIN_HEADER_COLOR')) && + preg_match("/^[\dA-Fa-f]{6,8}$/", $this->session->get('LOGIN_HEADER_COLOR')) ) { - $this->session->set('HEADER_COLOR', '#' . $this->session->get('HEADER_COLOR')); + $this->session->set('LOGIN_HEADER_COLOR', '#' . $this->session->get('LOGIN_HEADER_COLOR')); } // TODO: make sure that header color is valid: // # + 6 hex @@ -1120,8 +1120,8 @@ class Login } // write back the pages data to the output array $this->session->setMany([ - 'PAGES' => $pages, - 'PAGES_ACL_LEVEL' => $pages_acl, + 'LOGIN_PAGES' => $pages, + 'LOGIN_PAGES_ACL_LEVEL' => $pages_acl, ]); // load the edit_access user rights $q = <<session->set('UNIT_DEFAULT_EAID', (int)$res['edit_access_id']); // DEPRECATED - $this->session->set('UNIT_DEFAULT_EACUID', (int)$res['cuid']); + $this->session->set('LOGIN_UNIT_DEFAULT_EAID', (int)$res['edit_access_id']); // DEPRECATED + $this->session->set('LOGIN_UNIT_DEFAULT_EACUID', (int)$res['cuid']); } $unit_uid_lookup[$res['uid']] = $res['edit_access_id']; // DEPRECATED $unit_cuid_lookup[$res['uid']] = $res['cuid']; @@ -1183,13 +1183,13 @@ class Login $unit_acl[$res['cuid']] = $res['level']; } $this->session->setMany([ - 'UNIT_UID' => $unit_uid_lookup, // DEPRECATED - 'UNIT_CUID' => $unit_cuid_lookup, - 'UNIT' => $unit_access_cuid, - 'UNIT_LEGACY' => $unit_access_eaid, // DEPRECATED - 'UNIT_ACL_LEVEL' => $unit_acl, - 'EAID' => $eaid, // DEPRECATED - 'EACUID' => $eacuid, + 'LOGIN_UNIT_UID' => $unit_uid_lookup, // DEPRECATED + 'LOGIN_UNIT_CUID' => $unit_cuid_lookup, + 'LOGIN_UNIT' => $unit_access_cuid, + 'LOGIN_UNIT_LEGACY' => $unit_access_eaid, // DEPRECATED + 'LOGIN_UNIT_ACL_LEVEL' => $unit_acl, + 'LOGIN_EAID' => $eaid, // DEPRECATED + 'LOGIN_EACUID' => $eacuid, ]); } // user has permission to THIS page } // user was not enabled or other login error @@ -1263,21 +1263,21 @@ class Login return; } // username (login), group name - $this->acl['user_name'] = $_SESSION['USER_NAME']; - $this->acl['group_name'] = $_SESSION['GROUP_NAME']; + $this->acl['user_name'] = $_SESSION['LOGIN_USER_NAME']; + $this->acl['group_name'] = $_SESSION['LOGIN_GROUP_NAME']; // edit user cuid - $this->acl['eucuid'] = $_SESSION['EUCUID']; - $this->acl['eucuuid'] = $_SESSION['EUCUUID']; + $this->acl['eucuid'] = $_SESSION['LOGIN_EUCUID']; + $this->acl['eucuuid'] = $_SESSION['LOGIN_EUCUUID']; // set additional acl $this->acl['additional_acl'] = [ - 'user' => $_SESSION['USER_ADDITIONAL_ACL'], - 'group' => $_SESSION['GROUP_ADDITIONAL_ACL'], + 'user' => $_SESSION['LOGIN_USER_ADDITIONAL_ACL'], + 'group' => $_SESSION['LOGIN_GROUP_ADDITIONAL_ACL'], ]; // we start with the default acl $this->acl['base'] = $this->default_acl_level; // set admin flag and base to 100 - if (!empty($_SESSION['ADMIN'])) { + if (!empty($_SESSION['LOGIN_ADMIN'])) { $this->acl['admin'] = 1; $this->acl['base'] = 100; } else { @@ -1285,36 +1285,36 @@ class Login // now go throw the flow and set the correct ACL // user > page > group // group ACL 0 - if ($_SESSION['GROUP_ACL_LEVEL'] != -1) { - $this->acl['base'] = (int)$_SESSION['GROUP_ACL_LEVEL']; + if ($_SESSION['LOGIN_GROUP_ACL_LEVEL'] != -1) { + $this->acl['base'] = (int)$_SESSION['LOGIN_GROUP_ACL_LEVEL']; } // page ACL 1 if ( - isset($_SESSION['PAGES_ACL_LEVEL'][$this->page_name]) && - $_SESSION['PAGES_ACL_LEVEL'][$this->page_name] != -1 + isset($_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name]) && + $_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name] != -1 ) { - $this->acl['base'] = (int)$_SESSION['PAGES_ACL_LEVEL'][$this->page_name]; + $this->acl['base'] = (int)$_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name]; } // user ACL 2 - if ($_SESSION['USER_ACL_LEVEL'] != -1) { - $this->acl['base'] = (int)$_SESSION['USER_ACL_LEVEL']; + if ($_SESSION['LOGIN_USER_ACL_LEVEL'] != -1) { + $this->acl['base'] = (int)$_SESSION['LOGIN_USER_ACL_LEVEL']; } } - $this->session->set('BASE_ACL_LEVEL', $this->acl['base']); + $this->session->set('LOGIN_BASE_ACL_LEVEL', $this->acl['base']); // set the current page acl // start with base acl // set group if not -1, overrides default // set page if not -1, overrides group set $this->acl['page'] = $this->acl['base']; - if ($_SESSION['GROUP_ACL_LEVEL'] != -1) { - $this->acl['page'] = $_SESSION['GROUP_ACL_LEVEL']; + if ($_SESSION['LOGIN_GROUP_ACL_LEVEL'] != -1) { + $this->acl['page'] = $_SESSION['LOGIN_GROUP_ACL_LEVEL']; } if ( - isset($_SESSION['PAGES_ACL_LEVEL'][$this->page_name]) && - $_SESSION['PAGES_ACL_LEVEL'][$this->page_name] != -1 + isset($_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name]) && + $_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name] != -1 ) { - $this->acl['page'] = $_SESSION['PAGES_ACL_LEVEL'][$this->page_name]; + $this->acl['page'] = $_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name]; } $this->acl['unit_id'] = null; @@ -1325,7 +1325,7 @@ class Login $this->acl['unit_detail'] = []; // PER ACCOUNT (UNIT/edit access)-> - foreach ($_SESSION['UNIT'] as $ea_cuid => $unit) { + foreach ($_SESSION['LOGIN_UNIT'] as $ea_cuid => $unit) { // if admin flag is set, all units are set to 100 if (!empty($this->acl['admin'])) { $this->acl['unit'][$ea_cuid] = $this->acl['base']; @@ -1355,7 +1355,7 @@ class Login } } // flag if to show extra edit access drop downs (because user has multiple groups assigned) - if (count($_SESSION['UNIT']) > 1) { + if (count($_SESSION['LOGIN_UNIT']) > 1) { $this->acl['show_ea_extra'] = true; } else { $this->acl['show_ea_extra'] = false; @@ -1370,7 +1370,7 @@ class Login // $this->debug('ACL', $this->print_ar($this->acl)); } - // MARK: lgin set locale + // MARK: login set locale /** * set locale @@ -2180,13 +2180,14 @@ HTML; ), [ // row 1 - empty($username) ? $this->session->get('USER_NAME') ?? '' : $username, - is_numeric($this->session->get('EUID')) ? - $this->session->get('EUID') : null, - is_string($this->session->get('EUCUID')) ? - $this->session->get('EUCUID') : null, - !empty($this->session->get('EUCUUID')) && Uids::validateUuuidv4($this->session->get('EUCUUID')) ? - $this->session->get('EUCUUID') : null, + empty($username) ? $this->session->get('LOGIN_USER_NAME') ?? '' : $username, + is_numeric($this->session->get('LOGIN_EUID')) ? + $this->session->get('LOGIN_EUID') : null, + is_string($this->session->get('LOGIN_EUCUID')) ? + $this->session->get('LOGIN_EUCUID') : null, + !empty($this->session->get('LOGIN_EUCUUID')) && + Uids::validateUuuidv4($this->session->get('LOGIN_EUCUUID')) ? + $this->session->get('LOGIN_EUCUUID') : null, (string)$event, (string)$error, $data_write, @@ -2313,8 +2314,7 @@ HTML; } } // if there is none, there is none, saves me POST/GET check - // $this->euid = (int)($this->session->get('EUID') ?? 0); - $this->edit_user_cuuid = (string)($this->session->get('EUCUUID') ?? ''); + $this->edit_user_cuuid = (string)($this->session->get('LOGIN_EUCUUID') ?? ''); // get login vars, are so, can't be changed // prepare // pass on vars to Object vars @@ -2481,7 +2481,7 @@ HTML; */ public function loginGetHeaderColor(): ?string { - return $this->session->get('HEADER_COLOR'); + return $this->session->get('LOGIN_HEADER_COLOR'); } /** @@ -2492,7 +2492,7 @@ HTML; public function loginGetPages(): array { - return $this->session->get('PAGES'); + return $this->session->get('LOGIN_PAGES'); } // MARK: logged in uid(pk)/cuid/ecuuid @@ -2528,6 +2528,11 @@ HTML; return (string)$this->edit_user_cuuid; } + /** + * Get the current set EUCUUID (edit user cuuid) + * + * @return string EUCUUID as string + */ public function loginGetEuCuuid(): string { return (string)$this->edit_user_cuuid; @@ -2791,9 +2796,9 @@ HTML; $this->edit_user_cuid = (string)$res['cuid']; $this->edit_user_cuuid = (string)$res['cuuid']; $this->session->setMany([ - 'EUID' => $this->edit_user_id, // DEPRECATED - 'EUCUID' => $this->edit_user_cuid, - 'EUCUUID' => $this->edit_user_cuuid, + 'LOGIN_EUID' => $this->edit_user_id, // DEPRECATED + 'LOGIN_EUCUID' => $this->edit_user_cuid, + 'LOGIN_EUCUUID' => $this->edit_user_cuuid, ]); // if called from public, so we can check if the permissions are ok return $this->permission_okay; @@ -2962,12 +2967,12 @@ HTML; { if ( $cuid !== null && - is_array($this->session->get('UNIT')) && - !array_key_exists($cuid, $this->session->get('UNIT')) + is_array($this->session->get('LOGIN_UNIT')) && + !array_key_exists($cuid, $this->session->get('LOGIN_UNIT')) ) { $cuid = null; - if (!empty($this->session->get('UNIT_DEFAULT_EACUID'))) { - $cuid = $this->session->get('UNIT_DEFAULT_EACUID'); + if (!empty($this->session->get('LOGIN_UNIT_DEFAULT_EACUID'))) { + $cuid = $this->session->get('LOGIN_UNIT_DEFAULT_EACUID'); } } return $cuid; @@ -2987,12 +2992,12 @@ HTML; { if ( $edit_access_id !== null && - is_array($this->session->get('UNIT_LEGACY')) && - !array_key_exists($edit_access_id, $this->session->get('UNIT_LEGACY')) + is_array($this->session->get('LOGIN_UNIT_LEGACY')) && + !array_key_exists($edit_access_id, $this->session->get('LOGIN_UNIT_LEGACY')) ) { $edit_access_id = null; - if (!empty($this->session->get('UNIT_DEFAULT_EAID'))) { - $edit_access_id = (int)$this->session->get('UNIT_DEFAULT_EAID'); + if (!empty($this->session->get('LOGIN_UNIT_DEFAULT_EAID'))) { + $edit_access_id = (int)$this->session->get('LOGIN_UNIT_DEFAULT_EAID'); } } return $edit_access_id; @@ -3010,10 +3015,10 @@ HTML; string $cuid, string|int $data_key ): false|string { - if (!isset($_SESSION['UNIT'][$cuid]['data'][$data_key])) { + if (!isset($_SESSION['LOGIN_UNIT'][$cuid]['data'][$data_key])) { return false; } - return $_SESSION['UNIT'][$cuid]['data'][$data_key]; + return $_SESSION['LOGIN_UNIT'][$cuid]['data'][$data_key]; } /** @@ -3026,10 +3031,10 @@ HTML; */ public function loginGetEditAccessIdFromUid(string $uid): int|false { - if (!isset($_SESSION['UNIT_UID'][$uid])) { + if (!isset($_SESSION['LOGIN_UNIT_UID'][$uid])) { return false; } - return (int)$_SESSION['UNIT_UID'][$uid]; + return (int)$_SESSION['LOGIN_UNIT_UID'][$uid]; } /** @@ -3040,10 +3045,10 @@ HTML; */ public function loginGetEditAccessCuidFromUid(string $uid): int|false { - if (!isset($_SESSION['UNIT_CUID'][$uid])) { + if (!isset($_SESSION['LOGIN_UNIT_CUID'][$uid])) { return false; } - return (int)$_SESSION['UNIT_CUID'][$uid]; + return (int)$_SESSION['LOGIN_UNIT_CUID'][$uid]; } /** From e8299a123b330b59b04130c7c7822de9a894f5f3 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 10 Dec 2024 10:06:49 +0900 Subject: [PATCH 10/28] Update Edit Log with JSONB blocks all action data goes into a JSON block and the old action columns will be deprecated Same for ip, new ip address block with all possible ip addeses Additional HTTP_ data goes into the http_data block new request_schema column to get if the request was done to http or https --- 4dev/database/table/edit_log.sql | 39 +++++++----- .../CoreLibsACLLogin_database_create_data.sql | 7 +-- .../edit_tables_cuid_cuuid_update_add.sql | 4 ++ www/admin/class_test.login.php | 6 ++ www/admin/class_test.php | 3 + www/lib/CoreLibs/ACL/Login.php | 62 +++++++++++-------- 6 files changed, 74 insertions(+), 47 deletions(-) diff --git a/4dev/database/table/edit_log.sql b/4dev/database/table/edit_log.sql index e1de32d3..7d2039a9 100644 --- a/4dev/database/table/edit_log.sql +++ b/4dev/database/table/edit_log.sql @@ -11,34 +11,41 @@ CREATE TABLE edit_log ( euid INT, -- this is a foreign key, but I don't nedd to reference to it FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, ecuid VARCHAR, - ecuuid UUID, + ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table username VARCHAR, password VARCHAR, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, - ip VARCHAR, + ip VARCHAR, -- just the REMOTE_IP, full set see ip_address + ip_address JSONB, -- REMOTE_IP and all other IPs (X_FORWARD, etc) as JSON block error TEXT, event TEXT, data_binary BYTEA, data TEXT, page VARCHAR, - action VARCHAR, - 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, + -- various info data sets user_agent VARCHAR, referer VARCHAR, script_name VARCHAR, query_string VARCHAR, + request_scheme VARCHAR, -- http or https server_name VARCHAR, http_host VARCHAR, - http_accept VARCHAR, - http_accept_charset VARCHAR, - http_accept_encoding VARCHAR, - session_id VARCHAR + http_data JSONB, + http_accept VARCHAR, -- in http_data + http_accept_charset VARCHAR, -- in http_data + http_accept_encoding VARCHAR, -- in http_data + -- session ID if set + session_id VARCHAR. + -- any action var, -> same set in action_data as JSON + action_data JSONB, + 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; diff --git a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql index 3d4a54b4..9216f692 100644 --- a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql +++ b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql @@ -579,9 +579,6 @@ CREATE TABLE edit_user ( strict SMALLINT DEFAULT 0, locked SMALLINT 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 admin SMALLINT NOT NULL DEFAULT 0, -- last login log @@ -620,8 +617,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.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.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.last_login IS 'Last succesfull login tiemstamp'; COMMENT ON COLUMN edit_user.login_error_count IS 'Number of failed logins, reset on successful login'; @@ -1015,7 +1010,7 @@ INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_ -- edit user -- inserts admin user so basic users can be created 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_group_id FROM edit_group WHERE name = 'Admin'), (SELECT edit_scheme_id FROM edit_scheme WHERE name = 'Admin'), diff --git a/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql b/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql index f4e36ec3..df2bdbef 100644 --- a/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql +++ b/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql @@ -3,6 +3,10 @@ ALTER TABLE edit_generic ADD cuuid UUID DEFAULT gen_random_uuid(); ALTER TABLE edit_log ADD ecuid VARCHAR; ALTER TABLE edit_log ADD ecuuid VARCHAR; ALTER TABLE edit_log ADD action_sub_id VARCHAR; +ALTER TABLE edit_log ADD http_data JSONB; +ALTER TABLE edit_log ADD ip_address JSONB; +ALTER TABLE edit_log ADD action_data JSONB; +ALTER TABLE edit_log ADD request_scheme VARCHAR; -- update set_edit_gneric -- adds the created or updated date tags diff --git a/www/admin/class_test.login.php b/www/admin/class_test.login.php index e5bbc6d4..d153078d 100644 --- a/www/admin/class_test.login.php +++ b/www/admin/class_test.login.php @@ -118,6 +118,12 @@ if (isset($login->loginGetAcl()['unit'])) { print "Something went wrong with the login
"; } +echo "
"; + +// IP check: 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'CLIENT_IP' in _SERVER +// Agent check: 'HTTP_USER_AGENT' + + echo "
"; print "SESSION: " . Support::printAr($_SESSION) . "
"; diff --git a/www/admin/class_test.php b/www/admin/class_test.php index 138496ef..616f684a 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -194,6 +194,9 @@ print "HOST: " . HOST_NAME . " => DB HOST: " . DB_CONFIG_NAME . " => " . Support print "DS is: " . DIRECTORY_SEPARATOR . "
"; print "SERVER HOST: " . $_SERVER['HTTP_HOST'] . "
"; +print "
READ _SERVER ARRAY:
"; +print Support::dumpVar(array_map('htmlentities', $_SERVER)); + print ""; # __END__ diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index db6fb705..69160ce8 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -1904,7 +1904,7 @@ body { margin: 2% 5%; } .login-data { - margin: 0 5% 5% 5%; + margin: 2% 5% 5% 5%; } .login-data-row { display: flex; @@ -1951,7 +1951,7 @@ button.login-button { margin: 5% 0; } .login-data { - margin: 0 5% 5% 5%; + margin: 5%; } .login-error { margin: 10% 5%; @@ -2160,16 +2160,18 @@ HTML; $q = <<db->dbExecParams( @@ -2186,7 +2188,7 @@ HTML; is_string($this->session->get('LOGIN_EUCUID')) ? $this->session->get('LOGIN_EUCUID') : null, !empty($this->session->get('LOGIN_EUCUUID')) && - Uids::validateUuuidv4($this->session->get('LOGIN_EUCUUID')) ? + Uids::validateUuuidv4($this->session->get('LOGIN_EUCUUID')) ? $this->session->get('LOGIN_EUCUUID') : null, (string)$event, (string)$error, @@ -2195,29 +2197,39 @@ HTML; (string)$this->page_name, // row 2 $_SERVER["REMOTE_ADDR"] ?? null, + [ + 'REMOTE_ADDR' => $_SERVER["REMOTE_ADDR"], + ], $_SERVER['HTTP_USER_AGENT'] ?? null, $_SERVER['HTTP_REFERER'] ?? null, $_SERVER['SCRIPT_FILENAME'] ?? null, $_SERVER['QUERY_STRING'] ?? null, + $_SERVER['REQUEST_SCHEME'] ?? 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, + $_SERVER['HTTP_HOST'] ?? null, + [ + 'HTTP_ACCEPT' => $_SERVER['HTTP_ACCEPT'] ?? null, + 'HTTP_ACCEPT_CHARSET' => $_SERVER['HTTP_ACCEPT_CHARSET'] ?? null, + 'HTTP_ACCEPT_LANGUAGE' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? null, + 'HTTP_ACCEPT_ENCODING' => $_SERVER['HTTP_ACCEPT_ENCODING'] ?? null, + ], $this->session->getSessionId() !== '' ? $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, + // action data as JSONB + [ + 'action' => $action_set['action'] ?? null, + 'action_id' => $action_set['action_id'] ?? null, + 'action_sub_id' => $action_set['action_sub_id'] ?? null, + 'action_yes' => $action_set['action_yes'] ?? null, + 'action_flag' => $action_set['action_flag'] ?? null, + 'action_menu' => $action_set['action_menu'] ?? null, + 'action_loaded' => $action_set['action_loaded'] ?? null, + 'action_value' => $action_set['action_value'] ?? null, + 'action_type' => $action_set['action_type'] ?? null, + 'action_error' => $action_set['action_error'] ?? null, + ] ], 'NULL' ); From 78591d6ba46d0f3cb3c5cfe6c4f1b6e5dcd71b42 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 10 Dec 2024 12:01:06 +0900 Subject: [PATCH 11/28] Fix Param regex lookup Query was not counting params after "--" comment strings --- 4dev/tests/DB/CoreLibsDBIOTest.php | 33 +++++++++++++++++++ www/lib/CoreLibs/DB/IO.php | 1 + .../DB/Support/ConvertPlaceholder.php | 15 +++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/4dev/tests/DB/CoreLibsDBIOTest.php b/4dev/tests/DB/CoreLibsDBIOTest.php index 19420a47..fab83940 100644 --- a/4dev/tests/DB/CoreLibsDBIOTest.php +++ b/4dev/tests/DB/CoreLibsDBIOTest.php @@ -5135,6 +5135,39 @@ final class CoreLibsDBIOTest extends TestCase SQL, 'count' => 6, 'convert' => false, + ], + 'comments in insert' => [ + 'query' => << 4, + 'convert' => false + ], + // Note some are not set + 'a complete set of possible' => [ + 'query' => << $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, ] ]; } diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index 4daad5b8..d579fd16 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -1332,6 +1332,7 @@ class IO */ private function __dbCheckQueryParams(string $query, array $params): bool { + // $this->log->debug('DB QUERY PARAMS REGEX', ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS); $placeholder_count = $this->__dbCountQueryParams($query); $params_count = count($params); if ($params_count != $placeholder_count) { diff --git a/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php b/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php index 0b9542b2..3541276e 100644 --- a/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php +++ b/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php @@ -14,8 +14,19 @@ namespace CoreLibs\DB\Support; class ConvertPlaceholder { - /** @var string split regex */ - private const PATTERN_QUERY_SPLIT = '[(<>=,?-]|->|->>|#>|#>>|@>|<@|\?\|\?\&|\|\||#-'; + // NOTE for missing: range */+ are not iplemented in the regex below, but - is for now + // NOTE some combinations are allowed, but the query will fail before this + /** @var string split regex, entries before $ group */ + private const PATTERN_QUERY_SPLIT = + ',|' // for ',' mostly in INSERT + . '[(<>=]|' // general set for (, <, >, = in any query with any combination + . '(?:[\(,]\s*\-\-\s*\w*)\r?\n|' // a comment that starts after a ( or , + . '\^@|' // text search for start from text with ^@ + . '\|\||' // concats two elements + . '&&|' // array overlap + . '\-\|\-|' // range overlap + . '[^-]-{1}|' // single -, used in JSON too + . '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-'; //JSON searches, Array searchs, etc /** @var string the main regex including the pattern query split */ private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:\?\?|' . self::PATTERN_QUERY_SPLIT . ')\s*'; /** @var string parts to ignore in the SQL */ From a7742bd5c82192381c45b6dc76829bc73846705b Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 10 Dec 2024 13:36:57 +0900 Subject: [PATCH 12/28] DB IO count params fix for comments --- 4dev/tests/DB/CoreLibsDBIOTest.php | 6 +++--- www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/4dev/tests/DB/CoreLibsDBIOTest.php b/4dev/tests/DB/CoreLibsDBIOTest.php index fab83940..faadf6e1 100644 --- a/4dev/tests/DB/CoreLibsDBIOTest.php +++ b/4dev/tests/DB/CoreLibsDBIOTest.php @@ -5141,11 +5141,11 @@ final class CoreLibsDBIOTest extends TestCase INSERT INTO table_with_primary_key ( row_int, row_numeric, row_varchar, row_varchar_literal ) VALUES ( - -- comment + -- comment 1 $1, $2, - -- comment + -- comment 2 $3 - -- comment + -- comment 3 , $4 ) SQL, diff --git a/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php b/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php index 3541276e..fd8eea60 100644 --- a/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php +++ b/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php @@ -20,7 +20,7 @@ class ConvertPlaceholder private const PATTERN_QUERY_SPLIT = ',|' // for ',' mostly in INSERT . '[(<>=]|' // general set for (, <, >, = in any query with any combination - . '(?:[\(,]\s*\-\-\s*\w*)\r?\n|' // a comment that starts after a ( or , + . '(?:[\(,]\s*\-\-[\s\w]*)\r?\n|' // a comment that starts after a ( or , . '\^@|' // text search for start from text with ^@ . '\|\||' // concats two elements . '&&|' // array overlap From 41cb6358f9a14147c988d774a6c427a348ebd82a Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 10 Dec 2024 14:40:07 +0900 Subject: [PATCH 13/28] phpunit checks update, update edit_log logging sets --- .../CoreLibsACLLogin_database_create_data.sql | 39 +++++++++++-------- www/lib/CoreLibs/ACL/Login.php | 18 ++++++--- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql index 9216f692..c5f4bb3c 100644 --- a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql +++ b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql @@ -651,36 +651,43 @@ CREATE TABLE edit_log ( euid INT, -- this is a foreign key, but I don't nedd to reference to it FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, ecuid VARCHAR, - ecuuid UUID, + ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table username VARCHAR, password VARCHAR, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, - ip VARCHAR, + ip VARCHAR, -- just the REMOTE_IP, full set see ip_address + ip_address JSONB, -- REMOTE_IP and all other IPs (X_FORWARD, etc) as JSON block error TEXT, event TEXT, data_binary BYTEA, data TEXT, page VARCHAR, - action VARCHAR, - 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, + -- various info data sets user_agent VARCHAR, referer VARCHAR, script_name VARCHAR, query_string VARCHAR, + request_scheme VARCHAR, -- http or https server_name VARCHAR, http_host VARCHAR, - http_accept VARCHAR, - http_accept_charset VARCHAR, - http_accept_encoding VARCHAR, - session_id VARCHAR + http_data JSONB, + http_accept VARCHAR, -- in http_data + http_accept_charset VARCHAR, -- in http_data + http_accept_encoding VARCHAR, -- in http_data + -- session ID if set + session_id VARCHAR. + -- any action var, -> same set in action_data as JSON + action_data JSONB, + 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; -- END: table/edit_log.sql -- START: table/edit_log_overflow.sql diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 69160ce8..5b8609de 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -2197,9 +2197,15 @@ HTML; (string)$this->page_name, // row 2 $_SERVER["REMOTE_ADDR"] ?? null, - [ + Json::jsonConvertArrayTo([ 'REMOTE_ADDR' => $_SERVER["REMOTE_ADDR"], - ], + 'HTTP_X_FORWARDED_FOR' => !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? + explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']) + : [], + 'CLIENT_IP' => !empty($_SERVER['CLIENT_IP']) ? + explode(',', $_SERVER['CLIENT_IP']) + : [], + ]), $_SERVER['HTTP_USER_AGENT'] ?? null, $_SERVER['HTTP_REFERER'] ?? null, $_SERVER['SCRIPT_FILENAME'] ?? null, @@ -2208,17 +2214,17 @@ HTML; $_SERVER['SERVER_NAME'] ?? null, // row 3 $_SERVER['HTTP_HOST'] ?? null, - [ + Json::jsonConvertArrayTo([ 'HTTP_ACCEPT' => $_SERVER['HTTP_ACCEPT'] ?? null, 'HTTP_ACCEPT_CHARSET' => $_SERVER['HTTP_ACCEPT_CHARSET'] ?? null, 'HTTP_ACCEPT_LANGUAGE' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? null, 'HTTP_ACCEPT_ENCODING' => $_SERVER['HTTP_ACCEPT_ENCODING'] ?? null, - ], + ]), $this->session->getSessionId() !== '' ? $this->session->getSessionId() : null, // row 4 // action data as JSONB - [ + Json::jsonConvertArrayTo([ 'action' => $action_set['action'] ?? null, 'action_id' => $action_set['action_id'] ?? null, 'action_sub_id' => $action_set['action_sub_id'] ?? null, @@ -2229,7 +2235,7 @@ HTML; 'action_value' => $action_set['action_value'] ?? null, 'action_type' => $action_set['action_type'] ?? null, 'action_error' => $action_set['action_error'] ?? null, - ] + ]) ], 'NULL' ); From 46e44c19bfea238243530607d317bdd7bb112daf Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 10 Dec 2024 14:44:00 +0900 Subject: [PATCH 14/28] edit log table column order update --- 4dev/database/table/edit_log.sql | 19 +++++++++++++----- .../CoreLibsACLLogin_database_create_data.sql | 20 +++++++++++++------ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/4dev/database/table/edit_log.sql b/4dev/database/table/edit_log.sql index 7d2039a9..5c492849 100644 --- a/4dev/database/table/edit_log.sql +++ b/4dev/database/table/edit_log.sql @@ -12,15 +12,24 @@ CREATE TABLE edit_log ( FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, ecuid VARCHAR, ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table - username VARCHAR, - password VARCHAR, + -- date_created equal, but can be overridden event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, - ip VARCHAR, -- just the REMOTE_IP, full set see ip_address + -- 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, event TEXT, + -- bytea or string type storage of any data data_binary BYTEA, data TEXT, + -- set page name only page VARCHAR, -- various info data sets user_agent VARCHAR, @@ -31,13 +40,13 @@ CREATE TABLE edit_log ( server_name VARCHAR, http_host VARCHAR, http_data JSONB, + -- DEPRECATED [http*] http_accept VARCHAR, -- in http_data http_accept_charset VARCHAR, -- in http_data http_accept_encoding VARCHAR, -- in http_data - -- session ID if set - session_id VARCHAR. -- 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 diff --git a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql index c5f4bb3c..686d5a03 100644 --- a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql +++ b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql @@ -652,15 +652,24 @@ CREATE TABLE edit_log ( FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, ecuid VARCHAR, ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table - username VARCHAR, - password VARCHAR, + -- date_created equal, but can be overridden event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, - ip VARCHAR, -- just the REMOTE_IP, full set see ip_address + -- 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, event TEXT, + -- bytea or string type storage of any data data_binary BYTEA, data TEXT, + -- set page name only page VARCHAR, -- various info data sets user_agent VARCHAR, @@ -671,13 +680,13 @@ CREATE TABLE edit_log ( server_name VARCHAR, http_host VARCHAR, http_data JSONB, + -- DEPRECATED [http*] http_accept VARCHAR, -- in http_data http_accept_charset VARCHAR, -- in http_data http_accept_encoding VARCHAR, -- in http_data - -- session ID if set - session_id VARCHAR. -- 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 @@ -688,7 +697,6 @@ CREATE TABLE edit_log ( action_value VARCHAR, -- in action_data action_type VARCHAR, -- in action_data action_error VARCHAR -- in action_data -) INHERITS (edit_generic) WITHOUT OIDS; -- END: table/edit_log.sql -- START: table/edit_log_overflow.sql -- AUTHOR: Clemens Schwaighofer From 022c39e791051ec902ed37c549253e6f24b18e86 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 10 Dec 2024 15:24:45 +0900 Subject: [PATCH 15/28] Add missing phpunit test folder for deprecated session var load test --- 4dev/tests/Language/locale_other/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 4dev/tests/Language/locale_other/.gitignore diff --git a/4dev/tests/Language/locale_other/.gitignore b/4dev/tests/Language/locale_other/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/4dev/tests/Language/locale_other/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore From a46601fe0337cac0cabb5733bbabb69b187fcb87 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 10 Dec 2024 15:25:17 +0900 Subject: [PATCH 16/28] Sync folder is master and not trunk --- 4dev/composer/sync-to-composer-all-folder.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/4dev/composer/sync-to-composer-all-folder.sh b/4dev/composer/sync-to-composer-all-folder.sh index d717bfcb..07e57098 100755 --- a/4dev/composer/sync-to-composer-all-folder.sh +++ b/4dev/composer/sync-to-composer-all-folder.sh @@ -13,7 +13,7 @@ if [ "${GO}" != "go" ]; then fi; BASE="/storage/var/www/html/developers/clemens/core_data/"; -SOURCE="${BASE}php_libraries/trunk/" +SOURCE="${BASE}php_libraries/master/" TARGET="${BASE}composer-packages/CoreLibs-Composer-All/" rsync ${DRY_RUN}-Plzvrupt --stats --delete ${SOURCE}4dev/tests/ ${TARGET}test/phpunit/ From 534303476831e490ac6abb815eb64c3776528436 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 11 Dec 2024 10:30:41 +0900 Subject: [PATCH 17/28] Fix DB IO placeholder detect and count regex comment regex: (?:\-\-[^\r\n]*?\r?\n)* Which is AFTER the element search as the comment can appear anywhere after the tag trigger --- 4dev/tests/DB/CoreLibsDBIOTest.php | 32 ++++- .../class_test.db.convert-placeholder.php | 1 - www/admin/class_test.db.query-placeholder.php | 129 ++++++++++++++---- www/admin/class_test.php | 2 +- www/lib/CoreLibs/DB/IO.php | 2 +- .../DB/Support/ConvertPlaceholder.php | 17 ++- 6 files changed, 146 insertions(+), 37 deletions(-) diff --git a/4dev/tests/DB/CoreLibsDBIOTest.php b/4dev/tests/DB/CoreLibsDBIOTest.php index faadf6e1..14767292 100644 --- a/4dev/tests/DB/CoreLibsDBIOTest.php +++ b/4dev/tests/DB/CoreLibsDBIOTest.php @@ -5141,9 +5141,9 @@ final class CoreLibsDBIOTest extends TestCase INSERT INTO table_with_primary_key ( row_int, row_numeric, row_varchar, row_varchar_literal ) VALUES ( - -- comment 1 + -- comment 1 かな $1, $2, - -- comment 2 + -- comment 2 - $3 -- comment 3 , $4 @@ -5152,6 +5152,23 @@ final class CoreLibsDBIOTest extends TestCase 'count' => 4, 'convert' => false ], + 'comment in update' => [ + 'query' => << 4, + 'convert' => false, + ], // Note some are not set 'a complete set of possible' => [ 'query' => << 12, 'convert' => false, + ], + // all the same + 'all the same numbered' => [ + 'query' => << 1, + 'convert' => false, ] ]; } diff --git a/www/admin/class_test.db.convert-placeholder.php b/www/admin/class_test.db.convert-placeholder.php index 8cca56a9..e95586ad 100644 --- a/www/admin/class_test.db.convert-placeholder.php +++ b/www/admin/class_test.db.convert-placeholder.php @@ -28,7 +28,6 @@ $log = new CoreLibs\Logging\Logging([ 'log_per_date' => true, ]); - $PAGE_NAME = 'TEST CLASS: DB CONVERT PLACEHOLDER'; print ""; print "" . $PAGE_NAME . ""; diff --git a/www/admin/class_test.db.query-placeholder.php b/www/admin/class_test.db.query-placeholder.php index b2d729c4..c934962a 100644 --- a/www/admin/class_test.db.query-placeholder.php +++ b/www/admin/class_test.db.query-placeholder.php @@ -53,6 +53,9 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) { } else { print "NO DB HANDLER
"; } +// REGEX for placeholder count +print "Placeholder regex:
" . CoreLibs\DB\Support\ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS . "
"; + // turn on debug replace for placeholders $db->dbSetDebugReplacePlaceholder(true); @@ -62,53 +65,94 @@ $db->dbExec("TRUNCATE test_foo"); $uniqid = \CoreLibs\Create\Uids::uniqIdShort(); $binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: ''); $query_params = [ - $uniqid, - true, - 'STRING A', - 2, - 2.5, - 1, - date('H:m:s'), - date('Y-m-d H:i:s'), - json_encode(['a' => 'string', 'b' => 1, 'c' => 1.5, 'f' => true, 'g' => ['a', 1, 1.5]]), - null, - '{"a", "b"}', - '{1,2}', - '{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}', - '("Text", 4, 6.3)', - $binary_data + $uniqid, // test + true, // some_bool + 'STRING A', // string_a + 2, // number_a + 2.5, // numeric_a + 1, // smallint + date('H:m:s'), // some_internval + date('Y-m-d H:i:s'), // some_timestamp + json_encode(['a' => 'string', 'b' => 1, 'c' => 1.5, 'f' => true, 'g' => ['a', 1, 1.5]]), // json_string + null, // null_var + '{"a", "b"}', // array_char_1 + '{1,2}', // array_int_1 + '{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}', // array_composite + '("Text", 4, 6.3)', // composite_item + $binary_data, // some_binary + date('Y-m-d'), // some_date + date('H:i:s'), // some_time + '{"c", "d", "e"}', // array_char_2 + '{3,4,5}', // array_int_2 + 12345667778818, // bigint + 1.56, // numbrer_real + 3.75, // number_double + 124.5, // numeric_3 + \CoreLibs\Create\Uids::uuidv4() // uuid_var ]; $query_insert = <<dbExecParams($query_insert, $query_params); echo "*
"; echo "INSERT ALL COLUMN TYPES: " . Support::printToString($query_params) . " |
" - . "QUERY: " . $db->dbGetQuery() . " |
" + . "QUERY:
" . $db->dbGetQuery() . "
|
" . "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |
" . "RETURNING EXT:
" . print_r($db->dbGetReturningExt(), true) . "
|
" . "RETURNING RETURN:
" . print_r($db->dbGetReturningArray(), true) . "
 |
" @@ -146,6 +190,16 @@ SQL, 'params' => [], 'direction' => 'pg', ], + 'numbers' => [ + 'query' => << [\CoreLibs\Create\Uids::uniqIdShort(), 'string A-1', 1234], + 'direction' => 'pdo', + ], 'a?' => [ 'query' => << 'pg', ], + 'select, compare $' => [ + 'query' => <<= $1 OR number_a <= $2 OR + number_a > $3 OR number_a < $4 + OR number_a = $5 OR number_a <> $6 + SQL, + 'params' => [1, 2, 3, 4, 5, 6], + 'direction' => 'pg' + ] ]; $db->dbSetConvertPlaceholder(true); @@ -184,11 +250,12 @@ foreach ($test_queries as $info => $data) { // . "
"; if ($db->dbCheckQueryForSelect($query)) { $row = $db->dbReturnRowParams($query, $params); - print "[$info] SELECT: " . Support::prAr($row) . "
"; + print "[$info] SELECT: " . Support::prAr($row) . "
"; } else { $db->dbExecParams($query, $params); } - print "[$info] " . Support::printAr($db->dbGetPlaceholderConverted()) . "
"; + print "ERROR: " . $db->dbGetLastError(true) . "
"; + print "[$info] " . Support::printAr($db->dbGetPlaceholderConverted()) . "
"; echo "
"; } @@ -203,22 +270,29 @@ SQL, ['string A-1'] )) ) { - print "RES: " . Support::prAr($res) . "
"; + print "RES: " . Support::prAr($res) . "
"; } +print "ERROR: " . $db->dbGetLastError(true) . "
"; +echo "
"; print "CursorExt: " . Support::prAr($db->dbGetCursorExt(<<"; +// ERROR BELOW: missing params $res = $db->dbReturnRowParams(<<dbGetPlaceholderConverted()) . "
"; +print "ERROR: " . $db->dbGetLastError(true) . "
"; +echo "
"; +// ERROR BELOW: LIKE cannot have placeholder echo "dbReturn read LIKE:
"; while ( is_array($res = $db->dbReturnParams( @@ -232,6 +306,7 @@ SQL, ) { print "RES: " . Support::prAr($res) . "
"; } +print "ERROR: " . $db->dbGetLastError(true) . "
"; print ""; $db->log->debug('DEBUGEND', '==================================== [END]'); diff --git a/www/admin/class_test.php b/www/admin/class_test.php index 4f576fb2..40d2eba1 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -91,7 +91,7 @@ HTML; $test_files = [ 'class_test.db.php' => 'Class Test: DB', 'class_test.db.types.php' => 'Class Test: DB column type convert', - 'class_test.db.query-placeholder.php' => 'Class Test: DB query placeholder convert', + 'class_test.db.query-placeholder.php' => 'Class Test: DB placeholder queries', 'class_test.db.dbReturn.php' => 'Class Test: DB dbReturn', 'class_test.db.single.php' => 'Class Test: DB single query tests', 'class_test.db.convert-placeholder.php' => 'Class Test: DB convert placeholder', diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index d579fd16..7af3407f 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -1332,7 +1332,7 @@ class IO */ private function __dbCheckQueryParams(string $query, array $params): bool { - // $this->log->debug('DB QUERY PARAMS REGEX', ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS); + $this->log->debug('DB QUERY PARAMS REGEX', ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS); $placeholder_count = $this->__dbCountQueryParams($query); $params_count = count($params); if ($params_count != $placeholder_count) { diff --git a/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php b/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php index fd8eea60..dff03788 100644 --- a/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php +++ b/www/lib/CoreLibs/DB/Support/ConvertPlaceholder.php @@ -18,17 +18,20 @@ class ConvertPlaceholder // NOTE some combinations are allowed, but the query will fail before this /** @var string split regex, entries before $ group */ private const PATTERN_QUERY_SPLIT = - ',|' // for ',' mostly in INSERT - . '[(<>=]|' // general set for (, <, >, = in any query with any combination - . '(?:[\(,]\s*\-\-[\s\w]*)\r?\n|' // a comment that starts after a ( or , + '\?\?|' // 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 + . '\-\|\-|' // range overlap for array . '[^-]-{1}|' // single -, used in JSON too . '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-'; //JSON searches, Array searchs, etc /** @var string the main regex including the pattern query split */ - 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 */ private const PATTERN_IGNORE = // digit -> ignore @@ -45,6 +48,7 @@ class ConvertPlaceholder /** @var string replace regex for named (:...) entries */ public const REGEX_REPLACE_NAMED = '/' . '(' . self::PATTERN_ELEMENT . ')' + . self::PATTERN_COMMENT . '(' . self::PATTERN_IGNORE . self::PATTERN_NAMED @@ -53,6 +57,7 @@ class ConvertPlaceholder /** @var string replace regex for question mark (?) entries */ public const REGEX_REPLACE_QUESTION_MARK = '/' . '(' . self::PATTERN_ELEMENT . ')' + . self::PATTERN_COMMENT . '(' . self::PATTERN_IGNORE . self::PATTERN_QUESTION_MARK @@ -61,6 +66,7 @@ class ConvertPlaceholder /** @var string replace regex for numbered ($n) entries */ public const REGEX_REPLACE_NUMBERED = '/' . '(' . self::PATTERN_ELEMENT . ')' + . self::PATTERN_COMMENT . '(' . self::PATTERN_IGNORE . self::PATTERN_NUMBERED @@ -71,6 +77,7 @@ class ConvertPlaceholder // prefix string part, must match towards // seperator for ( = , ? - [and json/jsonb in pg doc section 9.15] . self::PATTERN_ELEMENT + . self::PATTERN_COMMENT // match for replace part . '(?:' // ignore parts From ba11a936db3605f22644a5749b1a5f4fc326c1a1 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 11 Dec 2024 10:36:31 +0900 Subject: [PATCH 18/28] DB IO remove debug placeholder output --- www/lib/CoreLibs/DB/IO.php | 1 - 1 file changed, 1 deletion(-) diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index 7af3407f..4daad5b8 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -1332,7 +1332,6 @@ class IO */ private function __dbCheckQueryParams(string $query, array $params): bool { - $this->log->debug('DB QUERY PARAMS REGEX', ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS); $placeholder_count = $this->__dbCountQueryParams($query); $params_count = count($params); if ($params_count != $placeholder_count) { From 8d3882a6feb7fe8a18a3ef38b8a0e70451096d4f Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 11 Dec 2024 21:02:21 +0900 Subject: [PATCH 19/28] Session and ACL Login Class update Session: regenerate session id after some time or random. Default is 'never', can be 'interval' form 0 to 1h and random from always to 1 in 100 Session also checks that strict session settings are enabled Login class: Automatic re-read of acl settings after some time (default 5min, can be chnaged via option). Default set strict headers, can be turned off via option Moved various parts into their own methods and cleaned up double call logic. Login is now recorded in the last login entry no more debug flags are read from the database anymore All options are set via array and not with a single option (was auto login) --- 4dev/database/table/edit_user.sql | 3 + 4dev/tests/ACL/CoreLibsACLLoginTest.php | 7 +- .../CoreLibsACLLogin_database_create_data.sql | 3 + .../Create/CoreLibsCreateSessionTest.php | 21 +- .../edit_tables_cuid_cuuid_update_add.sql | 4 + www/admin/class_test.login.php | 10 +- www/admin/class_test.session.php | 2 +- www/lib/CoreLibs/ACL/Login.php | 1252 ++++++++++------- www/lib/CoreLibs/Create/Session.php | 169 ++- 9 files changed, 944 insertions(+), 527 deletions(-) diff --git a/4dev/database/table/edit_user.sql b/4dev/database/table/edit_user.sql index 2b3c58d7..47c4914c 100644 --- a/4dev/database/table/edit_user.sql +++ b/4dev/database/table/edit_user.sql @@ -37,6 +37,8 @@ CREATE TABLE edit_user ( protected SMALLINT NOT NULL DEFAULT 0, -- is admin user admin SMALLINT NOT NULL DEFAULT 0, + -- force lgout counter + force_logout INT DEFAULT 0, -- last login log last_login TIMESTAMP WITHOUT TIME ZONE, -- login error @@ -74,6 +76,7 @@ COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be lo 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.admin IS 'If set, this user is SUPER admin'; +COMMENT ON COLUMN edit_user.force_logout IS 'Counter for forced log out, if this one is higher than the session set one the session gets terminated'; 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_date_last IS 'Last login error date'; diff --git a/4dev/tests/ACL/CoreLibsACLLoginTest.php b/4dev/tests/ACL/CoreLibsACLLoginTest.php index de57645f..1582a1ee 100644 --- a/4dev/tests/ACL/CoreLibsACLLoginTest.php +++ b/4dev/tests/ACL/CoreLibsACLLoginTest.php @@ -1185,7 +1185,6 @@ final class CoreLibsACLLoginTest extends TestCase foreach ($session as $session_var => $session_value) { $_SESSION[$session_var] = $session_value; } - /** @var \CoreLibs\ACL\Login&MockObject */ $login_mock = $this->getMockBuilder(\CoreLibs\ACL\Login::class) ->setConstructorArgs([ @@ -1204,7 +1203,7 @@ final class CoreLibsACLLoginTest extends TestCase . 'locale' . DIRECTORY_SEPARATOR, ] ]) - ->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin']) + ->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin', 'loginEnhanceHttpSecurity']) ->getMock(); $login_mock->expects($this->any()) ->method('loginTerminate') @@ -1222,6 +1221,10 @@ final class CoreLibsACLLoginTest extends TestCase ->method('loginPrintLogin') ->willReturnCallback(function () { }); + $login_mock->expects($this->any()) + ->method('loginEnhanceHttpSecurity') + ->willReturnCallback(function () { + }); // if mock_settings: enabled OFF // run DB update and set off diff --git a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql index 686d5a03..e1d1a0cb 100644 --- a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql +++ b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql @@ -581,6 +581,8 @@ CREATE TABLE edit_user ( protected SMALLINT NOT NULL DEFAULT 0, -- is admin user admin SMALLINT NOT NULL DEFAULT 0, + -- forced logout counter + force_logout INT DEFAULT 0, -- last login log last_login TIMESTAMP WITHOUT TIME ZONE, -- login error @@ -697,6 +699,7 @@ CREATE TABLE edit_log ( action_value VARCHAR, -- in action_data action_type VARCHAR, -- in action_data action_error VARCHAR -- in action_data +) INHERITS (edit_generic) WITHOUT OIDS; -- END: table/edit_log.sql -- START: table/edit_log_overflow.sql -- AUTHOR: Clemens Schwaighofer diff --git a/4dev/tests/Create/CoreLibsCreateSessionTest.php b/4dev/tests/Create/CoreLibsCreateSessionTest.php index 2ac833fd..411b610b 100644 --- a/4dev/tests/Create/CoreLibsCreateSessionTest.php +++ b/4dev/tests/Create/CoreLibsCreateSessionTest.php @@ -54,7 +54,9 @@ final class CoreLibsCreateSessionTest extends TestCase 'getSessionId' => '1234abcd4567' ], 'sessionNameGlobals', - false, + [ + 'auto_write_close' => false, + ], ], 'auto write close' => [ 'sessionNameAutoWriteClose', @@ -66,7 +68,9 @@ final class CoreLibsCreateSessionTest extends TestCase 'getSessionId' => '1234abcd4567' ], 'sessionNameAutoWriteClose', - true, + [ + 'auto_write_close' => true, + ], ], ]; } @@ -81,13 +85,14 @@ final class CoreLibsCreateSessionTest extends TestCase * @param string $input * @param array $mock_data * @param string $expected + * @param array $options * @return void */ public function testStartSession( string $input, array $mock_data, string $expected, - ?bool $auto_write_close, + ?array $options, ): void { /** @var \CoreLibs\Create\Session&MockObject $session_mock */ $session_mock = $this->createPartialMock( @@ -174,9 +179,14 @@ final class CoreLibsCreateSessionTest extends TestCase 4, '/^\[SESSION\] Failed to activate session/' ], + 'expired session' => [ + \RuntimeException::class, + 5, + '/^\[SESSION\] Expired session found/' + ], 'not a valid session id returned' => [ \UnexpectedValueException::class, - 5, + 6, '/^\[SESSION\] getSessionId did not return a session id/' ], */ ]; @@ -206,7 +216,8 @@ final class CoreLibsCreateSessionTest extends TestCase $this->expectException($exception); $this->expectExceptionCode($exception_code); $this->expectExceptionMessageMatches($expected_error); - new \CoreLibs\Create\Session($session_name); + // 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]); } /** diff --git a/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql b/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql index df2bdbef..ec72ed72 100644 --- a/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql +++ b/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql @@ -7,6 +7,10 @@ ALTER TABLE edit_log ADD http_data JSONB; ALTER TABLE edit_log ADD ip_address JSONB; ALTER TABLE edit_log ADD action_data JSONB; ALTER TABLE edit_log ADD request_scheme VARCHAR; +ALTER TABLE edit_user ADD force_logout INT DEFAULT 0; +COMMENT ON COLUMN edit_user.force_logout IS 'Counter for forced log out, if this one is higher than the session set one the session gets terminated'; +ALTER TABLE edit_user ADD last_login TIMESTAMP WITHOUT TIME ZONE; +COMMENT ON COLUMN edit_user.last_login IS 'Last succesfull login tiemstamp'; -- update set_edit_gneric -- adds the created or updated date tags diff --git a/www/admin/class_test.login.php b/www/admin/class_test.login.php index d153078d..1be459a9 100644 --- a/www/admin/class_test.login.php +++ b/www/admin/class_test.login.php @@ -21,7 +21,10 @@ $SET_SESSION_NAME = EDIT_SESSION_NAME; use CoreLibs\Debug\Support; // init login & backend class -$session = new CoreLibs\Create\Session($SET_SESSION_NAME); +$session = new CoreLibs\Create\Session($SET_SESSION_NAME, [ + 'regenerate' => 'interval', + 'regenerate_interval' => 10, // every 10 seconds +]); $log = new CoreLibs\Logging\Logging([ 'log_folder' => BASE . LOG, 'log_file_id' => $LOG_FILE_ID, @@ -90,6 +93,8 @@ print << HTML; +echo "SESSION ID: " . $session->getSessionIdCall() . "
"; + echo "CHECK PERMISSION: " . ($login->loginCheckPermissions() ? 'OK' : 'BAD') . "
"; echo "IS ADMIN: " . ($login->loginIsAdmin() ? 'OK' : 'BAD') . "
"; echo "MIN ACCESS BASE: " . ($login->loginCheckAccessBase('admin') ? 'OK' : 'BAD') . "
"; @@ -118,8 +123,7 @@ if (isset($login->loginGetAcl()['unit'])) { print "Something went wrong with the login
"; } -echo "
"; - +// echo "
"; // IP check: 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'CLIENT_IP' in _SERVER // Agent check: 'HTTP_USER_AGENT' diff --git a/www/admin/class_test.session.php b/www/admin/class_test.session.php index ed9439ef..a8227c78 100644 --- a/www/admin/class_test.session.php +++ b/www/admin/class_test.session.php @@ -146,7 +146,7 @@ $_SESSION['this_will_be_written'] = 'not empty'; // open again with same name $session_name = 'class-test-session'; try { - $session_alt = new Session($session_name, auto_write_close:true); + $session_alt = new Session($session_name, ['auto_write_close' => true]); print "[4 SET] Current session id: " . $session_alt->getSessionId() . "
"; print "[4 SET] Current session auto write close: " . ($session_alt->checkAutoWriteClose() ? 'Yes' : 'No') . "
"; print "[START AGAIN] Current session id: " . $session_alt->getSessionId() . "
"; diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 5b8609de..db937bd8 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -217,6 +217,36 @@ class Login 'path' => '', ]; + // 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; + + /** @var int resync interval time in minutes */ + private const DEFAULT_AUTH_RESYNC_INTERVAL = 5 * 60; + /** @var int the session max garbage collection life time */ + // private const DEFAULT_SESSION_GC_MAXLIFETIME = ; + private int $default_session_gc_maxlifetime; + /** @var int in how many minutes an auth resync is done */ + private int $auth_resync_interval; + /** @var bool set the enhanced header security */ + private bool $header_enhance_security = false; + /** @var \CoreLibs\Logging\Logging logger */ public \CoreLibs\Logging\Logging $log; /** @var \CoreLibs\DB\IO database */ @@ -248,156 +278,19 @@ class Login // attach session class $this->session = $session; + $this->default_session_gc_maxlifetime = (int)ini_get("session.gc_maxlifetime"); + // set and check options if (false === $this->loginSetOptions($options)) { // on failure, exit echo "Could not set options"; $this->loginTerminate('Could not set options', 3000); } - - // string key, msg: string, flag: e (error), o (ok) - $this->login_error_msg = [ - '0' => [ - 'msg' => 'No error', - 'flag' => 'o' - ], - // actually obsolete - '100' => [ - 'msg' => '[EUCUUID] set from GET/POST!', - 'flag' => 'e', - ], - // query errors - '1009' => [ - 'msg' => 'Login query reading failed', - 'flag' => 'e', - ], - // user not found - '1010' => [ - 'msg' => 'Login Failed - Wrong Username or Password', - 'flag' => 'e' - ], - // blowfish password wrong - /* '1011' => [ - 'msg' => 'Login Failed - Wrong Username or Password', - 'flag' => 'e' - ], */ - // fallback md5 password wrong - '1012' => [ - 'msg' => 'Login Failed - Wrong Username or Password', - 'flag' => 'e' - ], - // new password_hash wrong - '1013' => [ - 'msg' => 'Login Failed - Wrong Username or Password', - 'flag' => 'e' - ], - '1101' => [ - 'msg' => 'Login Failed - Login User ID must be validated', - 'flag' => 'e' - ], - '1102' => [ - 'msg' => 'Login Failed - Login User ID is outside valid date range', - 'flag' => 'e' - ], - '102' => [ - 'msg' => 'Login Failed - Please enter username and password', - 'flag' => 'e' - ], - '103' => [ - 'msg' => 'You do not have the rights to access this Page', - 'flag' => 'e' - ], - '104' => [ - 'msg' => 'Login Failed - User not enabled', - 'flag' => 'e' - ], - '105' => [ - 'msg' => 'Login Failed - User is locked', - 'flag' => 'e' - ], - '106' => [ - 'msg' => 'Login Failed - User is deleted', - 'flag' => 'e' - ], - '107' => [ - 'msg' => 'Login Failed - User in locked via date period', - 'flag' => 'e' - ], - '108' => [ - 'msg' => 'Login Failed - User is locked via Login User ID', - 'flag' => 'e' - ], - '109' => [ - 'msg' => 'Check permission query reading failed', - 'flag' => 'e' - ], - // actually this is an illegal user, but I mask it - '220' => [ - 'msg' => 'Password change - The user could not be found', - 'flag' => 'e' - ], - '200' => [ - 'msg' => 'Password change - Please enter username and old password', - 'flag' => 'e' - ], - '201' => [ - 'msg' => 'Password change - The user could not be found', - 'flag' => 'e' - ], - '202' => [ - 'msg' => 'Password change - The old password is not correct', - 'flag' => 'e' - ], - '203' => [ - 'msg' => 'Password change - Please fill out both new password fields', - 'flag' => 'e' - ], - '204' => [ - 'msg' => 'Password change - The new passwords do not match', - 'flag' => 'e' - ], - // we should also not here WHAT is valid - '205' => [ - 'msg' => 'Password change - The new password is not in a valid format', - 'flag' => 'e' - ], - // for OK password change - '300' => [ - 'msg' => 'Password change successful', - 'flag' => 'o' - ], - // this is bad bad error - '9999' => [ - 'msg' => 'Necessary crypt engine could not be found. Login is impossible', - 'flag' => 'e' - ], - ]; - - // read the current edit_access_right list into an array - $q = <<= 0 - ORDER BY - level - SQL; - while (is_array($res = $this->db->dbReturn($q))) { - // level to description format (numeric) - $this->default_acl_list[$res['level']] = [ - 'type' => $res['type'], - 'name' => $res['name'] - ]; - $this->default_acl_list_type[(string)$res['type']] = (int)$res['level']; - } - // write that into the session - $this->session->setMany([ - 'LOGIN_DEFAULT_ACL_LIST' => $this->default_acl_list, - 'LOGIN_DEFAULT_ACL_LIST_TYPE' => $this->default_acl_list_type, - ]); - + // init error array + $this->loginInitErrorMessages(); + // acess right list + $this->loginLoadAccessRightList(); + // log allowed write flags $this->loginSetEditLogWriteTypeAvailable(); // this will be deprecated @@ -425,6 +318,7 @@ class Login } else { $this->log->critical($message, ['code' => $code]); } + // TODO throw error and not exit exit($code); } @@ -577,6 +471,20 @@ class Login } $this->password_forgot = $options['forgot_flow']; + // sync _SESSION acl settings + if ( + !isset($options['auth_resync_interval']) || + !is_numeric($options['auth_resync_interval']) || + $options['auth_resync_interval'] < 0 || + $options['auth_resync_interval'] > $this->default_session_gc_maxlifetime + ) { + // default 5 minutues + $options['auth_resync_interval'] = self::DEFAULT_AUTH_RESYNC_INTERVAL; + } else { + $options['auth_resync_interval'] = (int)$options['auth_resync_interval']; + } + $this->auth_resync_interval = $options['auth_resync_interval']; + // *** LANGUAGE // LANG: LOCALE PATH if (empty($options['locale_path'])) { @@ -631,12 +539,210 @@ class Login $options['site_encoding'] = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ? SITE_ENCODING : 'UTF-8'; } + // set enhancded security flag + if ( + empty($options['enhanced_security']) || + !is_bool($options['enhanced_security']) + ) { + $options['enhanced_security'] = true; + } + $this->header_enhance_security = $options['enhanced_security']; // write array to options $this->options = $options; return true; } + /** + * sets the login error message array + * + * @return void + */ + private function loginInitErrorMessages() + { + // string key, msg: string, flag: e (error), o (ok) + $this->login_error_msg = [ + '0' => [ + 'msg' => 'No error', + 'flag' => 'o' + ], + // actually obsolete + '100' => [ + 'msg' => '[EUCUUID] set from GET/POST!', + 'flag' => 'e', + ], + // query errors + '1009' => [ + 'msg' => 'Login query reading failed', + 'flag' => 'e', + ], + // user not found + '1010' => [ + 'msg' => 'Login Failed - Wrong Username or Password', + 'flag' => 'e' + ], + // general login error + '1011' => [ + 'msg' => 'Login Failed - General authentication error', + 'flag' => 'e' + ], + // fallback md5 password wrong + '1012' => [ + 'msg' => 'Login Failed - Wrong Username or Password', + 'flag' => 'e' + ], + // new password_hash wrong + '1013' => [ + 'msg' => 'Login Failed - Wrong Username or Password', + 'flag' => 'e' + ], + '1101' => [ + 'msg' => 'Login Failed - Login User ID must be validated', + 'flag' => 'e' + ], + '1102' => [ + 'msg' => 'Login Failed - Login User ID is outside valid date range', + 'flag' => 'e' + ], + '102' => [ + 'msg' => 'Login Failed - Please enter username and password', + 'flag' => 'e' + ], + '103' => [ + 'msg' => 'You do not have the rights to access this Page', + 'flag' => 'e' + ], + '104' => [ + 'msg' => 'Login Failed - User not enabled', + 'flag' => 'e' + ], + '105' => [ + 'msg' => 'Login Failed - User is locked', + 'flag' => 'e' + ], + '106' => [ + 'msg' => 'Login Failed - User is deleted', + 'flag' => 'e' + ], + '107' => [ + 'msg' => 'Login Failed - User in locked via date period', + 'flag' => 'e' + ], + '108' => [ + 'msg' => 'Login Failed - User is locked via Login User ID', + 'flag' => 'e' + ], + '109' => [ + 'msg' => 'Check permission query reading failed', + 'flag' => 'e' + ], + '110' => [ + 'msg' => 'Forced logout', + 'flag' => '', + ], + // actually this is an illegal user, but I mask it + '220' => [ + 'msg' => 'Password change - The user could not be found', + 'flag' => 'e' + ], + '200' => [ + 'msg' => 'Password change - Please enter username and old password', + 'flag' => 'e' + ], + '201' => [ + 'msg' => 'Password change - The user could not be found', + 'flag' => 'e' + ], + '202' => [ + 'msg' => 'Password change - The old password is not correct', + 'flag' => 'e' + ], + '203' => [ + 'msg' => 'Password change - Please fill out both new password fields', + 'flag' => 'e' + ], + '204' => [ + 'msg' => 'Password change - The new passwords do not match', + 'flag' => 'e' + ], + // we should also not here WHAT is valid + '205' => [ + 'msg' => 'Password change - The new password is not in a valid format', + 'flag' => 'e' + ], + // for OK password change + '300' => [ + 'msg' => 'Password change successful', + 'flag' => 'o' + ], + // this is bad bad error + '9999' => [ + 'msg' => 'Necessary crypt engine could not be found. Login is impossible', + 'flag' => 'e' + ], + ]; + } + + /** + * loads the access right list from the database + * + * @return void + */ + private function loginLoadAccessRightList(): void + { + // read the current edit_access_right list into an array + $q = <<= 0 + ORDER BY + level + SQL; + while (is_array($res = $this->db->dbReturn($q))) { + // level to description format (numeric) + $this->default_acl_list[$res['level']] = [ + 'type' => $res['type'], + 'name' => $res['name'] + ]; + $this->default_acl_list_type[(string)$res['type']] = (int)$res['level']; + } + // write that into the session + $this->session->setMany([ + 'LOGIN_DEFAULT_ACL_LIST' => $this->default_acl_list, + 'LOGIN_DEFAULT_ACL_LIST_TYPE' => $this->default_acl_list_type, + ]); + } + + /** + * Improves the application's security over HTTP(S) by setting specific headers + * + * @return void + */ + protected function loginEnhanceHttpSecurity(): void + { + // skip if not wanted + if (!$this->header_enhance_security) { + return; + } + // remove exposure of PHP version (at least where possible) + header_remove('X-Powered-By'); + // if the user is signed in + if ($this->permission_okay) { + // prevent clickjacking + header('X-Frame-Options: sameorigin'); + // prevent content sniffing (MIME sniffing) + header('X-Content-Type-Options: nosniff'); + + // disable caching of potentially sensitive data + header('Cache-Control: no-store, no-cache, must-revalidate', true); + header('Expires: Thu, 19 Nov 1981 00:00:00 GMT', true); + header('Pragma: no-cache', true); + } + } + // MARK: validation checks /** @@ -649,6 +755,7 @@ class Login * @param int $locked Locked because of too many invalid passwords * @param int $locked_period Locked because of time period set * @param int $login_user_id_locked Locked from using Login User Id + * @param int $force_logout Force logout counter, if higher than session, permission is false * @return bool */ private function loginValidationCheck( @@ -656,7 +763,8 @@ class Login int $enabled, int $locked, int $locked_period, - int $login_user_id_locked + int $login_user_id_locked, + int $force_logout ): bool { $validation = false; if ($deleted) { @@ -674,6 +782,8 @@ class Login } elseif ($login_user_id_locked) { // user is locked, either set or auto set $this->login_error = 108; + } elseif ($force_logout > $this->session->get('LOGIN_FORCE_LOGOUT')) { + $this->login_error = 110; } else { $validation = true; } @@ -757,7 +867,112 @@ class Login return $login_id_ok; } - // MARK: login user action + /** + * write error data for login errors + * + * @param array $res + * @return void + */ + private function loginWriteLoginError(array $res) + { + if (!$this->login_error) { + return; + } + $login_error_date_first = ''; + if ($res['login_error_count'] == 0) { + $login_error_date_first = ", login_error_date_first = NOW()"; + } + // update login error count for this user + $q = <<db->dbExecParams( + str_replace('{LOGIN_ERROR_SQL}', $login_error_date_first, $q), + [$res['edit_user_id']] + ); + // totally lock the user if error max is reached + if ( + $this->max_login_error_count != -1 && + $res['login_error_count'] + 1 > $this->max_login_error_count + ) { + // do some alert reporting in case this error is too big + // if strict is set, lock this user + // this needs manual unlocking by an admin user + if ($res['strict'] && !in_array($this->username, $this->lock_deny_users)) { + $q = << $res + * @return void + */ + private function loginSetEditUserUidData(array $res) + { + // normal user processing + // set class var and session var + $this->edit_user_id = (int)$res['edit_user_id']; + $this->edit_user_cuid = (string)$res['cuid']; + $this->edit_user_cuuid = (string)$res['cuuid']; + $this->session->setMany([ + 'LOGIN_EUID' => $this->edit_user_id, + 'LOGIN_EUCUID' => $this->edit_user_cuid, + 'LOGIN_EUCUUID' => $this->edit_user_cuuid, + ]); + } + + /** + * check for re-loading of ACL data after a period of time + * or if any of the core session vars is not set + * + * @return void + */ + private function loginAuthResync() + { + if (!$this->session->get('LOGIN_LAST_AUTH_RESYNC')) { + $this->session->set('LOGIN_LAST_AUTH_RESYNC', 0); + } + // reauth on missing session vars and timed out re-sync interval + $mandatory_session_vars = [ + 'LOGIN_USER_NAME', 'LOGIN_GROUP_NAME', 'LOGIN_EUCUID', 'LOGIN_EUCUUID', + 'LOGIN_USER_ADDITIONAL_ACL', 'LOGIN_GROUP_ADDITIONAL_ACL', + 'LOGIN_ADMIN', 'LOGIN_GROUP_ACL_LEVEL', 'LOGIN_PAGES_ACL_LEVEL', 'LOGIN_USER_ACL_LEVEL', + 'LOGIN_UNIT', 'LOGIN_UNIT_DEFAULT_EACUID' + ]; + $force_reauth = false; + foreach ($mandatory_session_vars as $_session_var) { + if (!isset($_SESSION[$_session_var])) { + $force_reauth = true; + break; + } + } + if ( + $this->session->get('LOGIN_LAST_AUTH_RESYNC') + $this->auth_resync_interval <= time() && + $force_reauth == false + ) { + return; + } + if (($res = $this->loginLoadUserData($this->edit_user_cuuid)) === false) { + return; + } + // set the session vars + $this->loginSetSession($res); + } + + // MARK: MAIN LOGIN ACTION /** * if user pressed login button this script is called, @@ -769,6 +984,10 @@ class Login { // if pressed login at least and is not yet loggined in if ($this->edit_user_cuuid || (!$this->login && !$this->login_user_id)) { + // run reload user data based on re-auth timeout, but only if we got a set cuuid + if ($this->edit_user_cuuid) { + $this->loginAuthResync(); + } return; } // if not username AND password where given @@ -778,16 +997,105 @@ class Login $this->permission_okay = false; return; } - // have to get the global stuff here for setting it later - // we have to get the themes in here too + // load user data, abort on error + if (($res = $this->loginLoadUserData()) === false) { + return; + } + // if login errors is half of max errors and the last login error + // was less than 10s ago, forbid any new login try + + // check flow + // - user is enabled + // - user is not locked + // - password is readable + // - encrypted password matches + // - plain password matches + if ( + !$this->loginValidationCheck( + (int)$res['deleted'], + (int)$res['enabled'], + (int)$res['locked'], + (int)$res['locked_period'], + (int)$res['login_user_id_locked'], + (int)$res['force_logout'] + ) + ) { + // error set in method (104, 105, 106, 107, 108) + } elseif ( + empty($this->username) && + !empty($this->login_user_id) && + !$this->loginLoginUserIdCheck( + (int)$res['login_user_id_valid_date'], + (int)$res['login_user_id_revalidate'] + ) + ) { + // check done in loginLoginIdCheck method + // aborts on must revalidate and not valid (date range) + } elseif ( + !empty($this->username) && + !$this->loginPasswordCheck($res['password']) + ) { + // none to be set, set in login password check + // this is not valid password input error here + // all error codes are set in loginPasswordCheck method + // also valid if login_user_id is ok + } else { + // check if the current password is an invalid hash and do a rehash and set password + // $this->debug('LOGIN', 'Hash: '.$res['password'].' -> VERIFY: ' + // .($Password::passwordVerify($this->password, $res['password']) ? 'OK' : 'FAIL') + // .' => HASH: '.(Password::passwordRehashCheck($res['password']) ? 'NEW NEEDED' : 'OK')); + if (Password::passwordRehashCheck($res['password'])) { + // update password hash to new one now + $q = <<db->dbExecParams($q, [ + Password::passwordSet($this->password), + $res['edit_user_id'] + ]); + } + // normal user processing + // set class var and session var + $this->loginSetEditUserUidData($res); + // set the last login time stamp for normal login only (not for reauthenticate) + $this->db->dbExecParams(<<edit_user_id]); + // set the session vars + $this->loginSetSession($res); + } // user was not enabled or other login error + // check for login error and write to the user + $this->loginWriteLoginError($res); + // if there was an login error, show login screen + if ($this->login_error) { + // reset the perm var, to confirm logout + $this->permission_okay = false; + } + } + + /** + * load user data and all connect4ed settings + * + * @param ?string $edit_user_cuuid for re-auth + * @return array|false + */ + private function loginLoadUserData(?string $edit_user_cuuid = null): array|false + { $q = <<login_user_id && empty($this->username))) { + // if login is OK and we have edit_user_cuuid as parameter, then this is internal re-auth + // else login_user_id OR password must be given + if (!empty($edit_user_cuuid)) { + $replace_string = 'eu.cuuid = $1'; + $params = [$this->edit_user_cuuid]; + } elseif (!empty($this->login_user_id) && empty($this->username)) { // check with login id if set and NO username $replace_string = 'eu.login_user_id = $1'; $params = [$this->login_user_id]; @@ -874,366 +1184,288 @@ class Login if (!empty($this->db->dbGetLastError())) { $this->login_error = 1009; $this->permission_okay = false; - return; + return false; } elseif (!is_array($res)) { // username is wrong, but we throw for wrong username // and wrong password the same error - $this->login_error = 1010; + // unless with have edit user cuuid set then we run an general ACL error + if (empty($edit_user_cuuid)) { + $this->login_error = 1010; + } else { + $this->login_error = 1011; + } $this->permission_okay = false; + return false; + } + return $res; + } + + // MARK: login set all session variables + + /** + * set all the _SESSION variables + * + * @param array $res user data loaded query result + * @return void + */ + private function loginSetSession(array $res): void + { + // user has permission to THIS page + if ($this->login_error != 0) { return; } - // if login errors is half of max errors and the last login error - // was less than 10s ago, forbid any new login try - - // check flow - // - user is enabled - // - user is not locked - // - password is readable - // - encrypted password matches - // - plain password matches + // set the dit group id + $edit_group_id = $res["edit_group_id"]; + $edit_user_id = (int)$res['edit_user_id']; + // update last revalidate flag if ( - !$this->loginValidationCheck( - (int)$res['deleted'], - (int)$res['enabled'], - (int)$res['locked'], - (int)$res['locked_period'], - (int)$res['login_user_id_locked'] - ) + !empty($res['login_user_id']) && + !empty($this->username) && !empty($this->password) ) { - // error set in method (104, 105, 106, 107, 108) - } elseif ( - empty($this->username) && - !empty($this->login_user_id) && - !$this->loginLoginUserIdCheck( - (int)$res['login_user_id_valid_date'], - (int)$res['login_user_id_revalidate'] - ) + $q = <<db->dbExecParams($q, [$edit_user_id]); + } + $locale = $res['locale'] ?? 'en'; + $encoding = $res['encoding'] ?? 'UTF-8'; + $this->session->setMany([ + // now set all session vars and read page permissions + // DEBUG flag is deprecated + // 'DEBUG_ALL' => $this->db->dbBoolean($res['debug']), + // 'DB_DEBUG' => $this->db->dbBoolean($res['db_debug']), + // login timestamp + 'LOGIN_LAST_AUTH_RESYNC' => time(), + // current forced logout counter + 'LOGIN_FORCE_LOGOUT' => $res['force_logout'], + // general info for user logged in + 'LOGIN_USER_NAME' => $res['username'], + 'LOGIN_EMAIL' => $res['email'], + 'LOGIN_ADMIN' => $res['admin'], + 'LOGIN_GROUP_NAME' => $res['edit_group_name'], + 'LOGIN_USER_ACL_LEVEL' => $res['user_level'], + 'LOGIN_USER_ACL_TYPE' => $res['user_type'], + 'LOGIN_USER_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['user_additional_acl']), + 'LOGIN_GROUP_ACL_LEVEL' => $res['group_level'], + 'LOGIN_GROUP_ACL_TYPE' => $res['group_type'], + 'LOGIN_GROUP_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['group_additional_acl']), + // deprecated TEMPLATE setting + // 'TEMPLATE' => $res['template'] ? $res['template'] : '', + 'LOGIN_HEADER_COLOR' => !empty($res['second_header_color']) ? + $res['second_header_color'] : + $res['first_header_color'], + // LANGUAGE/LOCALE/ENCODING: + // 'LOGIN_LANG' => $locale, + 'DEFAULT_CHARSET' => $encoding, + 'DEFAULT_LOCALE' => $locale . '.' . strtoupper($encoding), + 'DEFAULT_LANG' => $locale . '_' . strtolower(str_replace('-', '', $encoding)) + ]); + // missing # before, this is for legacy data, will be deprecated + if ( + !empty($this->session->get('LOGIN_HEADER_COLOR')) && + preg_match("/^[\dA-Fa-f]{6,8}$/", $this->session->get('LOGIN_HEADER_COLOR')) ) { - // check done in loginLoginIdCheck method - // aborts on must revalidate and not valid (date range) - } elseif ( - !empty($this->username) && - !$this->loginPasswordCheck($res['password']) - ) { - // none to be set, set in login password check - // this is not valid password input error here - // all error codes are set in loginPasswordCheck method - // also valid if login_user_id is ok - } else { - // check if the current password is an invalid hash and do a rehash and set password - // $this->debug('LOGIN', 'Hash: '.$res['password'].' -> VERIFY: ' - // .($Password::passwordVerify($this->password, $res['password']) ? 'OK' : 'FAIL') - // .' => HASH: '.(Password::passwordRehashCheck($res['password']) ? 'NEW NEEDED' : 'OK')); - if (Password::passwordRehashCheck($res['password'])) { - // update password hash to new one now - $q = <<db->dbExecParams($q, [ - Password::passwordSet($this->password), - $res['edit_user_id'] - ]); - } - // normal user processing - // set class var and session var - $this->edit_user_id = (int)$res['edit_user_id']; - $this->edit_user_cuid = (string)$res['cuid']; - $this->edit_user_cuuid = (string)$res['cuuid']; - $this->session->setMany([ - 'LOGIN_EUID' => $this->edit_user_id, // DEPRECATED - 'LOGIN_EUCUID' => $this->edit_user_cuid, - 'LOGIN_EUCUUID' => $this->edit_user_cuuid, - ]); - // check if user is okay - $this->loginCheckPermissions(); - if ($this->login_error == 0) { - // set the dit group id - $edit_group_id = $res["edit_group_id"]; - // update last revalidate flag - if ( - !empty($res['login_user_id']) && - !empty($this->username) && !empty($this->password) - ) { - $q = <<db->dbExecParams($q, [$this->edit_user_id]); - } - $locale = $res['locale'] ?? 'en'; - $encoding = $res['encoding'] ?? 'UTF-8'; - $this->session->setMany([ - // now set all session vars and read page permissions - // DEBUG flag is deprecated - // 'DEBUG_ALL' => $this->db->dbBoolean($res['debug']), - // 'DB_DEBUG' => $this->db->dbBoolean($res['db_debug']), - // general info for user logged in - 'LOGIN_USER_NAME' => $res['username'], - 'LOGIN_ADMIN' => $res['admin'], - 'LOGIN_GROUP_NAME' => $res['edit_group_name'], - 'LOGIN_USER_ACL_LEVEL' => $res['user_level'], - 'LOGIN_USER_ACL_TYPE' => $res['user_type'], - 'LOGIN_USER_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['user_additional_acl']), - 'LOGIN_GROUP_ACL_LEVEL' => $res['group_level'], - 'LOGIN_GROUP_ACL_TYPE' => $res['group_type'], - 'LOGIN_GROUP_ADDITIONAL_ACL' => Json::jsonConvertToArray($res['group_additional_acl']), - // deprecated TEMPLATE setting - // 'TEMPLATE' => $res['template'] ? $res['template'] : '', - 'LOGIN_HEADER_COLOR' => !empty($res['second_header_color']) ? - $res['second_header_color'] : - $res['first_header_color'], - // LANGUAGE/LOCALE/ENCODING: - // 'LOGIN_LANG' => $locale, - 'DEFAULT_CHARSET' => $encoding, - 'DEFAULT_LOCALE' => $locale . '.' . strtoupper($encoding), - 'DEFAULT_LANG' => $locale . '_' . strtolower(str_replace('-', '', $encoding)) - ]); - // missing # before, this is for legacy data, will be deprecated - if ( - !empty($this->session->get('LOGIN_HEADER_COLOR')) && - preg_match("/^[\dA-Fa-f]{6,8}$/", $this->session->get('LOGIN_HEADER_COLOR')) - ) { - $this->session->set('LOGIN_HEADER_COLOR', '#' . $this->session->get('LOGIN_HEADER_COLOR')); - } - // TODO: make sure that header color is valid: - // # + 6 hex - // # + 8 hex (alpha) - // rgb(), rgba(), hsl(), hsla() - // rgb: nnn.n for each - // hsl: nnn.n for first, nnn.n% for 2nd, 3rd - // Check\Colors::validateColor() - // reset any login error count for this user - if ($res['login_error_count'] > 0) { - $q = <<db->dbExecParams($q, [$this->edit_user_id]); - } - $edit_page_ids = []; - $pages = []; - $pages_acl = []; - // set pages access - $q = <<db->dbReturnParams($q, [$edit_group_id]))) { - // page id array for sub data readout - $edit_page_ids[$res['edit_page_id']] = $res['cuid']; - // create the array for pages - $pages[$res['cuid']] = [ - 'edit_page_id' => $res['edit_page_id'], - 'cuid' => $res['cuid'], - 'cuuid' => $res['cuuid'], - // for reference of content data on a differen page - 'content_alias_uid' => $res['content_alias_uid'], - 'hostname' => $res['hostname'], - 'filename' => $res['filename'], - 'page_name' => $res['edit_page_name'], - 'order' => $res['edit_page_order'], - 'menu' => $res['menu'], - 'popup' => $res['popup'], - 'popup_x' => $res['popup_x'], - 'popup_y' => $res['popup_y'], - 'online' => $res['online'], - 'acl_level' => $res['level'], - 'acl_type' => $res['type'], - 'query' => [], - 'visible' => [] - ]; - // make reference filename -> level - $pages_acl[$res['filename']] = $res['level']; - } // for each page - // edit page id params - $params = ['{' . join(',', array_keys($edit_page_ids)) . '}']; - // get the visible groups for all pages and write them to the pages - $q = <<db->dbReturnParams($q, $params))) { - $pages[$edit_page_ids[$res['edit_page_id']]]['visible'][$res['name']] = $res['flag']; - } - // get the same for the query strings - $q = <<db->dbReturnParams($q, $params))) { - $pages[$edit_page_ids[$res['edit_page_id']]]['query'][] = [ - 'name' => $res['name'], - 'value' => $res['value'], - 'dynamic' => $res['dynamic'] - ]; - } - // get the page content and add them to the page - $q = <<db->dbReturnParams($q, $params))) { - $pages[$edit_page_ids[$res['edit_page_id']]]['content'][$res['uid']] = [ - 'name' => $res['name'], - 'uid' => $res['uid'], - 'cuid' => $res['cuid'], - 'cuuid' => $res['cuuid'], - 'online' => $res['online'], - 'order' => $res['order_number'], - // access name and level - 'acl_type' => $res['type'], - 'acl_level' => $res['level'] - ]; - } - // write back the pages data to the output array - $this->session->setMany([ - 'LOGIN_PAGES' => $pages, - 'LOGIN_PAGES_ACL_LEVEL' => $pages_acl, - ]); - // load the edit_access user rights - $q = <<db->dbReturnParams($q, [$this->edit_user_id]))) { - // read edit access data fields and drop them into the unit access array - $q_sub = <<db->dbReturnParams($q_sub, [$res['edit_access_id']]))) { - $ea_data[$res_sub['name']] = $res_sub['value']; - } - // build master unit array - $unit_access_cuid[$res['cuid']] = [ - 'id' => (int)$res['edit_access_id'], // DEPRECATED - 'cuuid' => $res['cuuid'], - 'acl_level' => $res['level'], - 'acl_type' => $res['type'], - 'name' => $res['name'], - 'uid' => $res['uid'], - 'color' => $res['color'], - 'default' => $res['edit_default'], - 'additional_acl' => Json::jsonConvertToArray($res['additional_acl']), - 'data' => $ea_data - ]; - $unit_access_eaid[$res['edit_access_id']] = [ - 'cuid' => $res['cuid'], - ]; - // set the default unit - if ($res['edit_default']) { - $this->session->set('LOGIN_UNIT_DEFAULT_EAID', (int)$res['edit_access_id']); // DEPRECATED - $this->session->set('LOGIN_UNIT_DEFAULT_EACUID', (int)$res['cuid']); - } - $unit_uid_lookup[$res['uid']] = $res['edit_access_id']; // DEPRECATED - $unit_cuid_lookup[$res['uid']] = $res['cuid']; - // sub arrays for simple access - array_push($eaid, $res['edit_access_id']); - array_push($eacuid, $res['cuid']); - $unit_acl[$res['cuid']] = $res['level']; - } - $this->session->setMany([ - 'LOGIN_UNIT_UID' => $unit_uid_lookup, // DEPRECATED - 'LOGIN_UNIT_CUID' => $unit_cuid_lookup, - 'LOGIN_UNIT' => $unit_access_cuid, - 'LOGIN_UNIT_LEGACY' => $unit_access_eaid, // DEPRECATED - 'LOGIN_UNIT_ACL_LEVEL' => $unit_acl, - 'LOGIN_EAID' => $eaid, // DEPRECATED - 'LOGIN_EACUID' => $eacuid, - ]); - } // user has permission to THIS page - } // user was not enabled or other login error - if ($this->login_error && is_array($res)) { - $login_error_date_first = ''; - if ($res['login_error_count'] == 0) { - $login_error_date_first = ", login_error_date_first = NOW()"; - } - // update login error count for this user + $this->session->set('LOGIN_HEADER_COLOR', '#' . $this->session->get('LOGIN_HEADER_COLOR')); + } + // TODO: make sure that header color is valid: + // # + 6 hex + // # + 8 hex (alpha) + // rgb(), rgba(), hsl(), hsla() + // rgb: nnn.n for each + // hsl: nnn.n for first, nnn.n% for 2nd, 3rd + // Check\Colors::validateColor() + // reset any login error count for this user + if ($res['login_error_count'] > 0) { $q = <<db->dbExecParams( - str_replace('{LOGIN_ERROR_SQL}', $login_error_date_first, $q), - [$res['edit_user_id']] - ); - // totally lock the user if error max is reached - if ( - $this->max_login_error_count != -1 && - $res['login_error_count'] + 1 > $this->max_login_error_count - ) { - // do some alert reporting in case this error is too big - // if strict is set, lock this user - // this needs manual unlocking by an admin user - if ($res['strict'] && !in_array($this->username, $this->lock_deny_users)) { - $q = <<db->dbExecParams($q, [$edit_user_id]); + } + $edit_page_ids = []; + $pages = []; + $pages_acl = []; + // set pages access + $q = <<db->dbReturnParams($q, [$edit_group_id]))) { + // page id array for sub data readout + $edit_page_ids[$res['edit_page_id']] = $res['cuid']; + // create the array for pages + $pages[$res['cuid']] = [ + 'edit_page_id' => $res['edit_page_id'], + 'cuid' => $res['cuid'], + 'cuuid' => $res['cuuid'], + // for reference of content data on a differen page + 'content_alias_uid' => $res['content_alias_uid'], + 'hostname' => $res['hostname'], + 'filename' => $res['filename'], + 'page_name' => $res['edit_page_name'], + 'order' => $res['edit_page_order'], + 'menu' => $res['menu'], + 'popup' => $res['popup'], + 'popup_x' => $res['popup_x'], + 'popup_y' => $res['popup_y'], + 'online' => $res['online'], + 'acl_level' => $res['level'], + 'acl_type' => $res['type'], + 'query' => [], + 'visible' => [] + ]; + // make reference filename -> level + $pages_acl[$res['filename']] = $res['level']; + } // for each page + // edit page id params + $params = ['{' . join(',', array_keys($edit_page_ids)) . '}']; + // get the visible groups for all pages and write them to the pages + $q = <<db->dbReturnParams($q, $params))) { + $pages[$edit_page_ids[$res['edit_page_id']]]['visible'][$res['name']] = $res['flag']; + } + // get the same for the query strings + $q = <<db->dbReturnParams($q, $params))) { + $pages[$edit_page_ids[$res['edit_page_id']]]['query'][] = [ + 'name' => $res['name'], + 'value' => $res['value'], + 'dynamic' => $res['dynamic'] + ]; + } + // get the page content and add them to the page + $q = <<db->dbReturnParams($q, $params))) { + $pages[$edit_page_ids[$res['edit_page_id']]]['content'][$res['uid']] = [ + 'name' => $res['name'], + 'uid' => $res['uid'], + 'cuid' => $res['cuid'], + 'cuuid' => $res['cuuid'], + 'online' => $res['online'], + 'order' => $res['order_number'], + // access name and level + 'acl_type' => $res['type'], + 'acl_level' => $res['level'] + ]; + } + // write back the pages data to the output array + $this->session->setMany([ + 'LOGIN_PAGES' => $pages, + 'LOGIN_PAGES_ACL_LEVEL' => $pages_acl, + ]); + // load the edit_access user rights + $q = <<db->dbReturnParams($q, [$edit_user_id]))) { + // read edit access data fields and drop them into the unit access array + $q_sub = <<db->dbReturnParams($q_sub, [$res['edit_access_id']]))) { + $ea_data[$res_sub['name']] = $res_sub['value']; } + // build master unit array + $unit_access_cuid[$res['cuid']] = [ + 'id' => (int)$res['edit_access_id'], // DEPRECATED + 'cuuid' => $res['cuuid'], + 'acl_level' => $res['level'], + 'acl_type' => $res['type'], + 'name' => $res['name'], + 'uid' => $res['uid'], + 'color' => $res['color'], + 'default' => $res['edit_default'], + 'additional_acl' => Json::jsonConvertToArray($res['additional_acl']), + 'data' => $ea_data + ]; + $unit_access_eaid[$res['edit_access_id']] = [ + 'cuid' => $res['cuid'], + ]; + // set the default unit + $this->session->setMany([ + 'LOGIN_UNIT_DEFAULT_EAID' => null, + 'LOGIN_UNIT_DEFAULT_EACUID' => null, + ]); + if ($res['edit_default']) { + $this->session->set('LOGIN_UNIT_DEFAULT_EAID', (int)$res['edit_access_id']); // DEPRECATED + $this->session->set('LOGIN_UNIT_DEFAULT_EACUID', (int)$res['cuid']); + } + $unit_uid_lookup[$res['uid']] = $res['edit_access_id']; // DEPRECATED + $unit_cuid_lookup[$res['uid']] = $res['cuid']; + // sub arrays for simple access + array_push($eaid, $res['edit_access_id']); + array_push($eacuid, $res['cuid']); + $unit_acl[$res['cuid']] = $res['level']; } - // if there was an login error, show login screen - if ($this->login_error) { - // reset the perm var, to confirm logout - $this->permission_okay = false; - } + $this->session->setMany([ + 'LOGIN_UNIT_UID' => $unit_uid_lookup, // DEPRECATED + 'LOGIN_UNIT_CUID' => $unit_cuid_lookup, + 'LOGIN_UNIT' => $unit_access_cuid, + 'LOGIN_UNIT_LEGACY' => $unit_access_eaid, // DEPRECATED + 'LOGIN_UNIT_ACL_LEVEL' => $unit_acl, + 'LOGIN_EAID' => $eaid, // DEPRECATED + 'LOGIN_EACUID' => $eacuid, + ]); } // MARK: login set ACL @@ -1361,7 +1593,7 @@ class Login $this->acl['show_ea_extra'] = false; } // set the default edit access - $this->acl['default_edit_access'] = $_SESSION['UNIT_DEFAULT'] ?? null; + $this->acl['default_edit_access'] = $_SESSION['LOGIN_UNIT_DEFAULT_EACUID']; // integrate the type acl list, but only for the keyword -> level $this->acl['min'] = $this->default_acl_list_type; // set the full acl list too (lookup level number and get level data) @@ -2198,7 +2430,7 @@ HTML; // row 2 $_SERVER["REMOTE_ADDR"] ?? null, Json::jsonConvertArrayTo([ - 'REMOTE_ADDR' => $_SERVER["REMOTE_ADDR"], + 'REMOTE_ADDR' => $_SERVER["REMOTE_ADDR"] ?? null, 'HTTP_X_FORWARDED_FOR' => !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']) : [], @@ -2262,7 +2494,7 @@ HTML; // **** PUBLIC INTERNAL // ************************************************************************* - // MARK: PUBLIC LOGIN CALL + // MARK: MASTER PUBLIC LOGIN CALL /** * Main call that needs to be run to actaully check for login @@ -2355,10 +2587,12 @@ HTML; // if username & password & !$euid start login $this->loginLoginUser(); - // checks if $euid given check if user is okay for that side + // checks if $euid given check if user is okay for that site $this->loginCheckPermissions(); - // logsout user + // logout user $this->loginLogoutUser(); + // set headers for enhanced security + $this->loginEnhanceHttpSecurity(); // ** LANGUAGE SET AFTER LOGIN ** $this->loginSetLocale(); // load translator @@ -2731,7 +2965,7 @@ HTML; } $q = <<login_error = 103; } // set all the internal vars - $this->edit_user_id = (int)$res['edit_user_id']; - $this->edit_user_cuid = (string)$res['cuid']; - $this->edit_user_cuuid = (string)$res['cuuid']; - $this->session->setMany([ - 'LOGIN_EUID' => $this->edit_user_id, // DEPRECATED - 'LOGIN_EUCUID' => $this->edit_user_cuid, - 'LOGIN_EUCUUID' => $this->edit_user_cuuid, - ]); + $this->loginSetEditUserUidData($res); // if called from public, so we can check if the permissions are ok return $this->permission_okay; } diff --git a/www/lib/CoreLibs/Create/Session.php b/www/lib/CoreLibs/Create/Session.php index 6873ee27..0f2e4a38 100644 --- a/www/lib/CoreLibs/Create/Session.php +++ b/www/lib/CoreLibs/Create/Session.php @@ -21,21 +21,107 @@ class Session 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 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 * then no auto init is run * * @param string $session_name if set and not empty, will start session + * @param array $options */ - public function __construct(string $session_name, bool $auto_write_close = false) - { + public function __construct( + string $session_name, + array $options = [] + ) { + $this->setOptions($options); $this->initSession($session_name); - $this->auto_write_close = $auto_write_close; } // MARK: private methods + /** + * set session class options + * + * @param array $options + * @return void + */ + private function setOptions(array $options): void + { + if ( + !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); + } + /** * Start session * startSession should be called for complete check @@ -72,6 +158,72 @@ class Session return false; } + // 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 * @@ -151,6 +303,13 @@ class 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(), @@ -159,10 +318,12 @@ class Session } // check session id if (false === ($session_id = $this->getSessionIdCall())) { - throw new \UnexpectedValueException('[SESSION] getSessionId did not return a session id', 6); + 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(); From d1c461143178d57c72a573a8f5b49cccdf226892 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 11 Dec 2024 21:06:59 +0900 Subject: [PATCH 20/28] Indent fix for ACL Login --- www/lib/CoreLibs/ACL/Login.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index db937bd8..6fb11dd4 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -260,10 +260,10 @@ class Login * constructor, does ALL, opens db, works through connection checks, * finishes itself * - * @param \CoreLibs\DB\IO $db Database connection class - * @param \CoreLibs\Logging\Logging $log Logging class - * @param \CoreLibs\Create\Session $session Session interface class - * @param array $options Login ACL settings + * @param \CoreLibs\DB\IO $db Database connection class + * @param \CoreLibs\Logging\Logging $log Logging class + * @param \CoreLibs\Create\Session $session Session interface class + * @param array $options Login ACL settings */ public function __construct( \CoreLibs\DB\IO $db, From 7d4c9724fe61297f745cdf7fda606574e751baad Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 11 Dec 2024 21:10:00 +0900 Subject: [PATCH 21/28] Fix session options argument declaration for phpstan --- www/lib/CoreLibs/Create/Session.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/lib/CoreLibs/Create/Session.php b/www/lib/CoreLibs/Create/Session.php index 0f2e4a38..b3f9f772 100644 --- a/www/lib/CoreLibs/Create/Session.php +++ b/www/lib/CoreLibs/Create/Session.php @@ -40,7 +40,7 @@ class Session * then no auto init is run * * @param string $session_name if set and not empty, will start session - * @param array $options + * @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options */ public function __construct( string $session_name, @@ -55,7 +55,7 @@ class Session /** * set session class options * - * @param array $options + * @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options * @return void */ private function setOptions(array $options): void From e793c3975b651d775d9df92ed96ea09f075814bb Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 12 Dec 2024 12:02:49 +0900 Subject: [PATCH 22/28] Change all db now to clock_timestamp for triggers --- 4dev/database/function/set_date.sql | 4 +- 4dev/database/function/set_edit_generic.sql | 4 +- 4dev/database/function/set_generic_uid.sql | 4 +- 4dev/database/function/update_function.sql | 19 ------ .../CoreLibsACLLogin_database_create_data.sql | 4 +- www/lib/CoreLibs/ACL/Login.php | 20 ------ www/lib/CoreLibs/ACL/LoginUserStatus.php | 68 +++++++++++++++++++ 7 files changed, 76 insertions(+), 47 deletions(-) delete mode 100644 4dev/database/function/update_function.sql create mode 100644 www/lib/CoreLibs/ACL/LoginUserStatus.php diff --git a/4dev/database/function/set_date.sql b/4dev/database/function/set_date.sql index 408688a1..13a51743 100644 --- a/4dev/database/function/set_date.sql +++ b/4dev/database/function/set_date.sql @@ -5,9 +5,9 @@ RETURNS TRIGGER AS $$ BEGIN IF TG_OP = 'INSERT' THEN - NEW.date_created := 'now'; + NEW.date_created := clock_timestamp(); ELSIF TG_OP = 'UPDATE' THEN - NEW.date_updated := 'now'; + NEW.date_updated := clock_timestamp(); END IF; RETURN NEW; END; diff --git a/4dev/database/function/set_edit_generic.sql b/4dev/database/function/set_edit_generic.sql index 4ec68683..be7519b9 100644 --- a/4dev/database/function/set_edit_generic.sql +++ b/4dev/database/function/set_edit_generic.sql @@ -7,11 +7,11 @@ DECLARE random_length INT = 25; -- that should be long enough BEGIN IF TG_OP = 'INSERT' THEN - NEW.date_created := 'now'; + NEW.date_created := clock_timestamp(); NEW.cuid := random_string(random_length); NEW.cuuid := gen_random_uuid(); ELSIF TG_OP = 'UPDATE' THEN - NEW.date_updated := 'now'; + NEW.date_updated := clock_timestamp(); END IF; RETURN NEW; END; diff --git a/4dev/database/function/set_generic_uid.sql b/4dev/database/function/set_generic_uid.sql index 71c27275..8ad24467 100644 --- a/4dev/database/function/set_generic_uid.sql +++ b/4dev/database/function/set_generic_uid.sql @@ -8,12 +8,12 @@ DECLARE random_length INT = 32; -- long for massive data BEGIN IF TG_OP = 'INSERT' THEN - NEW.date_created := 'now'; + NEW.date_created := clock_timestamp(); IF NEW.uid IS NULL THEN NEW.uid := random_string(random_length); END IF; ELSIF TG_OP = 'UPDATE' THEN - NEW.date_updated := 'now'; + NEW.date_updated := clock_timestamp(); END IF; RETURN NEW; END; diff --git a/4dev/database/function/update_function.sql b/4dev/database/function/update_function.sql deleted file mode 100644 index 80a3dccf..00000000 --- a/4dev/database/function/update_function.sql +++ /dev/null @@ -1,19 +0,0 @@ --- adds the created or updated date tags - --- OLD, DEPRECATED, use set_generic.sql - --- CREATE OR REPLACE FUNCTION set_generic() --- RETURNS TRIGGER AS --- $$ --- BEGIN --- IF TG_OP = 'INSERT' THEN --- NEW.date_created := clock_timestamp(); --- NEW.user_created := current_user; --- ELSIF TG_OP = 'UPDATE' THEN --- NEW.date_updated := clock_timestamp(); --- NEW.user_updated := current_user; --- END IF; --- RETURN NEW; --- END; --- $$ --- LANGUAGE 'plpgsql'; diff --git a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql index e1d1a0cb..7caaf1c3 100644 --- a/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql +++ b/4dev/tests/ACL/database/CoreLibsACLLogin_database_create_data.sql @@ -30,11 +30,11 @@ DECLARE random_length INT = 12; -- that should be long enough BEGIN IF TG_OP = 'INSERT' THEN - NEW.date_created := 'now'; + NEW.date_created := clock_timestamp(); NEW.cuid := random_string(random_length); NEW.cuuid := gen_random_uuid(); ELSIF TG_OP = 'UPDATE' THEN - NEW.date_updated := 'now'; + NEW.date_updated := clock_timestamp(); END IF; RETURN NEW; END; diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 6fb11dd4..365f5e69 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -217,26 +217,6 @@ class Login 'path' => '', ]; - // 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; - /** @var int resync interval time in minutes */ private const DEFAULT_AUTH_RESYNC_INTERVAL = 5 * 60; /** @var int the session max garbage collection life time */ diff --git a/www/lib/CoreLibs/ACL/LoginUserStatus.php b/www/lib/CoreLibs/ACL/LoginUserStatus.php new file mode 100644 index 00000000..fb791abb --- /dev/null +++ b/www/lib/CoreLibs/ACL/LoginUserStatus.php @@ -0,0 +1,68 @@ + + */ + 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__ From 540269e61ffd180e4cabb47baf6e155a0e1f2798 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 12 Dec 2024 19:04:21 +0900 Subject: [PATCH 23/28] Fix update script for now to clock_timestamp --- .../edit_tables_cuid_cuuid_update_add.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql b/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql index ec72ed72..255f0b56 100644 --- a/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql +++ b/4dev/update/20241203_update_edit_tables/edit_tables_cuid_cuuid_update_add.sql @@ -22,11 +22,11 @@ DECLARE random_length INT = 25; -- that should be long enough BEGIN IF TG_OP = 'INSERT' THEN - NEW.date_created := 'now'; + NEW.date_created := clock_timestamp(); NEW.cuid := random_string(random_length); NEW.cuuid := gen_random_uuid(); ELSIF TG_OP = 'UPDATE' THEN - NEW.date_updated := 'now'; + NEW.date_updated := clock_timestamp(); END IF; RETURN NEW; END; From 1e90bb677e502482cf7b57aaf90effcdf20939d5 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 12 Dec 2024 21:07:17 +0900 Subject: [PATCH 24/28] Fix Symmetric encryption with wrong key handling - static call encrypt: do not check pre set key - indirect call: set new if key is different --- ...oreLibsSecuritySymmetricEncryptionTest.php | 167 +++++++++++++++++- .../CoreLibs/Security/SymmetricEncryption.php | 8 +- 2 files changed, 172 insertions(+), 3 deletions(-) diff --git a/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php index d3f502af..d7486501 100644 --- a/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php +++ b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php @@ -56,7 +56,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $decrypted, '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 $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted); @@ -65,7 +82,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $decrypted, 'Class Instance call', ); + } + /** + * test encrypt/decrypt produce correct output + * + * @covers ::generateRandomKey + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerEncryptDecryptSuccess + * @testdox encrypt/decrypt static $input must be $expected [$_dataName] + * + * @param string $input + * @param string $expected + * @return void + */ + public function testEncryptDecryptSuccessStatic(string $input, string $expected): void + { + $key = CreateKey::generateRandomKey(); // test static $encrypted = SymmetricEncryption::encryptKey($input, $key); $decrypted = SymmetricEncryption::decryptKey($encrypted, $key); @@ -114,13 +148,51 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $crypt = new SymmetricEncryption($key); $encrypted = $crypt->encrypt($input); $this->expectExceptionMessage($exception_message); - $crypt->setKey($key); + $crypt->setKey($wrong_key); $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 $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $this->expectExceptionMessage($exception_message); SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted); + } + + /** + * Test decryption with wrong key + * + * @covers ::generateRandomKey + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerEncryptFailed + * @testdox decrypt static with wrong key $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testEncryptFailedStatic(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + $wrong_key = CreateKey::generateRandomKey(); // class static $encrypted = SymmetricEncryption::encryptKey($input, $key); @@ -190,6 +262,56 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase SymmetricEncryption::decryptKey($encrypted, $key); } + /** + * test invalid key provided to decrypt or encrypt + * + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerWrongKey + * @testdox wrong key indirect $key throws $exception_message [$_dataName] + * + * @param string $key + * @param string $exception_message + * @return void + */ + public function testWrongKeyIndirect(string $key, string $exception_message): void + { + $enc_key = CreateKey::generateRandomKey(); + + // class instance + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::getInstance($key)->encrypt('test'); + // we must encrypt valid thing first so we can fail with the wrong key + $encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test'); + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::getInstance($key)->decrypt($encrypted); + } + + /** + * test invalid key provided to decrypt or encrypt + * + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerWrongKey + * @testdox wrong key static $key throws $exception_message [$_dataName] + * + * @param string $key + * @param string $exception_message + * @return void + */ + public function testWrongKeyStatic(string $key, string $exception_message): void + { + $enc_key = CreateKey::generateRandomKey(); + + // class static + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::encryptKey('test', $key); + // we must encrypt valid thing first so we can fail with the wrong key + $encrypted = SymmetricEncryption::encryptKey('test', $enc_key); + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decryptKey($encrypted, $key); + } + /** * Undocumented function * @@ -232,6 +354,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $this->expectExceptionMessage($exception_message); SymmetricEncryption::decryptKey($input, $key); } + + /** + * Undocumented function + * + * @covers ::decrypt + * @dataProvider providerWrongCiphertext + * @testdox too short ciphertext indirect $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testWrongCiphertextIndirect(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + + // class instance + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::getInstance($key)->decrypt($input); + + // class static + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decryptKey($input, $key); + } + + /** + * Undocumented function + * + * @covers ::decrypt + * @dataProvider providerWrongCiphertext + * @testdox too short ciphertext static $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testWrongCiphertextStatic(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + // class static + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decryptKey($input, $key); + } } // __END__ diff --git a/www/lib/CoreLibs/Security/SymmetricEncryption.php b/www/lib/CoreLibs/Security/SymmetricEncryption.php index c12f4b3f..2f8fb75e 100644 --- a/www/lib/CoreLibs/Security/SymmetricEncryption.php +++ b/www/lib/CoreLibs/Security/SymmetricEncryption.php @@ -49,7 +49,11 @@ class SymmetricEncryption */ public static function getInstance(string|null $key = null): self { - if (empty(self::$instance)) { + // new if no instsance or key is different + if ( + empty(self::$instance) || + self::$instance->key != $key + ) { self::$instance = new self($key); } return self::$instance; @@ -130,7 +134,7 @@ class SymmetricEncryption */ private function encryptData(string $message, ?string $key): string { - if (empty($this->key) || $key === null) { + if ($key === null) { throw new \UnexpectedValueException('Key not set'); } $key = $this->createKey($key); From c13934de99cdf63c5f260e702adf441552d77dcb Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 12 Dec 2024 21:09:41 +0900 Subject: [PATCH 25/28] Fix for wrong key handling in Symmetric encryption --- ...oreLibsSecuritySymmetricEncryptionTest.php | 167 +++++++++++++++++- .../CoreLibs/Security/SymmetricEncryption.php | 8 +- 2 files changed, 172 insertions(+), 3 deletions(-) diff --git a/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php index d3f502af..d7486501 100644 --- a/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php +++ b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php @@ -56,7 +56,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $decrypted, '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 $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted); @@ -65,7 +82,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $decrypted, 'Class Instance call', ); + } + /** + * test encrypt/decrypt produce correct output + * + * @covers ::generateRandomKey + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerEncryptDecryptSuccess + * @testdox encrypt/decrypt static $input must be $expected [$_dataName] + * + * @param string $input + * @param string $expected + * @return void + */ + public function testEncryptDecryptSuccessStatic(string $input, string $expected): void + { + $key = CreateKey::generateRandomKey(); // test static $encrypted = SymmetricEncryption::encryptKey($input, $key); $decrypted = SymmetricEncryption::decryptKey($encrypted, $key); @@ -114,13 +148,51 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $crypt = new SymmetricEncryption($key); $encrypted = $crypt->encrypt($input); $this->expectExceptionMessage($exception_message); - $crypt->setKey($key); + $crypt->setKey($wrong_key); $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 $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $this->expectExceptionMessage($exception_message); SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted); + } + + /** + * Test decryption with wrong key + * + * @covers ::generateRandomKey + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerEncryptFailed + * @testdox decrypt static with wrong key $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testEncryptFailedStatic(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + $wrong_key = CreateKey::generateRandomKey(); // class static $encrypted = SymmetricEncryption::encryptKey($input, $key); @@ -190,6 +262,56 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase SymmetricEncryption::decryptKey($encrypted, $key); } + /** + * test invalid key provided to decrypt or encrypt + * + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerWrongKey + * @testdox wrong key indirect $key throws $exception_message [$_dataName] + * + * @param string $key + * @param string $exception_message + * @return void + */ + public function testWrongKeyIndirect(string $key, string $exception_message): void + { + $enc_key = CreateKey::generateRandomKey(); + + // class instance + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::getInstance($key)->encrypt('test'); + // we must encrypt valid thing first so we can fail with the wrong key + $encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test'); + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::getInstance($key)->decrypt($encrypted); + } + + /** + * test invalid key provided to decrypt or encrypt + * + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerWrongKey + * @testdox wrong key static $key throws $exception_message [$_dataName] + * + * @param string $key + * @param string $exception_message + * @return void + */ + public function testWrongKeyStatic(string $key, string $exception_message): void + { + $enc_key = CreateKey::generateRandomKey(); + + // class static + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::encryptKey('test', $key); + // we must encrypt valid thing first so we can fail with the wrong key + $encrypted = SymmetricEncryption::encryptKey('test', $enc_key); + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decryptKey($encrypted, $key); + } + /** * Undocumented function * @@ -232,6 +354,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase $this->expectExceptionMessage($exception_message); SymmetricEncryption::decryptKey($input, $key); } + + /** + * Undocumented function + * + * @covers ::decrypt + * @dataProvider providerWrongCiphertext + * @testdox too short ciphertext indirect $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testWrongCiphertextIndirect(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + + // class instance + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::getInstance($key)->decrypt($input); + + // class static + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decryptKey($input, $key); + } + + /** + * Undocumented function + * + * @covers ::decrypt + * @dataProvider providerWrongCiphertext + * @testdox too short ciphertext static $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testWrongCiphertextStatic(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + // class static + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decryptKey($input, $key); + } } // __END__ diff --git a/www/lib/CoreLibs/Security/SymmetricEncryption.php b/www/lib/CoreLibs/Security/SymmetricEncryption.php index c12f4b3f..2f8fb75e 100644 --- a/www/lib/CoreLibs/Security/SymmetricEncryption.php +++ b/www/lib/CoreLibs/Security/SymmetricEncryption.php @@ -49,7 +49,11 @@ class SymmetricEncryption */ public static function getInstance(string|null $key = null): self { - if (empty(self::$instance)) { + // new if no instsance or key is different + if ( + empty(self::$instance) || + self::$instance->key != $key + ) { self::$instance = new self($key); } return self::$instance; @@ -130,7 +134,7 @@ class SymmetricEncryption */ private function encryptData(string $message, ?string $key): string { - if (empty($this->key) || $key === null) { + if ($key === null) { throw new \UnexpectedValueException('Key not set'); } $key = $this->createKey($key); From f966209e0a4276c0b8132a05f1dac78b0eca2cc5 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 12 Dec 2024 21:20:09 +0900 Subject: [PATCH 26/28] phpstan param declration fix for ACL Login user status --- www/lib/CoreLibs/ACL/LoginUserStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/lib/CoreLibs/ACL/LoginUserStatus.php b/www/lib/CoreLibs/ACL/LoginUserStatus.php index fb791abb..ddc5a84f 100644 --- a/www/lib/CoreLibs/ACL/LoginUserStatus.php +++ b/www/lib/CoreLibs/ACL/LoginUserStatus.php @@ -36,7 +36,7 @@ final class LoginUserStatus /** * Returns an array mapping the numerical role values to their descriptive names * - * @return array + * @return array */ public static function getMap() { From e9cfdb4bf07e6f36cad85c874876255de1cc1572 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 13 Dec 2024 09:35:54 +0900 Subject: [PATCH 27/28] Remove all deprecated tests --- www/admin/class_test.convert.colors.php | 5 +++-- www/admin/class_test.html_build.element.php | 4 ++-- www/admin/class_test.lang.php | 4 ++-- www/admin/class_test.readenvfile.php | 6 ++++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/www/admin/class_test.convert.colors.php b/www/admin/class_test.convert.colors.php index 6754aa51..c1ef6c2f 100644 --- a/www/admin/class_test.convert.colors.php +++ b/www/admin/class_test.convert.colors.php @@ -131,7 +131,8 @@ try { } catch (\LengthException $e) { print "*Exception: " . $e->getMessage() . "
" . print_r($e, true) . "

"; } -print "
"; + +/* print "
"; print "

LEGACY

"; // B(valid) $rgb = [50, 20, 30]; @@ -173,7 +174,7 @@ $hsb = [0, 0, 5]; print "S::COLOR hsb->rgb: $hsb[0], $hsb[1], $hsb[2]: " . DgS::printAr(SetVarType::setArray( Colors::hsb2rgb($hsb[0], $hsb[1], $hsb[2]) - )) . "
"; + )) . "
"; */ print "
"; diff --git a/www/admin/class_test.html_build.element.php b/www/admin/class_test.html_build.element.php index 6296fbd6..303388b3 100644 --- a/www/admin/class_test.html_build.element.php +++ b/www/admin/class_test.html_build.element.php @@ -74,8 +74,8 @@ print "EL_O:
" . print_r($el_o, true) . "
"; echo "
"; print "buildHtml():
" . htmlentities($el_o->buildHtml()) . "
"; -echo "
"; -print "phfo(\$el_o):
" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "
"; +/* echo "
"; +print "phfo(\$el_o):
" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "
"; */ echo "
"; print "phfa(\$el_list):
" . htmlentities($el_o::buildHtmlFromList($el_o_list, true)) . "
"; diff --git a/www/admin/class_test.lang.php b/www/admin/class_test.lang.php index 43a217e3..7a73199b 100644 --- a/www/admin/class_test.lang.php +++ b/www/admin/class_test.lang.php @@ -48,8 +48,7 @@ $locale = 'en.UTF-8'; $locale_info = L10n::parseLocale($locale); print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "
"; -echo "
AUTO DETECT
"; - +/* echo "
AUTO DETECT
"; // DEPRECATED // $get_locale = Language\GetLocale::setLocale(); // print "[AUTO, DEPRECATED]: " . Support::printAr($get_locale) . "
"; @@ -103,6 +102,7 @@ $get_locale = Language\GetLocale::setLocaleFromSession( BASE . INCLUDES . LOCALE ); print "[SESSION SET INVALID]: " . Support::printAr($get_locale) . "
"; + */ // try to load non existing echo "
NEW TYPE
"; diff --git a/www/admin/class_test.readenvfile.php b/www/admin/class_test.readenvfile.php index 9449bee8..2988dbcc 100644 --- a/www/admin/class_test.readenvfile.php +++ b/www/admin/class_test.readenvfile.php @@ -34,10 +34,12 @@ print '

' . $PAGE_NAME . '

'; print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "
"; +// This is now in \gullevek\dotenv\DotEnv::readEnvFile(...) + // test .env in local -$status = \CoreLibs\Get\DotEnv::readEnvFile('.', 'test.env'); +/* $status = \CoreLibs\Get\DotEnv::readEnvFile('.', 'test.env'); print "test.env: STATUS: " . $status . "
"; -print "AFTER reading test.env file: " . \CoreLibs\Debug\Support::printAr($_ENV) . "
"; +print "AFTER reading test.env file: " . \CoreLibs\Debug\Support::printAr($_ENV) . "
"; */ print ""; // ;; From e349613d60dbbb44a75cb9cb54b664ec72c2d49e Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 13 Dec 2024 10:17:28 +0900 Subject: [PATCH 28/28] phpunit updates Add testsuits for default run Fix wording in testdox add a fallback in the Debugging Support test suit --- 4dev/tests/Debug/CoreLibsDebugSupportTest.php | 3 +++ 4dev/tests/Logging/CoreLibsLoggingErrorMessagesTest.php | 2 +- phpunit.xml | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/4dev/tests/Debug/CoreLibsDebugSupportTest.php b/4dev/tests/Debug/CoreLibsDebugSupportTest.php index 74ed67ed..7b6ed197 100644 --- a/4dev/tests/Debug/CoreLibsDebugSupportTest.php +++ b/4dev/tests/Debug/CoreLibsDebugSupportTest.php @@ -568,6 +568,9 @@ final class CoreLibsDebugSupportTest extends TestCase 'assert expected 12' ); break; + default: + $this->assertTrue(true, 'Default fallback as true'); + break; } } diff --git a/4dev/tests/Logging/CoreLibsLoggingErrorMessagesTest.php b/4dev/tests/Logging/CoreLibsLoggingErrorMessagesTest.php index ad2d606c..5e9c3ac6 100644 --- a/4dev/tests/Logging/CoreLibsLoggingErrorMessagesTest.php +++ b/4dev/tests/Logging/CoreLibsLoggingErrorMessagesTest.php @@ -10,7 +10,7 @@ use CoreLibs\Logging\Logger\Level; /** * Test class for Logging * @coversDefaultClass \CoreLibs\Logging\ErrorMessages - * @testdox \CoreLibs\Logging\ErrorMEssages method tests + * @testdox \CoreLibs\Logging\ErrorMessages method tests */ final class CoreLibsLoggingErrorMessagesTest extends TestCase { diff --git a/phpunit.xml b/phpunit.xml index dcaf3022..90ff0695 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -5,4 +5,9 @@ convertDeprecationsToExceptions="true" bootstrap="4dev/tests/bootstrap.php" > + + + 4dev/tests + +