Compare commits

..

12 Commits

Author SHA1 Message Date
Clemens Schwaighofer
c13934de99 Fix for wrong key handling in Symmetric encryption 2024-12-12 21:09:41 +09:00
Clemens Schwaighofer
ba11a936db DB IO remove debug placeholder output 2024-12-11 10:36:31 +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
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
a7742bd5c8 DB IO count params fix for comments 2024-12-10 13:36:57 +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
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
13 changed files with 422 additions and 59 deletions

View File

@@ -13,7 +13,7 @@ if [ "${GO}" != "go" ]; then
fi; fi;
BASE="/storage/var/www/html/developers/clemens/core_data/"; 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/" TARGET="${BASE}composer-packages/CoreLibs-Composer-All/"
rsync ${DRY_RUN}-Plzvrupt --stats --delete ${SOURCE}4dev/tests/ ${TARGET}test/phpunit/ rsync ${DRY_RUN}-Plzvrupt --stats --delete ${SOURCE}4dev/tests/ ${TARGET}test/phpunit/

View File

@@ -10,10 +10,10 @@ CREATE TABLE edit_log (
edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
euid INT, -- this is a foreign key, but I don't nedd to reference to it euid INT, -- this is a foreign key, but I don't nedd to reference to it
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL, FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
username VARCHAR,
password VARCHAR,
ecuid VARCHAR, ecuid VARCHAR,
ecuuid UUID, ecuuid UUID,
username VARCHAR,
password VARCHAR,
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
ip VARCHAR, ip VARCHAR,
error TEXT, error TEXT,

View File

@@ -321,7 +321,7 @@ CREATE TABLE edit_generic (
-- DROP TABLE edit_visible_group; -- DROP TABLE edit_visible_group;
CREATE TABLE edit_visible_group ( CREATE TABLE edit_visible_group (
edit_visible_group_id SERIAL PRIMARY KEY, edit_visible_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR, name VARCHAR,
flag VARCHAR flag VARCHAR
) INHERITS (edit_generic) WITHOUT OIDS; ) INHERITS (edit_generic) WITHOUT OIDS;
@@ -336,7 +336,7 @@ CREATE TABLE edit_visible_group (
-- DROP TABLE edit_menu_group; -- DROP TABLE edit_menu_group;
CREATE TABLE edit_menu_group ( CREATE TABLE edit_menu_group (
edit_menu_group_id SERIAL PRIMARY KEY, edit_menu_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR, name VARCHAR,
flag VARCHAR, flag VARCHAR,
order_number INT NOT NULL order_number INT NOT NULL
@@ -354,7 +354,7 @@ CREATE TABLE edit_menu_group (
-- DROP TABLE edit_page; -- DROP TABLE edit_page;
CREATE TABLE edit_page ( CREATE TABLE edit_page (
edit_page_id SERIAL PRIMARY KEY, edit_page_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages
FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE, FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
filename VARCHAR, filename VARCHAR,
@@ -378,7 +378,7 @@ CREATE TABLE edit_page (
-- DROP TABLE edit_query_string; -- DROP TABLE edit_query_string;
CREATE TABLE edit_query_string ( CREATE TABLE edit_query_string (
edit_query_string_id SERIAL PRIMARY KEY, edit_query_string_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_page_id INT NOT NULL, edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
@@ -430,7 +430,7 @@ CREATE TABLE edit_page_menu_group (
-- DROP TABLE edit_access_right; -- DROP TABLE edit_access_right;
CREATE TABLE edit_access_right ( CREATE TABLE edit_access_right (
edit_access_right_id SERIAL PRIMARY KEY, edit_access_right_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR, name VARCHAR,
level SMALLINT, level SMALLINT,
type VARCHAR, type VARCHAR,
@@ -447,7 +447,7 @@ CREATE TABLE edit_access_right (
-- DROP TABLE edit_scheme; -- DROP TABLE edit_scheme;
CREATE TABLE edit_scheme ( CREATE TABLE edit_scheme (
edit_scheme_id SERIAL PRIMARY KEY, edit_scheme_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR, name VARCHAR,
header_color VARCHAR, header_color VARCHAR,
@@ -466,7 +466,7 @@ CREATE TABLE edit_scheme (
-- DROP TABLE edit_language; -- DROP TABLE edit_language;
CREATE TABLE edit_language ( CREATE TABLE edit_language (
edit_language_id SERIAL PRIMARY KEY, edit_language_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
lang_default SMALLINT NOT NULL DEFAULT 0, lang_default SMALLINT NOT NULL DEFAULT 0,
long_name VARCHAR, long_name VARCHAR,
@@ -485,7 +485,7 @@ CREATE TABLE edit_language (
-- DROP TABLE edit_group; -- DROP TABLE edit_group;
CREATE TABLE edit_group ( CREATE TABLE edit_group (
edit_group_id SERIAL PRIMARY KEY, edit_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_scheme_id INT, edit_scheme_id INT,
FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL, edit_access_right_id INT NOT NULL,
@@ -507,7 +507,7 @@ CREATE TABLE edit_group (
-- DROP TABLE edit_page_access; -- DROP TABLE edit_page_access;
CREATE TABLE edit_page_access ( CREATE TABLE edit_page_access (
edit_page_access_id SERIAL PRIMARY KEY, edit_page_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_group_id INT NOT NULL, edit_group_id INT NOT NULL,
FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_page_id INT NOT NULL, edit_page_id INT NOT NULL,
@@ -530,7 +530,7 @@ CREATE TABLE edit_page_access (
-- DROP TABLE edit_page_content; -- DROP TABLE edit_page_content;
CREATE TABLE edit_page_content ( CREATE TABLE edit_page_content (
edit_page_content_id SERIAL PRIMARY KEY, edit_page_content_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_page_id INT NOT NULL, edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL, edit_access_right_id INT NOT NULL,
@@ -551,7 +551,7 @@ CREATE TABLE edit_page_content (
-- DROP TABLE edit_user; -- DROP TABLE edit_user;
CREATE TABLE edit_user ( CREATE TABLE edit_user (
edit_user_id SERIAL PRIMARY KEY, edit_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
connect_edit_user_id INT, -- possible reference to other user connect_edit_user_id INT, -- possible reference to other user
FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_language_id INT NOT NULL, edit_language_id INT NOT NULL,
@@ -652,11 +652,11 @@ COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List st
-- DROP TABLE edit_log; -- DROP TABLE edit_log;
CREATE TABLE edit_log ( CREATE TABLE edit_log (
edit_log_id SERIAL PRIMARY KEY, edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
euid INT, -- this is a foreign key, but I don't nedd to reference to it euid INT, -- this is a foreign key, but I don't nedd to reference to it
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
ecuid VARCHAR, ecuid VARCHAR,
ecuuid UUID, ecuuid UUID,
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
username VARCHAR, username VARCHAR,
password VARCHAR, password VARCHAR,
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
@@ -712,7 +712,7 @@ ALTER TABLE edit_log_overflow ADD CONSTRAINT edit_log_overflow_euid_fkey FOREIGN
-- DROP TABLE edit_access; -- DROP TABLE edit_access;
CREATE TABLE edit_access ( CREATE TABLE edit_access (
edit_access_id SERIAL PRIMARY KEY, edit_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,
protected SMALLINT DEFAULT 0, protected SMALLINT DEFAULT 0,
deleted SMALLINT DEFAULT 0, deleted SMALLINT DEFAULT 0,
@@ -733,7 +733,7 @@ CREATE TABLE edit_access (
-- DROP TABLE edit_access_user; -- DROP TABLE edit_access_user;
CREATE TABLE edit_access_user ( CREATE TABLE edit_access_user (
edit_access_user_id SERIAL PRIMARY KEY, edit_access_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_access_id INT NOT NULL, edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_user_id INT NOT NULL, edit_user_id INT NOT NULL,
@@ -754,7 +754,7 @@ CREATE TABLE edit_access_user (
-- DROP TABLE edit_access_data; -- DROP TABLE edit_access_data;
CREATE TABLE edit_access_data ( CREATE TABLE edit_access_data (
edit_access_data_id SERIAL PRIMARY KEY, edit_access_data_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_access_id INT NOT NULL, edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0, enabled SMALLINT NOT NULL DEFAULT 0,

View File

@@ -17,7 +17,7 @@ Table with Primary Key: table_with_primary_key
Table without Primary Key: table_without_primary_key Table without Primary Key: table_without_primary_key
Table with primary key has additional row: Table with primary key has additional row:
row_primary_key SERIAL PRIMARY KEY, row_primary_key INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Each table has the following rows Each table has the following rows
row_int INT, row_int INT,
row_numeric NUMERIC, row_numeric NUMERIC,
@@ -160,7 +160,6 @@ final class CoreLibsDBIOTest extends TestCase
// create the tables // create the tables
$db->dbExec( $db->dbExec(
// primary key name is table + '_id' // primary key name is table + '_id'
// table_with_primary_key_id SERIAL PRIMARY KEY,
<<<SQL <<<SQL
CREATE TABLE table_with_primary_key ( CREATE TABLE table_with_primary_key (
table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
@@ -5136,6 +5135,67 @@ final class CoreLibsDBIOTest extends TestCase
SQL, SQL,
'count' => 6, 'count' => 6,
'convert' => false, 'convert' => false,
],
'comments in insert' => [
'query' => <<<SQL
INSERT INTO table_with_primary_key (
row_int, row_numeric, row_varchar, row_varchar_literal
) VALUES (
-- comment 1 かな
$1, $2,
-- comment 2 -
$3
-- comment 3
, $4
)
SQL,
'count' => 4,
'convert' => false
],
'comment in update' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
row_int =
-- COMMENT 1
$1,
row_numeric =
$2 -- COMMENT 2
,
row_varchar -- COMMENT 3
= $3
WHERE
row_varchar = $4
SQL,
'count' => 4,
'convert' => false,
],
// Note some are not set
'a complete set of possible' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
-- ROW
row_varchar = $1
WHERE
row_varchar = ANY($2) AND row_varchar <> $3
AND row_varchar > $4 AND row_varchar < $5
AND row_varchar >= $6 AND row_varchar <=$7
AND row_jsonb->'a' = $8 AND row_jsonb->>$9 = 'a'
AND row_jsonb<@$10 AND row_jsonb@>$11
AND row_varchar ^@ $12
SQL,
'count' => 12,
'convert' => false,
],
// all the same
'all the same numbered' => [
'query' => <<<SQL
UPDATE table_with_primary_key SET
row_int = $1::INT, row_numeric = $1::NUMERIC, row_varchar = $1
WHERE
row_varchar = $1
SQL,
'count' => 1,
'convert' => false,
] ]
]; ];
} }

View File

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

View File

@@ -56,7 +56,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$decrypted, $decrypted,
'Class call', 'Class call',
); );
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt indirect $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccessIndirect(string $input, string $expected): void
{
$key = CreateKey::generateRandomKey();
// test indirect // test indirect
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
$decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted); $decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted);
@@ -65,7 +82,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$decrypted, $decrypted,
'Class Instance call', 'Class Instance call',
); );
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt static $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccessStatic(string $input, string $expected): void
{
$key = CreateKey::generateRandomKey();
// test static // test static
$encrypted = SymmetricEncryption::encryptKey($input, $key); $encrypted = SymmetricEncryption::encryptKey($input, $key);
$decrypted = SymmetricEncryption::decryptKey($encrypted, $key); $decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
@@ -114,13 +148,51 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$crypt = new SymmetricEncryption($key); $crypt = new SymmetricEncryption($key);
$encrypted = $crypt->encrypt($input); $encrypted = $crypt->encrypt($input);
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
$crypt->setKey($key); $crypt->setKey($wrong_key);
$crypt->decrypt($encrypted); $crypt->decrypt($encrypted);
}
/**
* Test decryption with wrong key
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptFailed
* @testdox decrypt indirect with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailedIndirect(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
$wrong_key = CreateKey::generateRandomKey();
// class instance // class instance
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input); $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted); SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted);
}
/**
* Test decryption with wrong key
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptFailed
* @testdox decrypt static with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailedStatic(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
$wrong_key = CreateKey::generateRandomKey();
// class static // class static
$encrypted = SymmetricEncryption::encryptKey($input, $key); $encrypted = SymmetricEncryption::encryptKey($input, $key);
@@ -190,6 +262,56 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
SymmetricEncryption::decryptKey($encrypted, $key); SymmetricEncryption::decryptKey($encrypted, $key);
} }
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKey
* @testdox wrong key indirect $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKeyIndirect(string $key, string $exception_message): void
{
$enc_key = CreateKey::generateRandomKey();
// class instance
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($key)->encrypt('test');
// we must encrypt valid thing first so we can fail with the wrong key
$encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test');
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($key)->decrypt($encrypted);
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKey
* @testdox wrong key static $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKeyStatic(string $key, string $exception_message): void
{
$enc_key = CreateKey::generateRandomKey();
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::encryptKey('test', $key);
// we must encrypt valid thing first so we can fail with the wrong key
$encrypted = SymmetricEncryption::encryptKey('test', $enc_key);
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($encrypted, $key);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -232,6 +354,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
$this->expectExceptionMessage($exception_message); $this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key); SymmetricEncryption::decryptKey($input, $key);
} }
/**
* Undocumented function
*
* @covers ::decrypt
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext indirect $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertextIndirect(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
// class instance
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::getInstance($key)->decrypt($input);
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
/**
* Undocumented function
*
* @covers ::decrypt
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext static $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertextStatic(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
// class static
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decryptKey($input, $key);
}
} }
// __END__ // __END__

View File

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

View File

@@ -14,6 +14,9 @@ parameters:
# allRules: false # allRules: false
checkMissingCallableSignature: true checkMissingCallableSignature: true
treatPhpDocTypesAsCertain: false treatPhpDocTypesAsCertain: false
# phpVersion:
# min: 80200 # PHP 8.2.0
# max: 80300 # PHP latest
paths: paths:
- %currentWorkingDirectory%/www - %currentWorkingDirectory%/www
bootstrapFiles: bootstrapFiles:

View File

@@ -28,7 +28,6 @@ $log = new CoreLibs\Logging\Logging([
'log_per_date' => true, 'log_per_date' => true,
]); ]);
$PAGE_NAME = 'TEST CLASS: DB CONVERT PLACEHOLDER'; $PAGE_NAME = 'TEST CLASS: DB CONVERT PLACEHOLDER';
print "<!DOCTYPE html>"; print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>"; print "<html><head><title>" . $PAGE_NAME . "</title></head>";

View File

@@ -53,6 +53,9 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
} else { } else {
print "NO DB HANDLER<br>"; 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 // turn on debug replace for placeholders
$db->dbSetDebugReplacePlaceholder(true); $db->dbSetDebugReplacePlaceholder(true);
@@ -62,59 +65,115 @@ $db->dbExec("TRUNCATE test_foo");
$uniqid = \CoreLibs\Create\Uids::uniqIdShort(); $uniqid = \CoreLibs\Create\Uids::uniqIdShort();
$binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: ''); $binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: '');
$query_params = [ $query_params = [
$uniqid, $uniqid, // test
true, true, // some_bool
'STRING A', 'STRING A', // string_a
2, 2, // number_a
2.5, 2.5, // numeric_a
1, 1, // smallint
date('H:m:s'), date('H:m:s'), // some_internval
date('Y-m-d H:i:s'), 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_encode(['a' => 'string', 'b' => 1, 'c' => 1.5, 'f' => true, 'g' => ['a', 1, 1.5]]), // json_string
null, null, // null_var
'{"a", "b"}', '{"a", "b"}', // array_char_1
'{1,2}', '{1,2}', // array_int_1
'{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}', '{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}', // array_composite
'("Text", 4, 6.3)', '("Text", 4, 6.3)', // composite_item
$binary_data $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 $query_insert = <<<SQL
INSERT INTO test_foo ( INSERT INTO test_foo (
test, some_bool, string_a, number_a, number_a_numeric, smallint_a, -- row 1
some_time, some_timestamp, json_string, null_var, 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, array_char_1, array_int_1,
-- row 4
array_composite, array_composite,
-- row 5
composite_item, 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 ( ) VALUES (
-- row 1
$1, $2, $3, $4, $5, $6, $1, $2, $3, $4, $5, $6,
-- row 2
$7, $8, $9, $10, $7, $8, $9, $10,
-- row 3
$11, $12, $11, $12,
-- row 4
$13, $13,
-- row 5
$14, $14,
$15 -- row 6
$15,
-- row 7
$16, $17,
-- row 8
$18, $19,
-- row 9
$20, $21, $22, $23,
-- row 10
$24
) )
RETURNING RETURNING
test_foo_id, test_foo_id, number_serial, identity_always, identitiy_default, default_uuid,
test, some_bool, string_a, number_a, number_a_numeric, smallint_a, test, some_bool, string_a, number_a, numeric_a, smallint_a,
some_time, some_timestamp, json_string, null_var, some_internval, some_timestamp, json_string, null_var,
array_char_1, array_int_1, array_char_1, array_int_1,
array_composite, array_composite,
composite_item, 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; SQL;
$status = $db->dbExecParams($query_insert, $query_params); $status = $db->dbExecParams($query_insert, $query_params);
echo "<b>*</b><br>"; echo "<b>*</b><br>";
echo "INSERT ALL COLUMN TYPES: " echo "INSERT ALL COLUMN TYPES: "
. Support::printToString($query_params) . " |<br>" . Support::printToString($query_params) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>" . "QUERY: <pre>" . $db->dbGetQuery() . "</pre> |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |<br>" . "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |<br>"
. "RETURNING EXT: <pre>" . print_r($db->dbGetReturningExt(), true) . "</pre> |<br>" . "RETURNING EXT: <pre>" . print_r($db->dbGetReturningExt(), true) . "</pre> |<br>"
. "RETURNING RETURN: <pre>" . print_r($db->dbGetReturningArray(), true) . "<pre> |<br>" . "RETURNING RETURN: <pre>" . print_r($db->dbGetReturningArray(), true) . "<pre> |<br>"
. "ERROR: " . $db->dbGetLastError(true) . "<br>"; . "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<hr>"; 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 // test connectors: = , <> () for query detection
// convert placeholder tests // convert placeholder tests
@@ -131,6 +190,16 @@ SQL,
'params' => [], 'params' => [],
'direction' => 'pg', '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?' => [ 'a?' => [
'query' => <<<SQL 'query' => <<<SQL
INSERT INTO test_foo ( INSERT INTO test_foo (
@@ -157,6 +226,18 @@ SQL,
], ],
'direction' => 'pg', '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); $db->dbSetConvertPlaceholder(true);
@@ -169,11 +250,12 @@ foreach ($test_queries as $info => $data) {
// . "<br>"; // . "<br>";
if ($db->dbCheckQueryForSelect($query)) { if ($db->dbCheckQueryForSelect($query)) {
$row = $db->dbReturnRowParams($query, $params); $row = $db->dbReturnRowParams($query, $params);
print "[$info] SELECT: " . Support::prAr($row) . "<br>"; print "<b>[$info]</b> SELECT: " . Support::prAr($row) . "<br>";
} else { } else {
$db->dbExecParams($query, $params); $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>"; echo "<hr>";
} }
@@ -188,22 +270,29 @@ SQL,
['string A-1'] ['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 print "CursorExt: " . Support::prAr($db->dbGetCursorExt(<<<SQL
SELECT test, string_a, number_a SELECT test, string_a, number_a
FROM test_foo FROM test_foo
WHERE string_a = ? WHERE string_a = ?
SQL, ['string A-1'])); SQL, ['string A-1']));
echo "<hr>";
// ERROR BELOW: missing params
$res = $db->dbReturnRowParams(<<<SQL $res = $db->dbReturnRowParams(<<<SQL
SELECT test, string_a, number_a SELECT test, string_a, number_a
FROM test_foo FROM test_foo
WHERE string_a = $1 WHERE string_a = $1
SQL, []); SQL, []);
print "PL: " . Support::PrAr($db->dbGetPlaceholderConverted()) . "<br>"; 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>"; echo "dbReturn read LIKE: <br>";
while ( while (
is_array($res = $db->dbReturnParams( is_array($res = $db->dbReturnParams(
@@ -217,6 +306,7 @@ SQL,
) { ) {
print "RES: " . Support::prAr($res) . "<br>"; print "RES: " . Support::prAr($res) . "<br>";
} }
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
print "</body></html>"; print "</body></html>";
$db->log->debug('DEBUGEND', '==================================== [END]'); $db->log->debug('DEBUGEND', '==================================== [END]');

View File

@@ -62,15 +62,36 @@ $backend = new CoreLibs\Admin\Backend(
$backend->db->dbInfo(true); $backend->db->dbInfo(true);
ob_end_flush(); ob_end_flush();
print "<!DOCTYPE html>"; print <<<HTML
print "<html><head><title>TEST CLASS</title></head>"; <!DOCTYPE html>
print "<body>"; <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 // key: file name, value; name
$test_files = [ $test_files = [
'class_test.db.php' => 'Class Test: DB', 'class_test.db.php' => 'Class Test: DB',
'class_test.db.types.php' => 'Class Test: DB column type convert', '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.dbReturn.php' => 'Class Test: DB dbReturn',
'class_test.db.single.php' => 'Class Test: DB single query tests', 'class_test.db.single.php' => 'Class Test: DB single query tests',
'class_test.db.convert-placeholder.php' => 'Class Test: DB convert placeholder', 'class_test.db.convert-placeholder.php' => 'Class Test: DB convert placeholder',

View File

@@ -14,10 +14,24 @@ namespace CoreLibs\DB\Support;
class ConvertPlaceholder class ConvertPlaceholder
{ {
/** @var string split regex */ // NOTE for missing: range */+ are not iplemented in the regex below, but - is for now
private const PATTERN_QUERY_SPLIT = '[(<>=,?-]|->|->>|#>|#>>|@>|<@|\?\|\?\&|\|\||#-'; // NOTE some combinations are allowed, but the query will fail before this
/** @var string split regex, entries before $ group */
private const PATTERN_QUERY_SPLIT =
'\?\?|' // UNKNOWN: double ??, is this to avoid something?
. '[\(,]|' // for ',' and '(' mostly in INSERT or ANY()
. '[<>=]|' // general set for <, >, = in any query with any combination
. '\^@|' // text search for start from text with ^@
. '\|\||' // concats two elements
. '&&|' // array overlap
. '\-\|\-|' // range overlap for array
. '[^-]-{1}|' // single -, used in JSON too
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-'; //JSON searches, Array searchs, etc
/** @var string the main regex including the pattern query split */ /** @var string the main regex including the pattern query split */
private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:\?\?|' . self::PATTERN_QUERY_SPLIT . ')\s*'; private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:' . self::PATTERN_QUERY_SPLIT . ')\s*';
/** @var string comment regex
* anything that starts with -- and ends with a line break but any character that is not line break inbetween */
private const PATTERN_COMMENT = '(?:\-\-[^\r\n]*?\r?\n)*\s*';
/** @var string parts to ignore in the SQL */ /** @var string parts to ignore in the SQL */
private const PATTERN_IGNORE = private const PATTERN_IGNORE =
// digit -> ignore // digit -> ignore
@@ -34,6 +48,7 @@ class ConvertPlaceholder
/** @var string replace regex for named (:...) entries */ /** @var string replace regex for named (:...) entries */
public const REGEX_REPLACE_NAMED = '/' public const REGEX_REPLACE_NAMED = '/'
. '(' . self::PATTERN_ELEMENT . ')' . '(' . self::PATTERN_ELEMENT . ')'
. self::PATTERN_COMMENT
. '(' . '('
. self::PATTERN_IGNORE . self::PATTERN_IGNORE
. self::PATTERN_NAMED . self::PATTERN_NAMED
@@ -42,6 +57,7 @@ class ConvertPlaceholder
/** @var string replace regex for question mark (?) entries */ /** @var string replace regex for question mark (?) entries */
public const REGEX_REPLACE_QUESTION_MARK = '/' public const REGEX_REPLACE_QUESTION_MARK = '/'
. '(' . self::PATTERN_ELEMENT . ')' . '(' . self::PATTERN_ELEMENT . ')'
. self::PATTERN_COMMENT
. '(' . '('
. self::PATTERN_IGNORE . self::PATTERN_IGNORE
. self::PATTERN_QUESTION_MARK . self::PATTERN_QUESTION_MARK
@@ -50,6 +66,7 @@ class ConvertPlaceholder
/** @var string replace regex for numbered ($n) entries */ /** @var string replace regex for numbered ($n) entries */
public const REGEX_REPLACE_NUMBERED = '/' public const REGEX_REPLACE_NUMBERED = '/'
. '(' . self::PATTERN_ELEMENT . ')' . '(' . self::PATTERN_ELEMENT . ')'
. self::PATTERN_COMMENT
. '(' . '('
. self::PATTERN_IGNORE . self::PATTERN_IGNORE
. self::PATTERN_NUMBERED . self::PATTERN_NUMBERED
@@ -60,6 +77,7 @@ class ConvertPlaceholder
// prefix string part, must match towards // prefix string part, must match towards
// seperator for ( = , ? - [and json/jsonb in pg doc section 9.15] // seperator for ( = , ? - [and json/jsonb in pg doc section 9.15]
. self::PATTERN_ELEMENT . self::PATTERN_ELEMENT
. self::PATTERN_COMMENT
// match for replace part // match for replace part
. '(?:' . '(?:'
// ignore parts // ignore parts

View File

@@ -49,7 +49,11 @@ class SymmetricEncryption
*/ */
public static function getInstance(string|null $key = null): self public static function getInstance(string|null $key = null): self
{ {
if (empty(self::$instance)) { // new if no instsance or key is different
if (
empty(self::$instance) ||
self::$instance->key != $key
) {
self::$instance = new self($key); self::$instance = new self($key);
} }
return self::$instance; return self::$instance;
@@ -130,7 +134,7 @@ class SymmetricEncryption
*/ */
private function encryptData(string $message, ?string $key): string private function encryptData(string $message, ?string $key): string
{ {
if (empty($this->key) || $key === null) { if ($key === null) {
throw new \UnexpectedValueException('Key not set'); throw new \UnexpectedValueException('Key not set');
} }
$key = $this->createKey($key); $key = $this->createKey($key);