Compare commits

...

60 Commits

Author SHA1 Message Date
Clemens Schwaighofer
8d0036eaac Fix phpdoc return types 2024-12-23 11:26:50 +09:00
Clemens Schwaighofer
d1e65c702e Allow Seession settings to be changed
eg set the auto write + others
or set/unset can be chagned for single sets
2024-12-20 18:48:00 +09:00
Clemens Schwaighofer
7248906da7 Allow chaining of key set functions for encryption 2024-12-20 15:13:22 +09:00
Clemens Schwaighofer
7f9a4dc04f Merge branch 'Feature-AsymmetricEncryption' into NewFeatures 2024-12-18 10:52:29 +09:00
Clemens Schwaighofer
10935214eb Fix Class file name for asymmetric anonymous encryption 2024-12-18 10:50:27 +09:00
Clemens Schwaighofer
41e116f7d4 phpstan checks for level 9 2024-12-18 10:11:47 +09:00
Clemens Schwaighofer
881c93c343 Asymmetric Anoymouse Encryption phpunit tests 2024-12-18 09:56:48 +09:00
Clemens Schwaighofer
185d044a0b Symmetric encryption key set tests 2024-12-17 18:23:10 +09:00
Clemens Schwaighofer
cc067cc202 Update symmetric encryption with compare/get key, empty key test, unset on end
All key and messages are set SensitiveParameter type
On end, unset the key parameter with sodium mem zero
Get/Compare key set methods
Additional check on empty key
Add missing sodium mem zero for inner function variable clean up
2024-12-17 15:18:06 +09:00
Clemens Schwaighofer
37e2e54b2a Add asymmetric anonymous encryption
Private/Public key encryption for anonymous messages (not receipient)
2024-12-17 15:16:48 +09:00
Clemens Schwaighofer
711b3bfe97 Remove E_STRICT from error reporting, it is deprecated 2024-12-13 18:45:27 +09:00
Clemens Schwaighofer
3bd21c75d8 Make the font-size for ACL Login template a bit smaller
1.5em was too large, 1.3em is better
2024-12-13 13:58:56 +09:00
Clemens Schwaighofer
4971f62490 ecuid name fix in test file 2024-12-13 11:42:45 +09:00
Clemens Schwaighofer
1cf4fdf31a Fix column named for edit_log to eu prefixed
as eucuid and eucuuid
2024-12-13 11:37:52 +09:00
Clemens Schwaighofer
d16b920966 Update arrayReturnMatchinKeyOnly description 2024-12-13 11:29:37 +09:00
Clemens Schwaighofer
ab52bf59b5 phan/phpstan fixes 2024-12-13 10:38:24 +09:00
Clemens Schwaighofer
a8dd076aac Merge branch 'NewFeatures' into Feature-LoginClassAddUuidv4 2024-12-13 10:27:49 +09:00
Clemens Schwaighofer
c17ca1f847 Merge branch 'development' 2024-12-13 10:21:41 +09:00
Clemens Schwaighofer
e349613d60 phpunit updates
Add testsuits for default run
Fix wording in testdox
add a fallback in the Debugging Support test suit
2024-12-13 10:17:28 +09:00
Clemens Schwaighofer
e9cfdb4bf0 Remove all deprecated tests 2024-12-13 09:35:54 +09:00
Clemens Schwaighofer
f966209e0a phpstan param declration fix for ACL Login user status 2024-12-12 21:20:09 +09:00
Clemens Schwaighofer
c13934de99 Fix for wrong key handling in Symmetric encryption 2024-12-12 21:09:41 +09:00
Clemens Schwaighofer
1e90bb677e Fix Symmetric encryption with wrong key handling
- static call encrypt: do not check pre set key
- indirect call: set new if key is different
2024-12-12 21:07:17 +09:00
Clemens Schwaighofer
540269e61f Fix update script for now to clock_timestamp 2024-12-12 19:04:21 +09:00
Clemens Schwaighofer
e793c3975b Change all db now to clock_timestamp for triggers 2024-12-12 12:02:49 +09:00
Clemens Schwaighofer
7d4c9724fe Fix session options argument declaration for phpstan 2024-12-11 21:10:00 +09:00
Clemens Schwaighofer
d1c4611431 Indent fix for ACL Login 2024-12-11 21:06:59 +09:00
Clemens Schwaighofer
8d3882a6fe 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)
2024-12-11 21:05:56 +09:00
Clemens Schwaighofer
2b0434e36b Merge branch 'NewFeatures' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-11 10:36:57 +09:00
Clemens Schwaighofer
ba11a936db DB IO remove debug placeholder output 2024-12-11 10:36:31 +09:00
Clemens Schwaighofer
df591659cb Merge branch 'NewFeatures' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-11 10:35:19 +09:00
Clemens Schwaighofer
5343034768 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
2024-12-11 10:30:41 +09:00
Clemens Schwaighofer
dec56c9559 Merge remote-tracking branch 'all/NewFeatures' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-10 15:28:59 +09:00
Clemens Schwaighofer
880f15ac6f Merge branch 'development' 2024-12-10 15:26:24 +09:00
Clemens Schwaighofer
a46601fe03 Sync folder is master and not trunk 2024-12-10 15:25:17 +09:00
Clemens Schwaighofer
022c39e791 Add missing phpunit test folder for deprecated session var load test 2024-12-10 15:24:45 +09:00
Clemens Schwaighofer
fdefaca301 Missing php unit test path for locale check 2024-12-10 15:22:59 +09:00
Clemens Schwaighofer
46e44c19bf edit log table column order update 2024-12-10 14:44:42 +09:00
Clemens Schwaighofer
41cb6358f9 phpunit checks update, update edit_log logging sets 2024-12-10 14:40:07 +09:00
Clemens Schwaighofer
23142a4549 Merge branch 'Bug-DBIOWrongParamCountWithCommentBeforeParam' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-10 13:37:43 +09:00
Clemens Schwaighofer
a7742bd5c8 DB IO count params fix for comments 2024-12-10 13:36:57 +09:00
Clemens Schwaighofer
50f83b822c Merge branch 'Bug-DBIOWrongParamCountWithCommentBeforeParam' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-10 12:02:31 +09:00
Clemens Schwaighofer
78591d6ba4 Fix Param regex lookup
Query was not counting params after "--" comment strings
2024-12-10 12:01:06 +09:00
Clemens Schwaighofer
e8299a123b 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
2024-12-10 10:06:49 +09:00
Clemens Schwaighofer
10c320f60c 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
2024-12-09 19:37:23 +09:00
Clemens Schwaighofer
eeca138192 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
2024-12-09 19:22:54 +09:00
Clemens Schwaighofer
65715ea9c3 Add Array function to return only array entries based on matching key
A simple key based array filter
2024-12-09 19:13:03 +09:00
Clemens Schwaighofer
a56cbd8e97 ACL Login update layout to work with PC/Smartphone
Updated login template and password change template block
2024-12-09 16:20:21 +09:00
Clemens Schwaighofer
fe50a988a0 Switch session ACL Login user load check to cuuid
Update tests too for using edit user cuuid instead of the primary key
2024-12-06 20:11:28 +09:00
Clemens Schwaighofer
a84ab86e31 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
2024-12-06 18:07:06 +09:00
Clemens Schwaighofer
b044999772 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
2024-12-06 16:31:20 +09:00
Clemens Schwaighofer
98bf3a40cd Add logout button to class.test.php for logout test, ANY placeholder db test 2024-12-06 14:54:09 +09:00
Clemens Schwaighofer
cbd47fb015 edit log table update, Change all DB tests serial to identity for primary key 2024-12-05 14:59:49 +09:00
Clemens Schwaighofer
5f89917abd Add composer keywords 2024-12-05 14:30:12 +09:00
Clemens Schwaighofer
eeaff3042e phpstan config file update with phpVersion information 2024-12-05 14:16:57 +09:00
Clemens Schwaighofer
d070c4e461 phan min php set to 8.2 2024-12-05 13:59:20 +09:00
Clemens Schwaighofer
e57c336dba Clean up to use session methods and not _SESSION directly
Add session_unset for unsetAll and rename this method to "clear"
2024-12-05 13:52:45 +09:00
Clemens Schwaighofer
075fe967d5 Merge branch 'NewFeatures' into Feature-FixSessionClass 2024-12-05 12:18:51 +09:00
Clemens Schwaighofer
0e5f637052 Update Serial to Identity function
Return status as varchar from change.

clean up edit table SQL files with too many empty lines
2024-12-05 12:11:07 +09:00
Clemens Schwaighofer
2e1b767a85 Fix Session class with Many update and get
Update Login and Backend class to use interface when writing to avoid
problems with not written _SESSION vars with session is in write close status
2024-12-05 12:09:58 +09:00
94 changed files with 4416 additions and 1685 deletions

View File

@@ -27,7 +27,7 @@ use Phan\Config;
return [
// "target_php_version" => "8.2",
"minimum_target_php_version" => "8.1",
"minimum_target_php_version" => "8.2",
// turn color on (-C)
"color_issue_messages_if_supported" => true,
// If true, missing properties will be created when

View File

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

View File

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

View File

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

View File

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

View File

@@ -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';

View File

@@ -6,7 +6,8 @@
-- @param name col The column to be changed
-- @param varchar identity_type [default=a] Allowed a, d, assigned, default
-- @param varchar col_type [default=''] Allowed smallint, int, bigint, int2, int4, int8
-- @raises EXCEPTON on column not found, no linked sequence, more than one linked sequence found
-- @returns varchar status tring
-- @raises EXCEPTON on column not found, no linked sequence, more than one linked sequence found, invalid col type
--
CREATE OR REPLACE FUNCTION upgrade_serial_to_identity(
tbl regclass,
@@ -14,17 +15,18 @@ CREATE OR REPLACE FUNCTION upgrade_serial_to_identity(
identity_type varchar = 'a',
col_type varchar = ''
)
RETURNS void
RETURNS varchar
LANGUAGE plpgsql
AS $$
DECLARE
colnum smallint;
seqid oid;
count int;
col_type_oid int;
col_type_len int;
current_col_atttypid oid;
current_col_attlen int;
colnum SMALLINT;
seqid OID;
count INT;
col_type_oid INT;
col_type_len INT;
current_col_atttypid OID;
current_col_attlen INT;
status_string VARCHAR;
BEGIN
-- switch between always (default) or default identiy type
IF identity_type NOT IN ('a', 'd', 'assigned', 'default') THEN
@@ -59,6 +61,10 @@ BEGIN
RAISE EXCEPTION 'more than one linked sequence found';
END IF;
IF col_type <> '' AND col_type NOT IN ('smallint', 'int', 'bigint', 'int2', 'int4', 'int8') THEN
RAISE EXCEPTION 'Invalid col type: %', col_type;
END IF;
-- drop the default
EXECUTE 'ALTER TABLE ' || tbl || ' ALTER COLUMN ' || quote_ident(col) || ' DROP DEFAULT';
@@ -74,34 +80,31 @@ BEGIN
SET attidentity = identity_type
WHERE attrelid = tbl
AND attname = col;
RAISE NOTICE 'Update to identity for table "%" and columen "%" with type "%"', tbl, col, identity_type;
status_string := 'Updated to identity for table "' || tbl || '" and columen "' || col || '" with type "' || identity_type || '"';
-- set type if requested and not empty
IF col_type <> '' THEN
IF col_type IN ('smallint', 'int', 'bigint', 'int2', 'int4', 'int8') THEN
-- rewrite smallint, int, bigint
IF col_type = 'smallint' THEN
col_type := 'int2';
ELSIF col_type = 'int' THEN
col_type := 'int4';
ELSIF col_type = 'bigint' THEN
col_type := 'int8';
END IF;
-- get the length and oid for selected
SELECT oid, typlen INTO col_type_oid, col_type_len FROM pg_type WHERE typname = col_type;
-- set only if diff or hight
IF current_col_atttypid <> col_type_oid AND col_type_len > current_col_attlen THEN
RAISE NOTICE 'Change col type: %', col_type;
-- update type
UPDATE pg_attribute
SET
atttypid = col_type_oid, attlen = col_type_len
WHERE attrelid = tbl
AND attname = col;
END IF;
ELSE
RAISE NOTICE 'Invalid col type: %', col_type;
-- rewrite smallint, int, bigint
IF col_type = 'smallint' THEN
col_type := 'int2';
ELSIF col_type = 'int' THEN
col_type := 'int4';
ELSIF col_type = 'bigint' THEN
col_type := 'int8';
END IF;
-- get the length and oid for selected
SELECT oid, typlen INTO col_type_oid, col_type_len FROM pg_type WHERE typname = col_type;
-- set only if diff or hight
IF current_col_atttypid <> col_type_oid AND col_type_len > current_col_attlen THEN
status_string := status_string || '. Change col type: ' || col_type;
-- update type
UPDATE pg_attribute
SET
atttypid = col_type_oid, attlen = col_type_len
WHERE attrelid = tbl
AND attname = col;
END IF;
END IF;
RETURN status_string;
END;
$$;

View File

@@ -10,35 +10,51 @@ 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,
eucuid VARCHAR,
eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
-- date_created equal, but can be overridden
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
ip VARCHAR,
-- session ID if set
session_id VARCHAR,
-- username
username VARCHAR,
-- DEPRECATED [password]
password VARCHAR,
ip_address JSONB, -- REMOTE_IP and all other IPs (X_FORWARD, etc) as JSON block
-- DEPRECATED [ip]
ip VARCHAR, -- just the REMOTE_IP, full set see ip_address
-- string blocks, general
error TEXT,
event TEXT,
-- bytea or string type storage of any data
data_binary BYTEA,
data TEXT,
-- set page name only
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,
-- DEPRECATED [http*]
http_accept VARCHAR, -- in http_data
http_accept_charset VARCHAR, -- in http_data
http_accept_encoding VARCHAR, -- in http_data
-- any action var, -> same set in action_data as JSON
action_data JSONB,
-- DEPRECATED [action*]
action VARCHAR, -- in action_data
action_id VARCHAR, -- in action_data
action_sub_id VARCHAR, -- in action_data
action_yes VARCHAR, -- in action_data
action_flag VARCHAR, -- in action_data
action_menu VARCHAR, -- in action_data
action_loaded VARCHAR, -- in action_data
action_value VARCHAR, -- in action_data
action_type VARCHAR, -- in action_data
action_error VARCHAR -- in action_data
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -12,5 +12,3 @@ CREATE TABLE edit_menu_group (
flag VARCHAR,
order_number INT NOT NULL
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -16,5 +16,3 @@ CREATE TABLE edit_page_access (
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -35,11 +35,10 @@ 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,
-- force lgout counter
force_logout INT DEFAULT 0,
-- last login log
last_login TIMESTAMP WITHOUT TIME ZONE,
-- login error
@@ -76,9 +75,8 @@ 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.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';

View File

@@ -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)"
<<<SQL
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
)
SQL
];
foreach ($queries as $query) {
self::$db->dbExec($query);
}
// read edit access cuid, edit user cuid and edit user cuuid
$row = self::$db->dbReturnRowParams(
"SELECT cuid FROM edit_access WHERE uid = $1",
["AdminAccess"]
);
self::$edit_access_cuid = $row['cuid'] ?? '';
if (empty(self::$edit_access_cuid)) {
self::markTestIncomplete(
'Cannot read edit access cuid for "AdminAccess".'
);
}
$row = self::$db->dbReturnRowParams(
"SELECT cuid, cuuid FROM edit_user WHERE username = $1",
["admin"]
);
self::$edit_user_cuid = $row['cuid'] ?? '';
self::$edit_user_cuuid = $row['cuuid'] ?? '';
if (empty(self::$edit_user_cuid) || empty(self::$edit_user_cuuid)) {
self::markTestIncomplete(
'Cannot read edit user cuid or cuuid for "admin".'
);
}
// define mandatory constant
// must set
@@ -235,24 +265,25 @@ final class CoreLibsACLLoginTest extends TestCase
'ajax_post_action' => 'login',
],
],
'load, session euid set only, php error' => [
'load, session eucuuid set only, php error' => [
[
'page_name' => 'edit_users.php',
],
[],
[],
[
'EUID' => 1,
'ECUID' => 'abc',
'ECUUID' => '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,
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test',
'base_access' => 'list',
@@ -261,22 +292,23 @@ final class CoreLibsACLLoginTest extends TestCase
[],
[],
[
'EUID' => 1,
'ECUID' => 'abc',
'ECUUID' => '1233456-1234-1234-1234-123456789012',
'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' => [
'AdminAccess' => 1,
'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' => [
1 => [
'LOGIN_UNIT' => [
'123456789012' => [
'id' => 1,
'acl_level' => 80,
'name' => 'Admin Access',
'uid' => 'AdminAccess',
@@ -288,8 +320,8 @@ final class CoreLibsACLLoginTest extends TestCase
'additional_acl' => []
],
],
// 'UNIT_DEFAULT' => '',
// 'DEFAULT_ACL_LIST' => [],
// 'LOGIN_UNIT_DEFAULT' => '',
// 'LOGIN_DEFAULT_ACL_LIST' => [],
],
0,
[
@@ -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,
@@ -1111,11 +1176,15 @@ final class CoreLibsACLLoginTest extends TestCase
$_POST[$post_var] = $post_value;
}
// set ingoing session cuuid if requested
if (isset($session['LOGIN_EUCUUID']) && $session['LOGIN_EUCUUID'] == 'SET_EUCUUID_IN_TEST') {
$session['LOGIN_EUCUUID'] = self::$edit_user_cuuid;
}
// set _SESSION data
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([
@@ -1134,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')
@@ -1152,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
@@ -1369,6 +1442,19 @@ final class CoreLibsACLLoginTest extends TestCase
// run test
try {
// preset, we cannot set that in the provider
if (
isset($expected['check_access_cuid']) &&
$expected['check_access_cuid'] == 'SET_EDIT_ACCESS_CUID_IN_TEST'
) {
$expected['check_access_cuid'] = self::$edit_access_cuid;
}
if (
isset($mock_settings['edit_access_cuid']) &&
$mock_settings['edit_access_cuid'] == 'SET_EDIT_ACCESS_CUID_IN_TEST'
) {
$mock_settings['edit_access_cuid'] = self::$edit_access_cuid;
}
// if ajax call
// check if parameter, or globals (old type)
// else normal call
@@ -1427,6 +1513,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 +1554,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'
@@ -1480,11 +1585,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 ||

View File

@@ -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;
@@ -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,
@@ -579,11 +579,10 @@ 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,
-- forced logout counter
force_logout INT DEFAULT 0,
-- last login log
last_login TIMESTAMP WITHOUT TIME ZONE,
-- login error
@@ -620,8 +619,6 @@ COMMENT ON COLUMN edit_user.deleted IS 'Login is deleted (master switch), overri
COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be locked, default off';
COMMENT ON COLUMN edit_user.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';
@@ -652,40 +649,56 @@ 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
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,
eucuid VARCHAR,
eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
-- date_created equal, but can be overridden
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
ip VARCHAR,
-- session ID if set
session_id VARCHAR,
-- username
username VARCHAR,
-- DEPRECATED [password]
password VARCHAR,
ip_address JSONB, -- REMOTE_IP and all other IPs (X_FORWARD, etc) as JSON block
-- DEPRECATED [ip]
ip VARCHAR, -- just the REMOTE_IP, full set see ip_address
-- string blocks, general
error TEXT,
event TEXT,
-- bytea or string type storage of any data
data_binary BYTEA,
data TEXT,
-- set page name only
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,
-- DEPRECATED [http*]
http_accept VARCHAR, -- in http_data
http_accept_charset VARCHAR, -- in http_data
http_accept_encoding VARCHAR, -- in http_data
-- any action var, -> same set in action_data as JSON
action_data JSONB,
-- DEPRECATED [action*]
action VARCHAR, -- in action_data
action_id VARCHAR, -- in action_data
action_sub_id VARCHAR, -- in action_data
action_yes VARCHAR, -- in action_data
action_flag VARCHAR, -- in action_data
action_menu VARCHAR, -- in action_data
action_loaded VARCHAR, -- in action_data
action_value VARCHAR, -- in action_data
action_type VARCHAR, -- in action_data
action_error VARCHAR -- in action_data
) INHERITS (edit_generic) WITHOUT OIDS;
-- END: table/edit_log.sql
-- START: table/edit_log_overflow.sql
@@ -712,7 +725,7 @@ ALTER TABLE edit_log_overflow ADD CONSTRAINT edit_log_overflow_euid_fkey FOREIGN
-- DROP TABLE edit_access;
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 +746,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 +767,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,
@@ -1015,7 +1028,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'),

View File

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

View File

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

View File

@@ -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,
<<<SQL
CREATE TABLE table_with_primary_key (
table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
@@ -5136,6 +5135,67 @@ final class CoreLibsDBIOTest extends TestCase
SQL,
'count' => 6,
'convert' => false,
],
'comments in insert' => [
'query' => <<<SQL
INSERT INTO table_with_primary_key (
row_int, row_numeric, row_varchar, row_varchar_literal
) VALUES (
-- comment 1 かな
$1, $2,
-- comment 2 -
$3
-- comment 3
, $4
)
SQL,
'count' => 4,
'convert' => false
],
'comment in update' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
row_int =
-- COMMENT 1
$1,
row_numeric =
$2 -- COMMENT 2
,
row_varchar -- COMMENT 3
= $3
WHERE
row_varchar = $4
SQL,
'count' => 4,
'convert' => false,
],
// Note some are not set
'a complete set of possible' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
-- ROW
row_varchar = $1
WHERE
row_varchar = ANY($2) AND row_varchar <> $3
AND row_varchar > $4 AND row_varchar < $5
AND row_varchar >= $6 AND row_varchar <=$7
AND row_jsonb->'a' = $8 AND row_jsonb->>$9 = 'a'
AND row_jsonb<@$10 AND row_jsonb@>$11
AND row_varchar ^@ $12
SQL,
'count' => 12,
'convert' => false,
],
// all the same
'all the same numbered' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
row_int = $1::INT, row_numeric = $1::NUMERIC, row_varchar = $1
WHERE
row_varchar = $1
SQL,
'count' => 1,
'convert' => false,
]
];
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,838 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Security\CreateKey;
use CoreLibs\Security\AsymmetricAnonymousEncryption;
/**
* Test class for Security\AsymmetricAnonymousEncryption and Security\CreateKey
* @coversDefaultClass \CoreLibs\Security\AsymmetricAnonymousEncryption
* @testdox \CoreLibs\Security\AsymmetricAnonymousEncryption method tests
*/
final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
{
// MARK: key set and compare
/**
* Undocumented function
*
* @covers ::getKeyPair
* @covers ::compareKeyPair
* @covers ::getPublicKey
* @covers ::comparePublicKey
* @testdox Check if init class set key pair matches to created key pair and public key
*
* @return void
*/
public function testKeyPairInitGetCompare(): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$crypt = new AsymmetricAnonymousEncryption($key_pair);
$this->assertTrue(
$crypt->compareKeyPair($key_pair),
'set key pair not equal to original key pair'
);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'automatic set public key not equal to original public key'
);
$this->assertEquals(
$key_pair,
$crypt->getKeyPair(),
'set key pair returned not equal to original key pair'
);
$this->assertEquals(
$public_key,
$crypt->getPublicKey(),
'automatic set public key returned not equal to original public key'
);
}
/**
* Undocumented function
*
* @covers ::getKeyPair
* @covers ::compareKeyPair
* @covers ::getPublicKey
* @covers ::comparePublicKey
* @testdox Check if init class set key pair and public key matches to created key pair and public key
*
* @return void
*/
public function testKeyPairPublicKeyInitGetCompare(): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key);
$this->assertTrue(
$crypt->compareKeyPair($key_pair),
'set key pair not equal to original key pair'
);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'set public key not equal to original public key'
);
$this->assertEquals(
$key_pair,
$crypt->getKeyPair(),
'set key pair returned not equal to original key pair'
);
$this->assertEquals(
$public_key,
$crypt->getPublicKey(),
'set public key returned not equal to original public key'
);
}
/**
* Undocumented function
*
* @covers ::getKeyPair
* @covers ::getPublicKey
* @covers ::comparePublicKey
* @testdox Check if init class set public key matches to created public key
*
* @return void
*/
public function testPublicKeyInitGetCompare(): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'set public key not equal to original public key'
);
$this->assertEquals(
null,
$crypt->getKeyPair(),
'unset set key pair returned not equal to original key pair'
);
$this->assertEquals(
$public_key,
$crypt->getPublicKey(),
'set public key returned not equal to original public key'
);
}
/**
* Undocumented function
*
* @covers ::setKeyPair
* @covers ::getKeyPair
* @covers ::compareKeyPair
* @covers ::getPublicKey
* @covers ::comparePublicKey
* @testdox Check if set key pair after class init matches to created key pair and public key
*
* @return void
*/
public function testKeyPairSetGetCompare(): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$crypt = new AsymmetricAnonymousEncryption();
$crypt->setKeyPair($key_pair);
$this->assertTrue(
$crypt->compareKeyPair($key_pair),
'post class init set key pair not equal to original key pair'
);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'post class init automatic set public key not equal to original public key'
);
$this->assertEquals(
$key_pair,
$crypt->getKeyPair(),
'post class init set key pair returned not equal to original key pair'
);
$this->assertEquals(
$public_key,
$crypt->getPublicKey(),
'post class init automatic set public key returned not equal to original public key'
);
}
/**
* Undocumented function
*
* @covers ::setKeyPair
* @covers ::setPublicKey
* @covers ::getKeyPair
* @covers ::compareKeyPair
* @covers ::getPublicKey
* @covers ::comparePublicKey
* @testdox Check if set key pair after class init matches to created key pair and public key
*
* @return void
*/
public function testKeyPairPublicKeySetGetCompare(): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$crypt = new AsymmetricAnonymousEncryption();
$crypt->setKeyPair($key_pair);
$crypt->setPublicKey($public_key);
$this->assertTrue(
$crypt->compareKeyPair($key_pair),
'post class init set key pair not equal to original key pair'
);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'post class init set public key not equal to original public key'
);
$this->assertEquals(
$key_pair,
$crypt->getKeyPair(),
'post class init set key pair returned not equal to original key pair'
);
$this->assertEquals(
$public_key,
$crypt->getPublicKey(),
'post class init set public key returned not equal to original public key'
);
}
/**
* Undocumented function
*
* @covers ::setPublicKey
* @covers ::getKeyPair
* @covers ::compareKeyPair
* @covers ::getPublicKey
* @covers ::comparePublicKey
* @testdox Check if set key pair after class init matches to created key pair and public key
*
* @return void
*/
public function testPublicKeySetGetCompare(): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$crypt = new AsymmetricAnonymousEncryption();
$crypt->setPublicKey($public_key);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'post class init set public key not equal to original public key'
);
$this->assertEquals(
null,
$crypt->getKeyPair(),
'post class init unset key pair returned not equal to original key pair'
);
$this->assertEquals(
$public_key,
$crypt->getPublicKey(),
'post class init set public key returned not equal to original public key'
);
}
/**
* Undocumented function
*
* @testdox Check different key pair and public key set
*
* @return void
*/
public function testDifferentSetKeyPairPublicKey()
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$key_pair_2 = CreateKey::createKeyPair();
$public_key_2 = CreateKey::getPublicKey($key_pair_2);
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key_2);
$this->assertTrue(
$crypt->compareKeyPair($key_pair),
'key pair set matches key pair created'
);
$this->assertTrue(
$crypt->comparePublicKey($public_key_2),
'alternate public key set matches alternate public key created'
);
$this->assertFalse(
$crypt->comparePublicKey($public_key),
'alternate public key set does not match key pair public key'
);
}
/**
* Undocumented function
*
* @testdox Check if new set privat key does not overwrite set public key
*
* @return void
*/
public function testUpdateKeyPairNotUpdatePublicKey(): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$crypt = new AsymmetricAnonymousEncryption($key_pair);
$this->assertTrue(
$crypt->compareKeyPair($key_pair),
'set key pair not equal to original key pair'
);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'set public key not equal to original public key'
);
$key_pair_2 = CreateKey::createKeyPair();
$public_key_2 = CreateKey::getPublicKey($key_pair_2);
$crypt->setKeyPair($key_pair_2);
$this->assertTrue(
$crypt->compareKeyPair($key_pair_2),
'new set key pair not equal to original new key pair'
);
$this->assertTrue(
$crypt->comparePublicKey($public_key),
'original set public key not equal to original public key'
);
$this->assertFalse(
$crypt->comparePublicKey($public_key_2),
'new public key equal to original public key'
);
}
// MARK: empty encrytped string
/**
* Undocumented function
*
* @covers ::decryptKey
* @covers ::decrypt
* @testdox Test empty encrypted string to decrypt
*
* @return void
*/
public function testEmptyDecryptionString(): void
{
$this->expectExceptionMessage('Encrypted string cannot be empty');
AsymmetricAnonymousEncryption::decryptKey('', CreateKey::generateRandomKey());
}
// MARK: encrypt/decrypt
/**
* Undocumented function
*
* @return array
*/
public function providerEncryptDecryptSuccess(): array
{
return [
'valid string' => [
'input' => 'I am a secret',
'expected' => 'I am a secret',
],
];
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccess(string $input, string $expected): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
// test class
$crypt = new AsymmetricAnonymousEncryption($key_pair);
$encrypted = $crypt->encrypt($input);
$decrypted = $crypt->decrypt($encrypted);
$this->assertEquals(
$expected,
$decrypted,
'Class call',
);
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key);
$encrypted = $crypt->encrypt($input);
$decrypted = $crypt->decrypt($encrypted);
$this->assertEquals(
$expected,
$decrypted,
'Class call botjh set',
);
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt indirect $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccessIndirect(string $input, string $expected): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
// test indirect
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
$decrypted = AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
$this->assertEquals(
$expected,
$decrypted,
'Class Instance call',
);
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt indirect with public key $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccessIndirectPublicKey(string $input, string $expected): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
// test indirect
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
$decrypted = AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
$this->assertEquals(
$expected,
$decrypted,
'Class Instance call public key',
);
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt static $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccessStatic(string $input, string $expected): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
// test static
$encrypted = AsymmetricAnonymousEncryption::encryptKey($input, $public_key);
$decrypted = AsymmetricAnonymousEncryption::decryptKey($encrypted, $key_pair);
$this->assertEquals(
$expected,
$decrypted,
'Static call',
);
}
// MARK: invalid decrypt key
/**
* Undocumented function
*
* @return array
*/
public function providerEncryptFailed(): array
{
return [
'wrong decryption key' => [
'input' => 'I am a secret',
'excpetion_message' => 'Invalid key pair'
],
];
}
/**
* Test decryption with wrong key
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptFailed
* @testdox decrypt with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailed(string $input, string $exception_message): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$wrong_key_pair = CreateKey::createKeyPair();
// wrong key in class call
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
$encrypted = $crypt->encrypt($input);
$this->expectExceptionMessage($exception_message);
$crypt->setKeyPair($wrong_key_pair);
$crypt->decrypt($encrypted);
}
/**
* Test decryption with wrong key
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptFailed
* @testdox decrypt indirect with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailedIndirect(string $input, string $exception_message): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$wrong_key_pair = CreateKey::createKeyPair();
// class instance
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::getInstance($wrong_key_pair)->decrypt($encrypted);
}
/**
* Test decryption with wrong key
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptFailed
* @testdox decrypt static with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailedStatic(string $input, string $exception_message): void
{
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$wrong_key_pair = CreateKey::createKeyPair();
// class static
$encrypted = AsymmetricAnonymousEncryption::encryptKey($input, $public_key);
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::decryptKey($encrypted, $wrong_key_pair);
}
// MARK: invalid key pair
/**
* Undocumented function
*
* @return array
*/
public function providerWrongKeyPair(): array
{
return [
'not hex key pair' => [
'key_pair' => 'not_a_hex_key_pair',
'exception_message' => 'Invalid hex key pair'
],
'too short hex key pair' => [
'key_pair' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
'excpetion_message' => 'Key pair is not the correct size (must be '
],
'empty key pair' => [
'key_pair' => '',
'excpetion_message' => 'Key pair cannot be empty'
]
];
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKeyPair
* @testdox wrong key pair $key_pair throws $exception_message [$_dataName]
*
* @param string $key_pair
* @param string $exception_message
* @return void
*/
public function testWrongKeyPair(string $key_pair, string $exception_message): void
{
$enc_key_pair = CreateKey::createKeyPair();
// class
$this->expectExceptionMessage($exception_message);
$crypt = new AsymmetricAnonymousEncryption($key_pair);
$this->expectExceptionMessage($exception_message);
$crypt->encrypt('test');
$crypt->setKeyPair($enc_key_pair);
$encrypted = $crypt->encrypt('test');
$this->expectExceptionMessage($exception_message);
$crypt->setKeyPair($key_pair);
$crypt->decrypt($encrypted);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKeyPair
* @testdox wrong key pair indirect $key_pair throws $exception_message [$_dataName]
*
* @param string $key_pair
* @param string $exception_message
* @return void
*/
public function testWrongKeyPairIndirect(string $key_pair, string $exception_message): void
{
$enc_key_pair = CreateKey::createKeyPair();
// set valid encryption
$encrypted = AsymmetricAnonymousEncryption::getInstance($enc_key_pair)->encrypt('test');
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKeyPair
* @testdox wrong key pair static $key_pair throws $exception_message [$_dataName]
*
* @param string $key_pair
* @param string $exception_message
* @return void
*/
public function testWrongKeyPairStatic(string $key_pair, string $exception_message): void
{
$enc_key_pair = CreateKey::createKeyPair();
// set valid encryption
$encrypted = AsymmetricAnonymousEncryption::encryptKey('test', CreateKey::getPublicKey($enc_key_pair));
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::decryptKey($encrypted, $key_pair);
}
// MARK: invalid public key
/**
* Undocumented function
*
* @return array
*/
public function providerWrongPublicKey(): array
{
return [
'not hex public key' => [
'public_key' => 'not_a_hex_public_key',
'exception_message' => 'Invalid hex public key'
],
'too short hex public key' => [
'public_key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
'excpetion_message' => 'Public key is not the correct size (must be '
],
'empty public key' => [
'public_key' => '',
'excpetion_message' => 'Public key cannot be empty'
]
];
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongPublicKey
* @testdox wrong public key $public_key throws $exception_message [$_dataName]
*
* @param string $public_key
* @param string $exception_message
* @return void
*/
public function testWrongPublicKey(string $public_key, string $exception_message): void
{
$enc_key_pair = CreateKey::createKeyPair();
// $enc_public_key = CreateKey::getPublicKey($enc_key_pair);
// class
$this->expectExceptionMessage($exception_message);
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
$this->expectExceptionMessage($exception_message);
$crypt->decrypt('test');
$crypt->setKeyPair($enc_key_pair);
$encrypted = $crypt->encrypt('test');
$this->expectExceptionMessage($exception_message);
$crypt->setPublicKey($public_key);
$crypt->decrypt($encrypted);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongPublicKey
* @testdox wrong public key indirect $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongPublicKeyIndirect(string $key, string $exception_message): void
{
$enc_key = CreateKey::createKeyPair();
// class instance
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::getInstance(public_key:$key)->encrypt('test');
// we must encrypt valid thing first so we can fail with the wrong key
$encrypted = AsymmetricAnonymousEncryption::getInstance($enc_key)->encrypt('test');
// $this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::getInstance($key)->decrypt($encrypted);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongPublicKey
* @testdox wrong public key static $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongPublicKeyStatic(string $key, string $exception_message): void
{
$enc_key = CreateKey::createKeyPair();
// class static
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::encryptKey('test', $key);
// we must encrypt valid thing first so we can fail with the wrong key
$encrypted = AsymmetricAnonymousEncryption::encryptKey('test', $enc_key);
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::decryptKey($encrypted, $key);
}
// MARK: wrong cipher text
/**
* Undocumented function
*
* @return array
*/
public function providerWrongCiphertext(): array
{
return [
'invalid cipher text' => [
'input' => 'short',
'exception_message' => 'base642bin failed: '
],
'cannot decrypt' => [
// phpcs:disable Generic.Files.LineLength
'input' => 'Um8tBGiVfFAOg2YoUgA5fTqK1wXPB1S7uxhPNE1lqDxgntkEhYJDOmjXa0DMpBlYHjab6sC4mgzwZSzGCUnXDAgsHckwYwfAzs/r',
// phpcs:enable Generic.Files.LineLength
'exception_message' => 'Invalid key pair'
],
'invalid text' => [
'input' => 'U29tZSB0ZXh0IGhlcmU=',
'exception_message' => 'Invalid key pair'
]
];
}
/**
* Undocumented function
*
* @covers ::decrypt
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertext(string $input, string $exception_message): void
{
$key = CreateKey::createKeyPair();
// class
$crypt = new AsymmetricAnonymousEncryption($key);
$this->expectExceptionMessage($exception_message);
$crypt->decrypt($input);
}
/**
* Undocumented function
*
* @covers ::decryptKey
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext indirect $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertextIndirect(string $input, string $exception_message): void
{
$key = CreateKey::createKeyPair();
// class instance
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::getInstance($key)->decrypt($input);
// class static
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::decryptKey($input, $key);
}
/**
* Undocumented function
*
* @covers ::decryptKey
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext static $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertextStatic(string $input, string $exception_message): void
{
$key = CreateKey::createKeyPair();
// class static
$this->expectExceptionMessage($exception_message);
AsymmetricAnonymousEncryption::decryptKey($input, $key);
}
}
// __END__

View File

@@ -15,6 +15,77 @@ use CoreLibs\Security\SymmetricEncryption;
*/
final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
{
// MARK: key set compare
/**
* Undocumented function
*
* @covers ::compareKey
* @covers ::getKey
* @testdox Check if init class set key matches to created key
*
* @return void
*/
public function testKeyInitGetCompare(): void
{
$key = CreateKey::generateRandomKey();
$crypt = new SymmetricEncryption($key);
$this->assertTrue(
$crypt->compareKey($key),
'set key not equal to original key'
);
$this->assertEquals(
$key,
$crypt->getKey(),
'set key returned not equal to original key'
);
}
/**
* Undocumented function
*
* @covers ::setKey
* @covers ::compareKey
* @covers ::getKey
* @testdox Check if set key after class init matches to created key
*
* @return void
*/
public function testKeySetGetCompare(): void
{
$key = CreateKey::generateRandomKey();
$crypt = new SymmetricEncryption();
$crypt->setKey($key);
$this->assertTrue(
$crypt->compareKey($key),
'set key not equal to original key'
);
$this->assertEquals(
$key,
$crypt->getKey(),
'set key returned not equal to original key'
);
}
// MARK: empty encrypted string
/**
* Undocumented function
*
* @covers ::decryptKey
* @covers ::decrypt
* @testdox Test empty encrypted string to decrypt
*
* @return void
*/
public function testEmptyDecryptionString(): void
{
$this->expectExceptionMessage('Encrypted string cannot be empty');
SymmetricEncryption::decryptKey('', CreateKey::generateRandomKey());
}
// MARK: encrypt/decrypt compare
/**
* Undocumented function
*
@@ -56,7 +127,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 +153,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$decrypted,
'Class Instance call',
);
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encryptKey
* @covers ::decryptKey
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt static $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccessStatic(string $input, string $expected): void
{
$key = CreateKey::generateRandomKey();
// test static
$encrypted = SymmetricEncryption::encryptKey($input, $key);
$decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
@@ -77,6 +182,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
);
}
// MARK: invalid key
/**
* Undocumented function
*
@@ -114,13 +221,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 ::encryptKey
* @covers ::decryptKey
* @dataProvider providerEncryptFailed
* @testdox decrypt static with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailedStatic(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
$wrong_key = CreateKey::generateRandomKey();
// class static
$encrypted = SymmetricEncryption::encryptKey($input, $key);
@@ -128,6 +273,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
SymmetricEncryption::decryptKey($encrypted, $wrong_key);
}
// MARK: wrong key
/**
* Undocumented function
*
@@ -144,6 +291,10 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
'excpetion_message' => 'Key is not the correct size (must be '
],
'empty key' => [
'key' => '',
'excpetion_message' => 'Key cannot be empty'
]
];
}
@@ -164,6 +315,7 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$enc_key = CreateKey::generateRandomKey();
// class
$this->expectExceptionMessage($exception_message);
$crypt = new SymmetricEncryption($key);
$this->expectExceptionMessage($exception_message);
$crypt->encrypt('test');
@@ -172,6 +324,23 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$this->expectExceptionMessage($exception_message);
$crypt->setKey($key);
$crypt->decrypt($encrypted);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKey
* @testdox wrong key indirect $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKeyIndirect(string $key, string $exception_message): void
{
$enc_key = CreateKey::generateRandomKey();
// class instance
$this->expectExceptionMessage($exception_message);
@@ -180,6 +349,23 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$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 ::encryptKey
* @covers ::decryptKey
* @dataProvider providerWrongKey
* @testdox wrong key static $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKeyStatic(string $key, string $exception_message): void
{
$enc_key = CreateKey::generateRandomKey();
// class static
$this->expectExceptionMessage($exception_message);
@@ -190,6 +376,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
SymmetricEncryption::decryptKey($encrypted, $key);
}
// MARK: wrong input
/**
* Undocumented function
*
@@ -232,6 +420,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
/**
* Undocumented function
*
* @covers ::decryptKey
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext indirect $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertextIndirect(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
// class instance
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($key)->decrypt($input);
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
/**
* Undocumented function
*
* @covers ::decryptKey
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext static $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertextStatic(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
}
// __END__

View File

@@ -1,8 +1,16 @@
-- 20241203: update edit tables
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 eucuid VARCHAR;
ALTER TABLE edit_log ADD eucuuid 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;
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
@@ -14,13 +22,15 @@ 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;
$$
LANGUAGE 'plpgsql';
-- END --

View File

@@ -2,6 +2,7 @@
"name": "egrajp/development-corelibs-dev",
"version": "dev-master",
"description": "CoreLibs: Development package",
"keywords": ["corelib", "logging", "database", "templating", "tools"],
"type": "library",
"require": {
"php": ">=8.3"

View File

@@ -9,11 +9,14 @@ parameters:
#friendly:
# lineBefore: 3
# lineAfter: 3
level: 8 # max is now 9
level: 8 # max is now 10
# strictRules:
# allRules: false
checkMissingCallableSignature: true
treatPhpDocTypesAsCertain: false
# phpVersion:
# min: 80200 # PHP 8.2.0
# max: 80300 # PHP latest
paths:
- %currentWorkingDirectory%/www
bootstrapFiles:

View File

@@ -5,4 +5,9 @@
convertDeprecationsToExceptions="true"
bootstrap="4dev/tests/bootstrap.php"
>
<testsuites>
<testsuite name="deploy">
<directory>4dev/tests</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -250,6 +250,19 @@ foreach (array_keys($array) as $search) {
}
print "Key not exists: " . DgS::printAr(ArrayHandler::arrayGetNextKey($array, 'z')) . "<br>";
print "<hr>";
$keys = ['b', 'c', 'f'];
print "Return only: " . DgS::printAr($keys) . ": "
. DgS::printAr(ArrayHandler::arrayReturnMatchingKeyOnly($array, $keys)) . "<br>";
$out = array_filter($array, fn($key) => in_array($key, $keys), ARRAY_FILTER_USE_KEY);
print "array filter: " . DgS::printAr($keys) . ": " . DgS::printAr($out) . "<br>";
$out = array_intersect_key(
$array,
array_flip($keys)
);
print "array intersect key: " . DgS::printAr($keys) . ": " . DgS::printAr($out) . "<br>";
print "</body></html>";
// __END__

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
// basic class test file

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -18,7 +18,7 @@ require 'config.php';
$LOG_FILE_ID = 'classTest-convert-colors';
ob_end_flush();
use CoreLibs\Convert\Colors;
// use CoreLibs\Convert\Colors;
use CoreLibs\Convert\Color\Color;
use CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Debug\Support as DgS;
@@ -29,7 +29,6 @@ $log = new CoreLibs\Logging\Logging([
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
$color_class = 'CoreLibs\Convert\Colors';
/**
* print out a color block with info
@@ -131,7 +130,8 @@ try {
} catch (\LengthException $e) {
print "*Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
}
print "<hr>";
/* print "<hr>";
print "<h2>LEGACY</h2>";
// B(valid)
$rgb = [50, 20, 30];
@@ -173,7 +173,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])
)) . "<br>";
)) . "<br>"; */
print "<hr>";

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -7,7 +7,7 @@
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -28,7 +28,6 @@ $log = new CoreLibs\Logging\Logging([
'log_per_date' => true,
]);
$PAGE_NAME = 'TEST CLASS: DB CONVERT PLACEHOLDER';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";

View File

@@ -7,7 +7,7 @@
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -7,7 +7,7 @@
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -7,7 +7,7 @@
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -53,6 +53,9 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
} else {
print "NO DB HANDLER<br>";
}
// REGEX for placeholder count
print "Placeholder regex: <pre>" . CoreLibs\DB\Support\ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS . "</pre>";
// turn on debug replace for placeholders
$db->dbSetDebugReplacePlaceholder(true);
@@ -62,59 +65,115 @@ $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 = <<<SQL
INSERT INTO test_foo (
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
some_time, some_timestamp, json_string, null_var,
-- row 1
test, some_bool, string_a, number_a, numeric_a, smallint_a,
-- row 2
some_internval, some_timestamp, json_string, null_var,
-- row 3
array_char_1, array_int_1,
-- row 4
array_composite,
-- row 5
composite_item,
some_binary
-- row 6
some_binary,
-- row 7
some_date, some_time,
-- row 8
array_char_2, array_int_2,
-- row 9
bigint_a, number_real, number_double, numeric_3,
-- row 10
uuid_var
) VALUES (
-- row 1
$1, $2, $3, $4, $5, $6,
-- row 2
$7, $8, $9, $10,
-- row 3
$11, $12,
-- row 4
$13,
-- row 5
$14,
$15
-- row 6
$15,
-- row 7
$16, $17,
-- row 8
$18, $19,
-- row 9
$20, $21, $22, $23,
-- row 10
$24
)
RETURNING
test_foo_id,
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
some_time, some_timestamp, json_string, null_var,
test_foo_id, number_serial, identity_always, identitiy_default, default_uuid,
test, some_bool, string_a, number_a, numeric_a, smallint_a,
some_internval, some_timestamp, json_string, null_var,
array_char_1, array_int_1,
array_composite,
composite_item,
some_binary
some_binary,
some_date,
array_char_2, array_int_2,
bigint_a, number_real, number_double, numeric_3,
uuid_var
SQL;
$status = $db->dbExecParams($query_insert, $query_params);
echo "<b>*</b><br>";
echo "INSERT ALL COLUMN TYPES: "
. Support::printToString($query_params) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>"
. "QUERY: <pre>" . $db->dbGetQuery() . "</pre> |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |<br>"
. "RETURNING EXT: <pre>" . print_r($db->dbGetReturningExt(), true) . "</pre> |<br>"
. "RETURNING RETURN: <pre>" . print_r($db->dbGetReturningArray(), true) . "<pre> |<br>"
. "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<hr>";
print "<b>ANY call</b><br>";
$query = <<<SQL
SELECT test
FROM test_foo
WHERE string_a = ANY($1)
SQL;
$query_value = '{'
. join(',', ['STRING A'])
. '}';
while (is_array($res = $db->dbReturnParams($query, [$query_value]))) {
print "Result: " . Support::prAr($res) . "<br>";
}
echo "<hr>";
// test connectors: = , <> () for query detection
// convert placeholder tests
@@ -131,6 +190,16 @@ SQL,
'params' => [],
'direction' => 'pg',
],
'numbers' => [
'query' => <<<SQL
SELECT test, string_a, number_a
FROM test_foo
WHERE
foo = $1 AND bar = $1 AND foobar = $2
SQL,
'params' => [\CoreLibs\Create\Uids::uniqIdShort(), 'string A-1', 1234],
'direction' => 'pdo',
],
'a?' => [
'query' => <<<SQL
INSERT INTO test_foo (
@@ -157,6 +226,18 @@ SQL,
],
'direction' => 'pg',
],
'select, compare $' => [
'query' => <<<SQL
SELECT string_a
FROM test_foo
WHERE
number_a >= $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);
@@ -169,11 +250,12 @@ foreach ($test_queries as $info => $data) {
// . "<br>";
if ($db->dbCheckQueryForSelect($query)) {
$row = $db->dbReturnRowParams($query, $params);
print "[$info] SELECT: " . Support::prAr($row) . "<br>";
print "<b>[$info]</b> SELECT: " . Support::prAr($row) . "<br>";
} else {
$db->dbExecParams($query, $params);
}
print "[$info] " . Support::printAr($db->dbGetPlaceholderConverted()) . "<br>";
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
print "<b>[$info]</b> " . Support::printAr($db->dbGetPlaceholderConverted()) . "<br>";
echo "<hr>";
}
@@ -188,22 +270,29 @@ SQL,
['string A-1']
))
) {
print "RES: " . Support::prAr($res) . "<br>";
print "<b>RES</b>: " . Support::prAr($res) . "<br>";
}
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<hr>";
print "CursorExt: " . Support::prAr($db->dbGetCursorExt(<<<SQL
SELECT test, string_a, number_a
FROM test_foo
WHERE string_a = ?
SQL, ['string A-1']));
echo "<hr>";
// ERROR BELOW: missing params
$res = $db->dbReturnRowParams(<<<SQL
SELECT test, string_a, number_a
FROM test_foo
WHERE string_a = $1
SQL, []);
print "PL: " . Support::PrAr($db->dbGetPlaceholderConverted()) . "<br>";
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<hr>";
// ERROR BELOW: LIKE cannot have placeholder
echo "dbReturn read LIKE: <br>";
while (
is_array($res = $db->dbReturnParams(
@@ -217,6 +306,7 @@ SQL,
) {
print "RES: " . Support::prAr($res) . "<br>";
}
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
print "</body></html>";
$db->log->debug('DEBUGEND', '==================================== [END]');

View File

@@ -7,7 +7,7 @@
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -7,7 +7,7 @@
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -12,7 +12,7 @@ $PRINT_ALL = false;
$ECHO_ALL = true;
$DB_DEBUG = true;
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -18,6 +18,7 @@ require 'config.php';
$LOG_FILE_ID = 'classTest-encryption';
ob_end_flush();
use CoreLibs\Security\AsymmetricAnonymousEncryption;
use CoreLibs\Security\SymmetricEncryption;
use CoreLibs\Security\CreateKey;
@@ -36,6 +37,8 @@ print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "<h2>Symmetric Encryption</h2>";
$key = CreateKey::generateRandomKey();
print "Secret Key: " . $key . "<br>";
@@ -105,6 +108,49 @@ try {
// $encrypted = $se->encrypt($string);
// $decrypted = $se->decrypt($encrypted);
echo "<hr>";
print "<h2>Asymmetric Encryption</h2>";
$key_pair = CreateKey::createKeyPair();
$public_key = CreateKey::getPublicKey($key_pair);
$string = "I am some asymmetric secret";
print "Message: " . $string . "<br>";
$encrypted = sodium_crypto_box_seal($string, CreateKey::hex2bin($public_key));
$message = sodium_bin2base64($encrypted, SODIUM_BASE64_VARIANT_ORIGINAL);
print "Encrypted PL: " . $message . "<br>";
$result = sodium_base642bin($message, SODIUM_BASE64_VARIANT_ORIGINAL);
$decrypted = sodium_crypto_box_seal_open($result, CreateKey::hex2bin($key_pair));
print "Decrypted PL: " . $decrypted . "<br>";
$encrypted = AsymmetricAnonymousEncryption::encryptKey($string, $public_key);
print "Encrypted ST: " . $encrypted . "<br>";
$decrypted = AsymmetricAnonymousEncryption::decryptKey($encrypted, $key_pair);
print "Decrypted ST: " . $decrypted . "<br>";
$aa_crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key);
$encrypted = $aa_crypt->encrypt($string);
print "Encrypted: " . $encrypted . "<br>";
$decrypted = $aa_crypt->decrypt($encrypted);
print "Decrypted: " . $decrypted . "<br>";
print "Base64 encode: " . base64_encode('Some text here') . "<Br>";
/// this has to fail
$crypt = new AsymmetricAnonymousEncryption();
$crypt->setPublicKey(CreateKey::getPublicKey(CreateKey::createKeyPair()));
print "Public Key: " . $crypt->getPublicKey() . "<br>";
try {
$crypt->setPublicKey(CreateKey::createKeyPair());
} catch (RangeException $e) {
print "Invalid range: <pre>$e</pre>";
}
try {
$crypt->setKeyPair(CreateKey::getPublicKey(CreateKey::createKeyPair()));
} catch (RangeException $e) {
print "Invalid range: <pre>$e</pre>";
}
print "</body></html>";
// __END__

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -74,8 +74,8 @@ print "EL_O: <pre>" . print_r($el_o, true) . "</pre>";
echo "<hr>";
print "buildHtml(): <pre>" . htmlentities($el_o->buildHtml()) . "</pre>";
echo "<hr>";
print "phfo(\$el_o): <pre>" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "</pre>";
/* echo "<hr>";
print "phfo(\$el_o): <pre>" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "</pre>"; */
echo "<hr>";
print "phfa(\$el_list): <pre>" . htmlentities($el_o::buildHtmlFromList($el_o_list, true)) . "</pre>";

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -16,6 +16,8 @@ define('USE_DATABASE', false);
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-lang';
$SET_SESSION_NAME = EDIT_SESSION_NAME;
$session = new CoreLibs\Create\Session($SET_SESSION_NAME);
ob_end_flush();
$PAGE_NAME = 'TEST CLASS: LANG';
@@ -32,22 +34,21 @@ use CoreLibs\Debug\Support;
echo "<br><b>LIST LOCALES</b><br>";
$locale = 'en_US.UTF-8';
$locales = L10n::listLocales($locale);
$locales = Language\L10n::listLocales($locale);
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
$locale = 'en.UTF-8';
$locales = L10n::listLocales($locale);
$locales = Language\L10n::listLocales($locale);
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
echo "<br><b>PARSE LOCAL</b><br>";
$locale = 'en_US.UTF-8';
$locale_info = L10n::parseLocale($locale);
$locale_info = Language\L10n::parseLocale($locale);
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
$locale = 'en.UTF-8';
$locale_info = L10n::parseLocale($locale);
$locale_info = Language\L10n::parseLocale($locale);
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
echo "<br><b>AUTO DETECT</b><br>";
/* echo "<br><b>AUTO DETECT</b><br>";
// DEPRECATED
// $get_locale = Language\GetLocale::setLocale();
// print "[AUTO, DEPRECATED]: " . Support::printAr($get_locale) . "<br>";
@@ -70,10 +71,12 @@ print "[OVERRIDE]: " . Support::printAr($get_locale) . "<br>";
// DEFAULT_DOMAIN
// DEFAULT_CHARSET (should be set from DEFAULT_LOCALE)
// LOCALE_PATH
$_SESSION['DEFAULT_LOCALE'] = 'ja_JP.UTF-8';
$_SESSION['DEFAULT_CHARSET'] = 'UTF-8';
$_SESSION['DEFAULT_DOMAIN'] = 'admin';
$_SESSION['LOCALE_PATH'] = BASE . INCLUDES . LOCALE;
$session->setMany([
'DEFAULT_LOCALE' => 'ja_JP.UTF-8',
'DEFAULT_CHARSET' => 'UTF-8',
'DEFAULT_DOMAIN' => 'admin',
'LOCALE_PATH' => BASE . INCLUDES . LOCALE,
]);
$get_locale = Language\GetLocale::setLocaleFromSession(
SITE_LOCALE,
SITE_DOMAIN,
@@ -86,10 +89,12 @@ print "[SESSION SET]: " . Support::printAr($get_locale) . "<br>";
// DEFAULT_DOMAIN
// DEFAULT_CHARSET (should be set from DEFAULT_LOCALE)
// LOCALE_PATH
$_SESSION['DEFAULT_LOCALE'] = '00000#####';
$_SESSION['DEFAULT_CHARSET'] = '';
$_SESSION['DEFAULT_DOMAIN'] = 'admin';
$_SESSION['LOCALE_PATH'] = BASE . INCLUDES . LOCALE;
$session->setMany([
'DEFAULT_LOCALE' => '00000#####',
'DEFAULT_CHARSET' => '',
'DEFAULT_DOMAIN' => 'admin',
'LOCALE_PATH' => BASE . INCLUDES . LOCALE,
]);
$get_locale = Language\GetLocale::setLocaleFromSession(
SITE_LOCALE,
SITE_DOMAIN,
@@ -97,6 +102,7 @@ $get_locale = Language\GetLocale::setLocaleFromSession(
BASE . INCLUDES . LOCALE
);
print "[SESSION SET INVALID]: " . Support::printAr($get_locale) . "<br>";
*/
// try to load non existing
echo "<br><b>NEW TYPE</b><br>";

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -17,8 +17,14 @@ 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);
$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,
@@ -43,23 +49,87 @@ ob_end_flush();
$login->loginMainCall();
$PAGE_NAME = 'TEST CLASS: LOGIN';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print str_replace(
'{PAGE_NAME}',
$PAGE_NAME,
<<<HTML
<!DOCTYPE html>
<html><head>
<title>{PAGE_NAME}</title>
</head>
<body>
<div><a href="class_test.php">Class Test Master</a></div>
<div><h1>{PAGE_NAME}</h1></div>
HTML
);
// button logout
print <<<HTML
<script language="JavaScript">
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();
}
</script>
<div style="margin: 20px 0;">
<button onclick="loginLogout();" type="button">Logout</button>
</div>
HTML;
// string logout
print <<<HTML
<div style="margin: 20px 0;">
<form method="post" name="loginlogout">
<a href="javascript:document.loginlogout.login_logout.value=Logout;document.loginlogout.submit();">Logout</a>
<input type="hidden" name="login_logout" value="">
</form>
</div>
HTML;
echo "SESSION ID: " . $session->getSessionIdCall() . "<br>";
echo "CHECK PERMISSION: " . ($login->loginCheckPermissions() ? 'OK' : 'BAD') . "<br>";
echo "IS ADMIN: " . ($login->loginIsAdmin() ? 'OK' : 'BAD') . "<br>";
echo "MIN ACCESS BASE: " . ($login->loginCheckAccessBase('admin') ? 'OK' : 'BAD') . "<br>";
echo "MIN ACCESS PAGE: " . ($login->loginCheckAccessPage('admin') ? 'OK' : 'BAD') . "<br>";
echo "ACL: " . \CoreLibs\Debug\Support::printAr($login->loginGetAcl()) . "<br>";
echo "ACL (MIN): " . \CoreLibs\Debug\Support::printAr($login->loginGetAcl()['min'] ?? []) . "<br>";
echo "LOCALE: " . \CoreLibs\Debug\Support::printAr($login->loginGetLocale()) . "<br>";
echo "ACL: " . Support::printAr($login->loginGetAcl()) . "<br>";
echo "ACL (MIN): " . Support::printAr($login->loginGetAcl()['min'] ?? []) . "<br>";
echo "LOCALE: " . Support::printAr($login->loginGetLocale()) . "<br>";
echo "ECUID: " . $login->loginGetEcuid() . "<br>";
echo "ECUUID: " . $login->loginGetEcuuid() . "<br>";
echo "ECUID: " . $login->loginGetEuCuid() . "<br>";
echo "ECUUID: " . $login->loginGetEuCuuid() . "<br>";
echo "<hr>";
// set + check edit access id
$edit_access_cuid = 'buRW8Gu2Lkkf';
if (isset($login->loginGetAcl()['unit'])) {
print "EDIT ACCESS CUID: " . $edit_access_cuid . "<br>";
print "ACL UNIT: " . print_r(array_keys($login->loginGetAcl()['unit']), true) . "<br>";
print "ACCESS CHECK: " . Support::prBl($login->loginCheckEditAccessCuid($edit_access_cuid)) . "<br>";
if ($login->loginCheckEditAccessCuid($edit_access_cuid)) {
print "Set new:" . $edit_access_cuid . "<br>";
} else {
print "Load default unit id: " . $login->loginGetAcl()['unit_id'] . "<br>";
}
} else {
print "Something went wrong with the login<br>";
}
// echo "<hr>";
// IP check: 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'CLIENT_IP' in _SERVER
// Agent check: 'HTTP_USER_AGENT'
echo "<hr>";
print "SESSION: " . Support::printAr($_SESSION) . "<br>";
$login->writeLog(
'TEST LOG',

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -62,15 +62,36 @@ $backend = new CoreLibs\Admin\Backend(
$backend->db->dbInfo(true);
ob_end_flush();
print "<!DOCTYPE html>";
print "<html><head><title>TEST CLASS</title></head>";
print "<body>";
print <<<HTML
<!DOCTYPE html>
<html><head>
<title>TEST CLASS</title>
<script language="JavaScript">
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();
}
</script>
</head>
<body>
<div style="margin: 20px 0;">
<button onclick="loginLogout();" type="button">Logout</button>
</div>
HTML;
// key: file name, value; name
$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',
@@ -128,33 +149,20 @@ foreach ($test_files as $file => $name) {
print '<div><a href="' . $file . '">' . $name . '</a></div>';
}
print "<br>";
print "ECUID: " . $session->get('LOGIN_EUCUID') . "<br>";
print "ECUUID: " . $session->get('LOGIN_EUCUUID') . "<br>";
print "<hr>";
print "L: " . Support::dumpVar($locale) . "<br>";
print "LOCALE: " . Support::dumpVar($locale) . "<br>";
// print all _ENV vars set
print "<div>READ _ENV ARRAY:</div>";
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) . "<br>";
print "ACCESS CHECK: " . (string)$login->loginCheckEditAccess($edit_access_id) . "<br>";
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<br>";
}
// $backend->log->debug('SESSION', \CoreLibs\Debug\Support::dumpVar($_SESSION));
print '<form method="post" name="loginlogout">';
print '<a href="javascript:document.loginlogout.login_logout.value=\'Logou\';'
. 'document.loginlogout.submit();">Logout</a>';
print '<input type="hidden" name="login_logout" value="">';
print '</form>';
print "<br>";
print "Log Level: " . $backend->log->getLoggingLevel()->getName() . "<br>";
print "Log ID: " . $backend->log->getLogFileId() . "<br>";
print "Log Date: " . $backend->log->getLogDate() . "<br>";
@@ -176,26 +184,7 @@ foreach (
$log->debug('SOME MARK', 'Some error output');
// INTERNAL SET
print "EDIT ACCESS ID: " . $backend->edit_access_id . "<br>";
// print "ACL: <br>".$backend->print_ar($login->loginGetAcl())."<br>";
// $log->debug('ACL', "ACL: " . \CoreLibs\Debug\Support::dumpVar($login->loginGetAcl()));
// print "DEFAULT ACL: <br>".$backend->print_ar($login->default_acl_list)."<br>";
// print "DEFAULT ACL: <br>".$backend->print_ar($login->default_acl_list)."<br>";
// $result = array_flip(
// array_filter(
// array_flip($login->default_acl_list),
// function ($key) {
// if (is_numeric($key)) {
// return $key;
// }
// }
// )
// );
// print "DEFAULT ACL: <br>".$backend->print_ar($result)."<br>";
// DEPRICATED CALL
// $backend->adbSetACL($login->loginGetAcl());
print "<br>";
print "THIS HOST: " . HOST_NAME . ", with PROTOCOL: " . HOST_PROTOCOL . " is running SSL: " . HOST_SSL . "<br>";
print "DIR: " . DIR . "<br>";
print "BASE: " . BASE . "<br>";
@@ -205,8 +194,8 @@ print "HOST: " . HOST_NAME . " => DB HOST: " . DB_CONFIG_NAME . " => " . Support
print "DS is: " . DIRECTORY_SEPARATOR . "<br>";
print "SERVER HOST: " . $_SERVER['HTTP_HOST'] . "<br>";
print "ECUID: " . $_SESSION['ECUID'] . "<br>";
print "ECUUID: " . $_SESSION['ECUUID'] . "<br>";
print "<div>READ _SERVER ARRAY:</div>";
print Support::dumpVar(array_map('htmlentities', $_SERVER));
print "</body></html>";

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
@@ -34,10 +34,12 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
// 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 . "<br>";
print "AFTER reading test.env file: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
print "AFTER reading test.env file: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>"; */
print "</body></html>";
// ;;

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
/**
* Undocumented function
@@ -45,6 +45,7 @@ $log = new CoreLibs\Logging\Logging([
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
use CoreLibs\Debug\Support;
use CoreLibs\Create\Session;
$PAGE_NAME = 'TEST CLASS: SESSION';
@@ -56,7 +57,7 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$session_name = 'class-test-session';
print "Valid session name static check for '" . $session_name . "': "
. \CoreLibs\Debug\Support::prBl(Session::checkValidSessionName($session_name)) . "<br>";
. Support::prBl(Session::checkValidSessionName($session_name)) . "<br>";
$var = 'foo';
$value = 'bar';
$session = new Session($session_name);
@@ -96,13 +97,27 @@ print "[READ WRAP] Isset: " . ($session->isset('setwrap') ? 'Yes' : 'No') . "<br
$session->unset('setwrap');
print "[READ WRAP] unset setwrap: " . $session->get('setwrap') . "<br>";
print "[READ WRAP] unset Isset: " . ($session->isset('setwrap') ? 'Yes' : 'No') . "<br>";
// test __get/__set
$session->setwrap = 'YES, magic set _SESSION var'; /** @phpstan-ignore-line GET/SETTER */
print "[READ MAGIC] A setwrap: " . ($session->setwrap ?? '') . "<br>";
print "[READ MAGIC] Isset: " . (isset($session->setwrap) ? 'Yes' : 'No') . "<br>";
unset($session->setwrap);
print "[READ MAGIC] unset setwrap: " . ($session->setwrap ?? '') . "<br>";
print "[READ MAGIC] unset Isset: " . (isset($session->setwrap) ? 'Yes' : 'No') . "<br>";
$session->set('foo 3', 'brause');
// set many
$session->setMany([
'foo 1' => 'bar',
'foo 2' => 'kamel',
]);
print "[READ MANY]: " . Support::printAr($session->getMany(['foo 1', 'foo 2'])) . "<br>";
try {
$session->setMany([ /** @phpstan-ignore-line deliberate error */
'ok' => 'ok',
'a123' => 'bar',
1 => 'bar',
]);
} catch (\Exception $e) {
print "FAILED] Session manySet failed:<br>" . $e->getMessage() . "<br><pre>" . $e . "</pre><br>";
}
try {
$session->set('123', 'illigal');
} catch (\Exception $e) {
print "FAILED] Session set failed:<br>" . $e->getMessage() . "<br><pre>" . $e . "</pre><br>";
}
print "<hr>";
// differnt session name
@@ -115,7 +130,8 @@ try {
print "[3 FAILED] Session start failed:<br>" . $e->getMessage() . "<br><pre>" . $e . "</pre><br>";
}
print "[ALL SESSION]: " . \CoreLibs\Debug\Support::printAr($_SESSION) . "<br>";
print "[ALL SESSION]: " . Support::printAr($_SESSION) . "<br>";
// close session
$session->writeClose();
@@ -130,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() . "<br>";
print "[4 SET] Current session auto write close: " . ($session_alt->checkAutoWriteClose() ? 'Yes' : 'No') . "<br>";
print "[START AGAIN] Current session id: " . $session_alt->getSessionId() . "<br>";
@@ -142,7 +158,7 @@ try {
}
$_SESSION['will_be_written_again'] = 'Full';
print "[ALL SESSION]: " . \CoreLibs\Debug\Support::printAr($_SESSION) . "<br>";
print "[ALL SESSION]: " . Support::printAr($_SESSION) . "<br>";
// close session
$session->writeClose();

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
/**
* Undocumented function

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

View File

@@ -4,7 +4,7 @@
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -358,7 +358,7 @@ class Backend
}
$q = <<<SQL
INSERT INTO {DB_SCHEMA}.edit_log (
username, euid, ecuid, ecuuid, event_date, event, error, data, data_binary, page,
username, euid, eucuid, eucuuid, event_date, event, error, data, data_binary, page,
ip, user_agent, referer, script_name, query_string, server_name, http_host,
http_accept, http_accept_charset, http_accept_encoding, session_id,
action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded,
@@ -380,12 +380,12 @@ class Backend
[
// row 1
'',
!empty($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
$_SESSION['EUID'] : null,
!empty($_SESSION['ECUID']) && is_string($_SESSION['ECUID']) ?
$_SESSION['ECUID'] : null,
!empty($_SESSION['ECUUID']) && Uids::validateUuuidv4($_SESSION['ECUID']) ?
$_SESSION['ECUID'] : null,
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('ECUID')) ?
$this->session->get('ECUID') : null,
(string)$event,
'',
$data_write,
@@ -468,7 +468,7 @@ class Backend
}
// get the session pages array
$PAGES = $_SESSION['PAGES'] ?? null;
$PAGES = $this->session->get('PAGES');
if (!isset($PAGES) || !is_array($PAGES)) {
$PAGES = [];
}

View File

@@ -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');

View File

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

View File

@@ -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<string> allowed session id regenerate (rotate) options */
private const ALLOWED_REGENERATE_OPTIONS = ['none', 'random', 'interval'];
/** @var int default random interval */
public const DEFAULT_REGENERATE_RANDOM = 100;
/** @var int default rotate internval in minutes */
public const DEFAULT_REGENERATE_INTERVAL = 5 * 60;
/** @var int maximum time for regenerate interval is one hour */
public const MAX_REGENERATE_INTERAL = 60 * 60;
/**
* init a session, if array is empty or array does not have session_name set
* then no auto init is run
*
* @param string $session_name if set and not empty, will start session
* @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options
*/
public function __construct(string $session_name, 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{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $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
*
@@ -97,6 +249,23 @@ class Session
return true;
}
/**
* validate _SESSION key, must be valid variable
*
* @param int|float|string $key
* @return true
*/
private function checkValidSessionEntryKey(int|float|string $key): true
{
if (!is_string($key) || is_numeric($key)) {
throw new \UnexpectedValueException(
'[SESSION] Given key for _SESSION is not a valid value for a varaible: ' . $key,
1
);
}
return true;
}
// MARK: init session (on class start)
/**
@@ -134,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(),
@@ -142,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();
@@ -162,6 +340,9 @@ class Session
public function restartSession(): bool
{
if (!$this->checkActiveSession()) {
if (empty($this->session_name)) {
throw new \RuntimeException('[SESSION] Cannot restart session without a session name', 1);
}
$this->startSessionCall();
return true;
}
@@ -182,11 +363,12 @@ class Session
* set the auto write close flag
*
* @param bool $flag
* @return void
* @return Session
*/
public function setAutoWriteClose(bool $flag): void
public function setAutoWriteClose(bool $flag): Session
{
$this->auto_write_close = $flag;
return $this;
}
/**
@@ -235,6 +417,21 @@ class Session
return \CoreLibs\Get\System::checkCLI();
}
/**
* get session status
* PHP_SESSION_DISABLED if sessions are disabled.
* PHP_SESSION_NONE if sessions are enabled, but none exists.
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
*
* https://www.php.net/manual/en/function.session-status.php
*
* @return int See possible return int values above
*/
public function getSessionStatus(): int
{
return session_status();
}
// MARK: write close session
/**
@@ -256,13 +453,18 @@ class Session
* Proper destroy a session
* - unset the _SESSION array
* - unset cookie if cookie on and we have not strict mode
* - unset session_name and session_id internal vars
* - destroy session
*
* @return bool
* @return bool True on successful session destroy
*/
public function sessionDestroy(): bool
{
$_SESSION = [];
// abort to false if not unsetable
if (!session_unset()) {
return false;
}
$this->clear();
if (
ini_get('session.use_cookies') &&
!ini_get('session.use_strict_mode')
@@ -282,24 +484,12 @@ class Session
$params['httponly']
);
}
// unset internal vars
$this->session_name = '';
$this->session_id = '';
return session_destroy();
}
/**
* get session status
* PHP_SESSION_DISABLED if sessions are disabled.
* PHP_SESSION_NONE if sessions are enabled, but none exists.
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
*
* https://www.php.net/manual/en/function.session-status.php
*
* @return int See possible return int values above
*/
public function getSessionStatus(): int
{
return session_status();
}
// MARK: _SESSION set/unset methods
/**
@@ -307,11 +497,14 @@ class Session
*
* @return void
*/
public function unsetAll(): void
public function clear(): void
{
$this->restartSession();
foreach (array_keys($_SESSION ?? []) as $name) {
unset($_SESSION[$name]);
if (!session_unset()) {
throw new \RuntimeException('[SESSION] Cannot unset session vars', 1);
}
if (!empty($_SESSION)) {
$_SESSION = [];
}
$this->closeSessionCall();
}
@@ -319,35 +512,65 @@ class Session
/**
* set _SESSION entry 'name' with any value
*
* @param string|int $name array name in _SESSION
* @param mixed $value value to set (can be anything)
* @return void
* @param string $name array name in _SESSION
* @param mixed $value value to set (can be anything)
* @return Session
*/
public function set(string|int $name, mixed $value): void
public function set(string $name, mixed $value): Session
{
$this->checkValidSessionEntryKey($name);
$this->restartSession();
$_SESSION[$name] = $value;
$this->closeSessionCall();
return $this;
}
/**
* set many session entries in one set
*
* @param array<string,mixed> $set key is the key in the _SESSION, value is any data to set
* @return void
*/
public function setMany(array $set): void
{
$this->restartSession();
// skip any that are not valid
foreach ($set as $key => $value) {
$this->checkValidSessionEntryKey($key);
$_SESSION[$key] = $value;
}
$this->closeSessionCall();
}
/**
* get _SESSION 'name' entry or empty string if not set
*
* @param string|int $name value key to get from _SESSION
* @return mixed value stored in _SESSION
* @param string $name value key to get from _SESSION
* @return mixed value stored in _SESSION, if not found set to null
*/
public function get(string|int $name): mixed
public function get(string $name): mixed
{
return $_SESSION[$name] ?? null;
}
/**
* get multiple session entries
*
* @param array<string> $set
* @return array<string,mixed>
*/
public function getMany(array $set): array
{
return array_intersect_key($_SESSION, array_flip($set));
}
/**
* Check if a name is set in the _SESSION array
*
* @param string|int $name Name to check for
* @return bool True for set, False fornot set
* @param string $name Name to check for
* @return bool True for set, False fornot set
*/
public function isset(string|int $name): bool
public function isset(string $name): bool
{
return isset($_SESSION[$name]);
}
@@ -355,78 +578,35 @@ class Session
/**
* unset one _SESSION entry 'name' if exists
*
* @param string|int $name _SESSION key name to remove
* @return void
* @param string $name _SESSION key name to remove
* @return Session
*/
public function unset(string|int $name): void
public function unset(string $name): Session
{
if (!isset($_SESSION[$name])) {
return;
return $this;
}
$this->restartSession();
unset($_SESSION[$name]);
$this->closeSessionCall();
return $this;
}
// MARK: [DEPRECATED] __set/__get magic methods
// ->var = value;
/**
* Undocumented function
* reset many session entry
*
* @param string|int $name
* @param mixed $value
* @param array<string> $set list of session keys to reset
* @return void
* @deprecated use ->set()
*/
public function __set(string|int $name, mixed $value): void
public function unsetMany(array $set): void
{
$this->restartSession();
$_SESSION[$name] = $value;
$this->closeSessionCall();
}
/**
* Undocumented function
*
* @param string|int $name
* @return mixed If name is not found, it will return null
* @deprecated use ->get()
*/
public function __get(string|int $name): mixed
{
if (isset($_SESSION[$name])) {
return $_SESSION[$name];
foreach ($set as $key) {
if (!isset($_SESSION[$key])) {
continue;
}
unset($_SESSION[$key]);
}
return null;
}
/**
* Undocumented function
*
* @param string|int $name
* @return bool
* @deprecated use ->isset()
*/
public function __isset(string|int $name): bool
{
return isset($_SESSION[$name]);
}
/**
* Undocumented function
*
* @param string|int $name
* @return void
* @deprecated use ->unset()
*/
public function __unset(string|int $name): void
{
if (!isset($_SESSION[$name])) {
return;
}
$this->restartSession();
unset($_SESSION[$name]);
$this->closeSessionCall();
}
}

View File

@@ -14,10 +14,24 @@ 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 =
'\?\?|' // UNKNOWN: double ??, is this to avoid something?
. '[\(,]|' // for ',' and '(' mostly in INSERT or ANY()
. '[<>=]|' // general set for <, >, = in any query with any combination
. '\^@|' // text search for start from text with ^@
. '\|\||' // concats two elements
. '&&|' // array overlap
. '\-\|\-|' // range overlap for array
. '[^-]-{1}|' // single -, used in JSON too
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-'; //JSON searches, Array searchs, etc
/** @var string the main regex including the pattern query split */
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
@@ -34,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
@@ -42,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
@@ -50,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
@@ -60,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

View File

@@ -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',

View File

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

View File

@@ -0,0 +1,408 @@
<?php
/**
* very simple asymmetric encryption
* Better use:
* https://paragonie.com/project/halite
* https://github.com/paragonie/halite
*
* current code is just to encrypt and decrypt
*
* must use a valid encryption key created with
* Secruty\CreateKey class
*/
declare(strict_types=1);
namespace CoreLibs\Security;
use CoreLibs\Security\CreateKey;
use SodiumException;
class AsymmetricAnonymousEncryption
{
/** @var AsymmetricAnonymousEncryption self instance */
private static AsymmetricAnonymousEncryption $instance;
/** @var ?string key pair which holds secret and public key, needed for encryption */
private ?string $key_pair = null;
/** @var ?string public key, needed for decryption
* if not set but key_pair set, this will be extracted from key pair */
private ?string $public_key = null;
/**
* init class
* if key not passed, key must be set with createKey
*
* @param string|null $key_pair
* @param string|null $public_key
*/
public function __construct(
#[\SensitiveParameter]
string|null $key_pair = null,
string|null $public_key = null
) {
if ($public_key !== null) {
$this->setPublicKey($public_key);
}
if ($key_pair !== null) {
$this->setKeyPair($key_pair);
if (empty($public_key)) {
$public_key = CreateKey::getPublicKey($key_pair);
$this->setPublicKey($public_key);
}
}
}
/**
* Returns the singleton self object.
* For function wrapper use
*
* @param string|null $key_pair
* @param string|null $public_key
* @return AsymmetricAnonymousEncryption object
*/
public static function getInstance(
#[\SensitiveParameter]
string|null $key_pair = null,
string|null $public_key = null
): self {
// new if no instsance or key is different
if (
empty(self::$instance) ||
self::$instance->key_pair != $key_pair ||
self::$instance->public_key != $public_key
) {
self::$instance = new self($key_pair, $public_key);
}
return self::$instance;
}
/**
* clean up
*/
public function __destruct()
{
if (empty($this->key_pair)) {
return;
}
try {
// would set it to null, but we we do not want to make key null
sodium_memzero($this->key_pair);
return;
} catch (SodiumException) {
// empty catch
}
if (is_null($this->key_pair)) {
return;
}
$zero = str_repeat("\0", mb_strlen($this->key_pair, '8bit'));
$this->key_pair = $this->key_pair ^ (
$zero ^ $this->key_pair
);
unset($zero);
unset($this->key_pair); /** @phan-suppress-current-line PhanTypeObjectUnsetDeclaredProperty */
}
/* ************************************************************************
* MARK: PRIVATE
* *************************************************************************/
/**
* Create the internal key pair in binary
*
* @param ?string $key_pair
* @return string
* @throws \UnexpectedValueException key pair empty
* @throws \UnexpectedValueException invalid hex key pair
* @throws \UnexpectedValueException key pair not correct size
*/
private function createKeyPair(
#[\SensitiveParameter]
?string $key_pair
): string {
if (empty($key_pair)) {
throw new \UnexpectedValueException('Key pair cannot be empty');
}
try {
$key_pair = CreateKey::hex2bin($key_pair);
} catch (SodiumException $e) {
sodium_memzero($key_pair);
throw new \UnexpectedValueException('Invalid hex key pair: ' . $e->getMessage());
}
if (mb_strlen($key_pair, '8bit') !== SODIUM_CRYPTO_BOX_KEYPAIRBYTES) {
sodium_memzero($key_pair);
throw new \RangeException(
'Key pair is not the correct size (must be '
. SODIUM_CRYPTO_BOX_KEYPAIRBYTES . ' bytes long).'
);
}
return $key_pair;
}
/**
* create the internal public key in binary
*
* @param ?string $public_key
* @return string
* @throws \UnexpectedValueException public key empty
* @throws \UnexpectedValueException invalid hex key
* @throws \UnexpectedValueException invalid key length
*/
private function createPublicKey(?string $public_key): string
{
if (empty($public_key)) {
throw new \UnexpectedValueException('Public key cannot be empty');
}
try {
$public_key = CreateKey::hex2bin($public_key);
} catch (SodiumException $e) {
sodium_memzero($public_key);
throw new \UnexpectedValueException('Invalid hex public key: ' . $e->getMessage());
}
if (mb_strlen($public_key, '8bit') !== SODIUM_CRYPTO_BOX_PUBLICKEYBYTES) {
sodium_memzero($public_key);
throw new \RangeException(
'Public key is not the correct size (must be '
. SODIUM_CRYPTO_BOX_PUBLICKEYBYTES . ' bytes long).'
);
}
return $public_key;
}
/**
* encrypt a message asymmetric with a bpulic key
*
* @param string $message
* @param ?string $public_key
* @return string
* @throws \UnexpectedValueException create encryption failed
* @throws \UnexpectedValueException convert to base64 failed
*/
private function asymmetricEncryption(
#[\SensitiveParameter]
string $message,
?string $public_key
): string {
$public_key = $this->createPublicKey($public_key);
try {
$encrypted = sodium_crypto_box_seal($message, $public_key);
} catch (SodiumException $e) {
sodium_memzero($message);
throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage());
}
sodium_memzero($message);
try {
$result = sodium_bin2base64($encrypted, SODIUM_BASE64_VARIANT_ORIGINAL);
} catch (SodiumException $e) {
sodium_memzero($encrypted);
throw new \UnexpectedValueException("bin2base64 failed: " . $e->getMessage());
}
sodium_memzero($encrypted);
return $result;
}
/**
* decrypt a message that is asymmetric encrypted with a key pair
*
* @param string $message
* @param ?string $key_pair
* @return string
* @throws \UnexpectedValueException message string empty
* @throws \UnexpectedValueException base64 decoding failed
* @throws \UnexpectedValueException decryption failed
* @throws \UnexpectedValueException could not decrypt message
*/
private function asymmetricDecryption(
#[\SensitiveParameter]
string $message,
#[\SensitiveParameter]
?string $key_pair
): string {
if (empty($message)) {
throw new \UnexpectedValueException('Encrypted string cannot be empty');
}
$key_pair = $this->createKeyPair($key_pair);
try {
$result = sodium_base642bin($message, SODIUM_BASE64_VARIANT_ORIGINAL);
} catch (SodiumException $e) {
sodium_memzero($message);
sodium_memzero($key_pair);
throw new \UnexpectedValueException("base642bin failed: " . $e->getMessage());
}
sodium_memzero($message);
$plaintext = false;
try {
$plaintext = sodium_crypto_box_seal_open($result, $key_pair);
} catch (SodiumException $e) {
sodium_memzero($message);
sodium_memzero($key_pair);
sodium_memzero($result);
throw new \UnexpectedValueException("Decrypting message failed: " . $e->getMessage());
}
sodium_memzero($key_pair);
sodium_memzero($result);
if (!is_string($plaintext)) {
throw new \UnexpectedValueException('Invalid key pair');
}
return $plaintext;
}
/* ************************************************************************
* MARK: PUBLIC
* *************************************************************************/
/**
* sets the private key for encryption
*
* @param string $key_pair Key pair in hex
* @return AsymmetricAnonymousEncryption
* @throws \UnexpectedValueException key pair empty
*/
public function setKeyPair(
#[\SensitiveParameter]
string $key_pair
): AsymmetricAnonymousEncryption {
if (empty($key_pair)) {
throw new \UnexpectedValueException('Key pair cannot be empty');
}
// check if valid;
$this->createKeyPair($key_pair);
// set new key pair
$this->key_pair = $key_pair;
sodium_memzero($key_pair);
// set public key if not set
if (empty($this->public_key)) {
$this->public_key = CreateKey::getPublicKey($this->key_pair);
// check if valid
$this->createPublicKey($this->public_key);
}
return $this;
}
/**
* check if set key pair matches given one
*
* @param string $key_pair
* @return bool
*/
public function compareKeyPair(
#[\SensitiveParameter]
string $key_pair
): bool {
return $this->key_pair === $key_pair;
}
/**
* get the current set key pair, null if not set
*
* @return string|null
*/
public function getKeyPair(): ?string
{
return $this->key_pair;
}
/**
* sets the public key for decryption
* if only key pair exists Security\Create::getPublicKey() can be used to
* extract the public key from the key pair
*
* @param string $public_key Public Key in hex
* @return AsymmetricAnonymousEncryption
* @throws \UnexpectedValueException public key empty
*/
public function setPublicKey(string $public_key): AsymmetricAnonymousEncryption
{
if (empty($public_key)) {
throw new \UnexpectedValueException('Public key cannot be empty');
}
// check if valid
$this->createPublicKey($public_key);
$this->public_key = $public_key;
sodium_memzero($public_key);
return $this;
}
/**
* check if the set public key matches the given one
*
* @param string $public_key
* @return bool
*/
public function comparePublicKey(string $public_key): bool
{
return $this->public_key === $public_key;
}
/**
* get the current set public key, null if not set
*
* @return string|null
*/
public function getPublicKey(): ?string
{
return $this->public_key;
}
/**
* Encrypt a message with a public key
* static version
*
* @param string $message Message to encrypt
* @param string $public_key Public key in hex to encrypt message with
* @return string Encrypted message as hex string
*/
public static function encryptKey(
#[\SensitiveParameter]
string $message,
string $public_key
): string {
return self::getInstance()->asymmetricEncryption($message, $public_key);
}
/**
* Encrypt a message
*
* @param string $message Message to ecnrypt
* @return string Encrypted message as hex string
*/
public function encrypt(
#[\SensitiveParameter]
string $message
): string {
return $this->asymmetricEncryption($message, $this->public_key);
}
/**
* decrypt a message with a key pair
* static version
*
* @param string $message Message to decrypt in hex
* @param string $key_pair Key pair in hex to decrypt the message with
* @return string Decrypted message
*/
public static function decryptKey(
#[\SensitiveParameter]
string $message,
#[\SensitiveParameter]
string $key_pair
): string {
return self::getInstance()->asymmetricDecryption($message, $key_pair);
}
/**
* decrypt a message
*
* @param string $message Message to decrypt in hex
* @return string Decrypted message
*/
public function decrypt(
#[\SensitiveParameter]
string $message
): string {
return $this->asymmetricDecryption($message, $this->key_pair);
}
}
// __END__

View File

@@ -35,14 +35,39 @@ class CreateKey
return random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
}
/**
* creates a sodium cyptobox keypair as hex string
*
* @return string hex string for the keypair
*/
public static function createKeyPair(): string
{
return self::bin2hex(sodium_crypto_box_keypair());
}
/**
* extracts the public key and returns it as hex string from the hex keypari
*
* @param string $hex_keypair hex encoded keypair
* @return string hex encoded public key
*/
public static function getPublicKey(
#[\SensitiveParameter]
string $hex_keypair
): string {
return self::bin2hex(sodium_crypto_box_publickey(self::hex2bin($hex_keypair)));
}
/**
* convert binary key to hex string
*
* @param string $hex_key Convert binary key string to hex
* @return string
*/
public static function bin2hex(string $hex_key): string
{
public static function bin2hex(
#[\SensitiveParameter]
string $hex_key
): string {
return sodium_bin2hex($hex_key);
}
@@ -52,8 +77,10 @@ class CreateKey
* @param string $string_key Convery hex key string to binary
* @return string
*/
public static function hex2bin(string $string_key): string
{
public static function hex2bin(
#[\SensitiveParameter]
string $string_key
): string {
return sodium_hex2bin($string_key);
}
}

View File

@@ -16,8 +16,10 @@ class Password
* @param string $password password
* @return string hashed password
*/
public static function passwordSet(string $password): string
{
public static function passwordSet(
#[\SensitiveParameter]
string $password
): string {
// always use the PHP default for the password
// password options ca be set in the password init,
// but should be kept as default
@@ -31,8 +33,11 @@ class Password
* @param string $hash password hash
* @return bool true or false
*/
public static function passwordVerify(string $password, string $hash): bool
{
public static function passwordVerify(
#[\SensitiveParameter]
string $password,
string $hash
): bool {
if (password_verify($password, $hash)) {
return true;
} else {

View File

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

View File

@@ -46,19 +46,19 @@ class qqUploadedFileXhr implements qqUploadedFile // phpcs:ignore Squiz.Classes.
*/
public function getName(): string
{
return $_GET['qqfile'] ?? '';
return !empty($_GET['qqfile']) && is_string($_GET['qqfile']) ? $_GET['qqfile'] : '';
}
/**
* Get file size from _SERVERa array, throws an error if not possible
*
* @return int
* @return int size of the file
*
* @throws \Exception
*/
public function getSize(): int
{
if (isset($_SERVER['CONTENT_LENGTH'])) {
if (isset($_SERVER['CONTENT_LENGTH']) && is_numeric($_SERVER['CONTENT_LENGTH'])) {
return (int)$_SERVER['CONTENT_LENGTH'];
} else {
throw new \Exception('Getting content length is not supported.');