Compare commits
25 Commits
46e44c19bf
...
v9.24.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4971f62490 | ||
|
|
1cf4fdf31a | ||
|
|
d16b920966 | ||
|
|
ab52bf59b5 | ||
|
|
a8dd076aac | ||
|
|
c17ca1f847 | ||
|
|
e349613d60 | ||
|
|
e9cfdb4bf0 | ||
|
|
f966209e0a | ||
|
|
c13934de99 | ||
|
|
1e90bb677e | ||
|
|
540269e61f | ||
|
|
e793c3975b | ||
|
|
7d4c9724fe | ||
|
|
d1c4611431 | ||
|
|
8d3882a6fe | ||
|
|
2b0434e36b | ||
|
|
ba11a936db | ||
|
|
df591659cb | ||
|
|
5343034768 | ||
|
|
dec56c9559 | ||
|
|
880f15ac6f | ||
|
|
a46601fe03 | ||
|
|
022c39e791 | ||
|
|
fdefaca301 |
@@ -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/
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ RETURNS TRIGGER AS
|
|||||||
$$
|
$$
|
||||||
BEGIN
|
BEGIN
|
||||||
IF TG_OP = 'INSERT' THEN
|
IF TG_OP = 'INSERT' THEN
|
||||||
NEW.date_created := 'now';
|
NEW.date_created := clock_timestamp();
|
||||||
ELSIF TG_OP = 'UPDATE' THEN
|
ELSIF TG_OP = 'UPDATE' THEN
|
||||||
NEW.date_updated := 'now';
|
NEW.date_updated := clock_timestamp();
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ DECLARE
|
|||||||
random_length INT = 25; -- that should be long enough
|
random_length INT = 25; -- that should be long enough
|
||||||
BEGIN
|
BEGIN
|
||||||
IF TG_OP = 'INSERT' THEN
|
IF TG_OP = 'INSERT' THEN
|
||||||
NEW.date_created := 'now';
|
NEW.date_created := clock_timestamp();
|
||||||
NEW.cuid := random_string(random_length);
|
NEW.cuid := random_string(random_length);
|
||||||
NEW.cuuid := gen_random_uuid();
|
NEW.cuuid := gen_random_uuid();
|
||||||
ELSIF TG_OP = 'UPDATE' THEN
|
ELSIF TG_OP = 'UPDATE' THEN
|
||||||
NEW.date_updated := 'now';
|
NEW.date_updated := clock_timestamp();
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ DECLARE
|
|||||||
random_length INT = 32; -- long for massive data
|
random_length INT = 32; -- long for massive data
|
||||||
BEGIN
|
BEGIN
|
||||||
IF TG_OP = 'INSERT' THEN
|
IF TG_OP = 'INSERT' THEN
|
||||||
NEW.date_created := 'now';
|
NEW.date_created := clock_timestamp();
|
||||||
IF NEW.uid IS NULL THEN
|
IF NEW.uid IS NULL THEN
|
||||||
NEW.uid := random_string(random_length);
|
NEW.uid := random_string(random_length);
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF TG_OP = 'UPDATE' THEN
|
ELSIF TG_OP = 'UPDATE' THEN
|
||||||
NEW.date_updated := 'now';
|
NEW.date_updated := clock_timestamp();
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
|
|||||||
@@ -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';
|
|
||||||
@@ -10,8 +10,8 @@ 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,
|
||||||
ecuid VARCHAR,
|
eucuid VARCHAR,
|
||||||
ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
||||||
-- date_created equal, but can be overridden
|
-- date_created equal, but can be overridden
|
||||||
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
-- session ID if set
|
-- session ID if set
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ CREATE TABLE edit_user (
|
|||||||
protected SMALLINT NOT NULL DEFAULT 0,
|
protected SMALLINT NOT NULL DEFAULT 0,
|
||||||
-- is admin user
|
-- is admin user
|
||||||
admin SMALLINT NOT NULL DEFAULT 0,
|
admin SMALLINT NOT NULL DEFAULT 0,
|
||||||
|
-- force lgout counter
|
||||||
|
force_logout INT DEFAULT 0,
|
||||||
-- last login log
|
-- last login log
|
||||||
last_login TIMESTAMP WITHOUT TIME ZONE,
|
last_login TIMESTAMP WITHOUT TIME ZONE,
|
||||||
-- login error
|
-- login error
|
||||||
@@ -74,6 +76,7 @@ COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be lo
|
|||||||
COMMENT ON COLUMN edit_user.locked IS 'Locked from too many wrong password logins';
|
COMMENT ON COLUMN edit_user.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.protected IS 'User can only be chnaged by admin user';
|
||||||
COMMENT ON COLUMN edit_user.admin IS 'If set, this user is SUPER admin';
|
COMMENT ON COLUMN edit_user.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.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_count IS 'Number of failed logins, reset on successful login';
|
||||||
COMMENT ON COLUMN edit_user.login_error_date_last IS 'Last login error date';
|
COMMENT ON COLUMN edit_user.login_error_date_last IS 'Last login error date';
|
||||||
|
|||||||
@@ -1185,7 +1185,6 @@ final class CoreLibsACLLoginTest extends TestCase
|
|||||||
foreach ($session as $session_var => $session_value) {
|
foreach ($session as $session_var => $session_value) {
|
||||||
$_SESSION[$session_var] = $session_value;
|
$_SESSION[$session_var] = $session_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var \CoreLibs\ACL\Login&MockObject */
|
/** @var \CoreLibs\ACL\Login&MockObject */
|
||||||
$login_mock = $this->getMockBuilder(\CoreLibs\ACL\Login::class)
|
$login_mock = $this->getMockBuilder(\CoreLibs\ACL\Login::class)
|
||||||
->setConstructorArgs([
|
->setConstructorArgs([
|
||||||
@@ -1204,7 +1203,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
|||||||
. 'locale' . DIRECTORY_SEPARATOR,
|
. 'locale' . DIRECTORY_SEPARATOR,
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin'])
|
->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin', 'loginEnhanceHttpSecurity'])
|
||||||
->getMock();
|
->getMock();
|
||||||
$login_mock->expects($this->any())
|
$login_mock->expects($this->any())
|
||||||
->method('loginTerminate')
|
->method('loginTerminate')
|
||||||
@@ -1222,6 +1221,10 @@ final class CoreLibsACLLoginTest extends TestCase
|
|||||||
->method('loginPrintLogin')
|
->method('loginPrintLogin')
|
||||||
->willReturnCallback(function () {
|
->willReturnCallback(function () {
|
||||||
});
|
});
|
||||||
|
$login_mock->expects($this->any())
|
||||||
|
->method('loginEnhanceHttpSecurity')
|
||||||
|
->willReturnCallback(function () {
|
||||||
|
});
|
||||||
|
|
||||||
// if mock_settings: enabled OFF
|
// if mock_settings: enabled OFF
|
||||||
// run DB update and set off
|
// run DB update and set off
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ DECLARE
|
|||||||
random_length INT = 12; -- that should be long enough
|
random_length INT = 12; -- that should be long enough
|
||||||
BEGIN
|
BEGIN
|
||||||
IF TG_OP = 'INSERT' THEN
|
IF TG_OP = 'INSERT' THEN
|
||||||
NEW.date_created := 'now';
|
NEW.date_created := clock_timestamp();
|
||||||
NEW.cuid := random_string(random_length);
|
NEW.cuid := random_string(random_length);
|
||||||
NEW.cuuid := gen_random_uuid();
|
NEW.cuuid := gen_random_uuid();
|
||||||
ELSIF TG_OP = 'UPDATE' THEN
|
ELSIF TG_OP = 'UPDATE' THEN
|
||||||
NEW.date_updated := 'now';
|
NEW.date_updated := clock_timestamp();
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
@@ -581,6 +581,8 @@ CREATE TABLE edit_user (
|
|||||||
protected SMALLINT NOT NULL DEFAULT 0,
|
protected SMALLINT NOT NULL DEFAULT 0,
|
||||||
-- is admin user
|
-- is admin user
|
||||||
admin SMALLINT NOT NULL DEFAULT 0,
|
admin SMALLINT NOT NULL DEFAULT 0,
|
||||||
|
-- forced logout counter
|
||||||
|
force_logout INT DEFAULT 0,
|
||||||
-- last login log
|
-- last login log
|
||||||
last_login TIMESTAMP WITHOUT TIME ZONE,
|
last_login TIMESTAMP WITHOUT TIME ZONE,
|
||||||
-- login error
|
-- login error
|
||||||
@@ -650,8 +652,8 @@ 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,
|
||||||
ecuid VARCHAR,
|
eucuid VARCHAR,
|
||||||
ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
||||||
-- date_created equal, but can be overridden
|
-- date_created equal, but can be overridden
|
||||||
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
-- session ID if set
|
-- session ID if set
|
||||||
@@ -697,6 +699,7 @@ CREATE TABLE edit_log (
|
|||||||
action_value VARCHAR, -- in action_data
|
action_value VARCHAR, -- in action_data
|
||||||
action_type VARCHAR, -- in action_data
|
action_type VARCHAR, -- in action_data
|
||||||
action_error VARCHAR -- in action_data
|
action_error VARCHAR -- in action_data
|
||||||
|
) INHERITS (edit_generic) WITHOUT OIDS;
|
||||||
-- END: table/edit_log.sql
|
-- END: table/edit_log.sql
|
||||||
-- START: table/edit_log_overflow.sql
|
-- START: table/edit_log_overflow.sql
|
||||||
-- AUTHOR: Clemens Schwaighofer
|
-- AUTHOR: Clemens Schwaighofer
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ final class CoreLibsCreateSessionTest extends TestCase
|
|||||||
'getSessionId' => '1234abcd4567'
|
'getSessionId' => '1234abcd4567'
|
||||||
],
|
],
|
||||||
'sessionNameGlobals',
|
'sessionNameGlobals',
|
||||||
false,
|
[
|
||||||
|
'auto_write_close' => false,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'auto write close' => [
|
'auto write close' => [
|
||||||
'sessionNameAutoWriteClose',
|
'sessionNameAutoWriteClose',
|
||||||
@@ -66,7 +68,9 @@ final class CoreLibsCreateSessionTest extends TestCase
|
|||||||
'getSessionId' => '1234abcd4567'
|
'getSessionId' => '1234abcd4567'
|
||||||
],
|
],
|
||||||
'sessionNameAutoWriteClose',
|
'sessionNameAutoWriteClose',
|
||||||
true,
|
[
|
||||||
|
'auto_write_close' => true,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -81,13 +85,14 @@ final class CoreLibsCreateSessionTest extends TestCase
|
|||||||
* @param string $input
|
* @param string $input
|
||||||
* @param array<mixed> $mock_data
|
* @param array<mixed> $mock_data
|
||||||
* @param string $expected
|
* @param string $expected
|
||||||
|
* @param array<string,mixed> $options
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testStartSession(
|
public function testStartSession(
|
||||||
string $input,
|
string $input,
|
||||||
array $mock_data,
|
array $mock_data,
|
||||||
string $expected,
|
string $expected,
|
||||||
?bool $auto_write_close,
|
?array $options,
|
||||||
): void {
|
): void {
|
||||||
/** @var \CoreLibs\Create\Session&MockObject $session_mock */
|
/** @var \CoreLibs\Create\Session&MockObject $session_mock */
|
||||||
$session_mock = $this->createPartialMock(
|
$session_mock = $this->createPartialMock(
|
||||||
@@ -174,9 +179,14 @@ final class CoreLibsCreateSessionTest extends TestCase
|
|||||||
4,
|
4,
|
||||||
'/^\[SESSION\] Failed to activate session/'
|
'/^\[SESSION\] Failed to activate session/'
|
||||||
],
|
],
|
||||||
|
'expired session' => [
|
||||||
|
\RuntimeException::class,
|
||||||
|
5,
|
||||||
|
'/^\[SESSION\] Expired session found/'
|
||||||
|
],
|
||||||
'not a valid session id returned' => [
|
'not a valid session id returned' => [
|
||||||
\UnexpectedValueException::class,
|
\UnexpectedValueException::class,
|
||||||
5,
|
6,
|
||||||
'/^\[SESSION\] getSessionId did not return a session id/'
|
'/^\[SESSION\] getSessionId did not return a session id/'
|
||||||
], */
|
], */
|
||||||
];
|
];
|
||||||
@@ -206,7 +216,8 @@ final class CoreLibsCreateSessionTest extends TestCase
|
|||||||
$this->expectException($exception);
|
$this->expectException($exception);
|
||||||
$this->expectExceptionCode($exception_code);
|
$this->expectExceptionCode($exception_code);
|
||||||
$this->expectExceptionMessageMatches($expected_error);
|
$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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5141,9 +5141,9 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
INSERT INTO table_with_primary_key (
|
INSERT INTO table_with_primary_key (
|
||||||
row_int, row_numeric, row_varchar, row_varchar_literal
|
row_int, row_numeric, row_varchar, row_varchar_literal
|
||||||
) VALUES (
|
) VALUES (
|
||||||
-- comment 1
|
-- comment 1 かな
|
||||||
$1, $2,
|
$1, $2,
|
||||||
-- comment 2
|
-- comment 2 -
|
||||||
$3
|
$3
|
||||||
-- comment 3
|
-- comment 3
|
||||||
, $4
|
, $4
|
||||||
@@ -5152,6 +5152,23 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
'count' => 4,
|
'count' => 4,
|
||||||
'convert' => false
|
'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
|
// Note some are not set
|
||||||
'a complete set of possible' => [
|
'a complete set of possible' => [
|
||||||
'query' => <<<SQL
|
'query' => <<<SQL
|
||||||
@@ -5168,6 +5185,17 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
SQL,
|
SQL,
|
||||||
'count' => 12,
|
'count' => 12,
|
||||||
'convert' => false,
|
'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,
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -568,6 +568,9 @@ final class CoreLibsDebugSupportTest extends TestCase
|
|||||||
'assert expected 12'
|
'assert expected 12'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
$this->assertTrue(true, 'Default fallback as true');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
4dev/tests/Language/locale_other/.gitignore
vendored
Normal file
2
4dev/tests/Language/locale_other/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
@@ -10,7 +10,7 @@ use CoreLibs\Logging\Logger\Level;
|
|||||||
/**
|
/**
|
||||||
* Test class for Logging
|
* Test class for Logging
|
||||||
* @coversDefaultClass \CoreLibs\Logging\ErrorMessages
|
* @coversDefaultClass \CoreLibs\Logging\ErrorMessages
|
||||||
* @testdox \CoreLibs\Logging\ErrorMEssages method tests
|
* @testdox \CoreLibs\Logging\ErrorMessages method tests
|
||||||
*/
|
*/
|
||||||
final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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__
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
-- 20241203: update edit tables
|
-- 20241203: update edit tables
|
||||||
ALTER TABLE edit_generic ADD cuuid UUID DEFAULT gen_random_uuid();
|
ALTER TABLE edit_generic ADD cuuid UUID DEFAULT gen_random_uuid();
|
||||||
ALTER TABLE edit_log ADD ecuid VARCHAR;
|
ALTER TABLE edit_log ADD eucuid VARCHAR;
|
||||||
ALTER TABLE edit_log ADD ecuuid VARCHAR;
|
ALTER TABLE edit_log ADD eucuuid VARCHAR;
|
||||||
ALTER TABLE edit_log ADD action_sub_id VARCHAR;
|
ALTER TABLE edit_log ADD action_sub_id VARCHAR;
|
||||||
ALTER TABLE edit_log ADD http_data JSONB;
|
ALTER TABLE edit_log ADD http_data JSONB;
|
||||||
ALTER TABLE edit_log ADD ip_address JSONB;
|
ALTER TABLE edit_log ADD ip_address JSONB;
|
||||||
ALTER TABLE edit_log ADD action_data JSONB;
|
ALTER TABLE edit_log ADD action_data JSONB;
|
||||||
ALTER TABLE edit_log ADD request_scheme VARCHAR;
|
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
|
-- update set_edit_gneric
|
||||||
-- adds the created or updated date tags
|
-- adds the created or updated date tags
|
||||||
@@ -18,13 +22,17 @@ DECLARE
|
|||||||
random_length INT = 25; -- that should be long enough
|
random_length INT = 25; -- that should be long enough
|
||||||
BEGIN
|
BEGIN
|
||||||
IF TG_OP = 'INSERT' THEN
|
IF TG_OP = 'INSERT' THEN
|
||||||
NEW.date_created := 'now';
|
NEW.date_created := clock_timestamp();
|
||||||
NEW.cuid := random_string(random_length);
|
NEW.cuid := random_string(random_length);
|
||||||
NEW.cuuid := gen_random_uuid();
|
NEW.cuuid := gen_random_uuid();
|
||||||
ELSIF TG_OP = 'UPDATE' THEN
|
ELSIF TG_OP = 'UPDATE' THEN
|
||||||
NEW.date_updated := 'now';
|
NEW.date_updated := clock_timestamp();
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
$$
|
$$
|
||||||
LANGUAGE 'plpgsql';
|
LANGUAGE 'plpgsql';
|
||||||
|
|
||||||
|
--
|
||||||
|
ALTER TABLE edit_log RENAME ecuid TO eucuid;
|
||||||
|
ALTER TABLE edit_log RENAME ecuuid TO eucuuid;
|
||||||
|
|||||||
@@ -5,4 +5,9 @@
|
|||||||
convertDeprecationsToExceptions="true"
|
convertDeprecationsToExceptions="true"
|
||||||
bootstrap="4dev/tests/bootstrap.php"
|
bootstrap="4dev/tests/bootstrap.php"
|
||||||
>
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="deploy">
|
||||||
|
<directory>4dev/tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ require 'config.php';
|
|||||||
$LOG_FILE_ID = 'classTest-convert-colors';
|
$LOG_FILE_ID = 'classTest-convert-colors';
|
||||||
ob_end_flush();
|
ob_end_flush();
|
||||||
|
|
||||||
use CoreLibs\Convert\Colors;
|
// use CoreLibs\Convert\Colors;
|
||||||
use CoreLibs\Convert\Color\Color;
|
use CoreLibs\Convert\Color\Color;
|
||||||
use CoreLibs\Convert\Color\Coordinates;
|
use CoreLibs\Convert\Color\Coordinates;
|
||||||
use CoreLibs\Debug\Support as DgS;
|
use CoreLibs\Debug\Support as DgS;
|
||||||
@@ -29,7 +29,6 @@ $log = new CoreLibs\Logging\Logging([
|
|||||||
'log_file_id' => $LOG_FILE_ID,
|
'log_file_id' => $LOG_FILE_ID,
|
||||||
'log_per_date' => true,
|
'log_per_date' => true,
|
||||||
]);
|
]);
|
||||||
$color_class = 'CoreLibs\Convert\Colors';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* print out a color block with info
|
* print out a color block with info
|
||||||
@@ -131,7 +130,8 @@ try {
|
|||||||
} catch (\LengthException $e) {
|
} catch (\LengthException $e) {
|
||||||
print "*Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
|
print "*Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
|
||||||
}
|
}
|
||||||
print "<hr>";
|
|
||||||
|
/* print "<hr>";
|
||||||
print "<h2>LEGACY</h2>";
|
print "<h2>LEGACY</h2>";
|
||||||
// B(valid)
|
// B(valid)
|
||||||
$rgb = [50, 20, 30];
|
$rgb = [50, 20, 30];
|
||||||
@@ -173,7 +173,7 @@ $hsb = [0, 0, 5];
|
|||||||
print "S::COLOR hsb->rgb: $hsb[0], $hsb[1], $hsb[2]: "
|
print "S::COLOR hsb->rgb: $hsb[0], $hsb[1], $hsb[2]: "
|
||||||
. DgS::printAr(SetVarType::setArray(
|
. DgS::printAr(SetVarType::setArray(
|
||||||
Colors::hsb2rgb($hsb[0], $hsb[1], $hsb[2])
|
Colors::hsb2rgb($hsb[0], $hsb[1], $hsb[2])
|
||||||
)) . "<br>";
|
)) . "<br>"; */
|
||||||
|
|
||||||
print "<hr>";
|
print "<hr>";
|
||||||
|
|
||||||
|
|||||||
@@ -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>";
|
||||||
|
|||||||
@@ -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,53 +65,94 @@ $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>"
|
||||||
@@ -146,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 (
|
||||||
@@ -172,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);
|
||||||
@@ -184,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>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,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(
|
||||||
@@ -232,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]');
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ print "EL_O: <pre>" . print_r($el_o, true) . "</pre>";
|
|||||||
|
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
print "buildHtml(): <pre>" . htmlentities($el_o->buildHtml()) . "</pre>";
|
print "buildHtml(): <pre>" . htmlentities($el_o->buildHtml()) . "</pre>";
|
||||||
echo "<hr>";
|
/* echo "<hr>";
|
||||||
print "phfo(\$el_o): <pre>" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "</pre>";
|
print "phfo(\$el_o): <pre>" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "</pre>"; */
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
print "phfa(\$el_list): <pre>" . htmlentities($el_o::buildHtmlFromList($el_o_list, true)) . "</pre>";
|
print "phfa(\$el_list): <pre>" . htmlentities($el_o::buildHtmlFromList($el_o_list, true)) . "</pre>";
|
||||||
|
|
||||||
|
|||||||
@@ -34,22 +34,21 @@ use CoreLibs\Debug\Support;
|
|||||||
echo "<br><b>LIST LOCALES</b><br>";
|
echo "<br><b>LIST LOCALES</b><br>";
|
||||||
|
|
||||||
$locale = 'en_US.UTF-8';
|
$locale = 'en_US.UTF-8';
|
||||||
$locales = L10n::listLocales($locale);
|
$locales = Language\L10n::listLocales($locale);
|
||||||
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
|
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
|
||||||
$locale = 'en.UTF-8';
|
$locale = 'en.UTF-8';
|
||||||
$locales = L10n::listLocales($locale);
|
$locales = Language\L10n::listLocales($locale);
|
||||||
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
|
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
|
||||||
|
|
||||||
echo "<br><b>PARSE LOCAL</b><br>";
|
echo "<br><b>PARSE LOCAL</b><br>";
|
||||||
$locale = 'en_US.UTF-8';
|
$locale = 'en_US.UTF-8';
|
||||||
$locale_info = L10n::parseLocale($locale);
|
$locale_info = Language\L10n::parseLocale($locale);
|
||||||
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
|
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
|
||||||
$locale = 'en.UTF-8';
|
$locale = 'en.UTF-8';
|
||||||
$locale_info = L10n::parseLocale($locale);
|
$locale_info = Language\L10n::parseLocale($locale);
|
||||||
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
|
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
|
||||||
|
|
||||||
echo "<br><b>AUTO DETECT</b><br>";
|
/* echo "<br><b>AUTO DETECT</b><br>";
|
||||||
|
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
// $get_locale = Language\GetLocale::setLocale();
|
// $get_locale = Language\GetLocale::setLocale();
|
||||||
// print "[AUTO, DEPRECATED]: " . Support::printAr($get_locale) . "<br>";
|
// print "[AUTO, DEPRECATED]: " . Support::printAr($get_locale) . "<br>";
|
||||||
@@ -103,6 +102,7 @@ $get_locale = Language\GetLocale::setLocaleFromSession(
|
|||||||
BASE . INCLUDES . LOCALE
|
BASE . INCLUDES . LOCALE
|
||||||
);
|
);
|
||||||
print "[SESSION SET INVALID]: " . Support::printAr($get_locale) . "<br>";
|
print "[SESSION SET INVALID]: " . Support::printAr($get_locale) . "<br>";
|
||||||
|
*/
|
||||||
|
|
||||||
// try to load non existing
|
// try to load non existing
|
||||||
echo "<br><b>NEW TYPE</b><br>";
|
echo "<br><b>NEW TYPE</b><br>";
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ $SET_SESSION_NAME = EDIT_SESSION_NAME;
|
|||||||
use CoreLibs\Debug\Support;
|
use CoreLibs\Debug\Support;
|
||||||
|
|
||||||
// init login & backend class
|
// 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 = new CoreLibs\Logging\Logging([
|
||||||
'log_folder' => BASE . LOG,
|
'log_folder' => BASE . LOG,
|
||||||
'log_file_id' => $LOG_FILE_ID,
|
'log_file_id' => $LOG_FILE_ID,
|
||||||
@@ -90,6 +93,8 @@ print <<<HTML
|
|||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
|
|
||||||
|
echo "SESSION ID: " . $session->getSessionIdCall() . "<br>";
|
||||||
|
|
||||||
echo "CHECK PERMISSION: " . ($login->loginCheckPermissions() ? 'OK' : 'BAD') . "<br>";
|
echo "CHECK PERMISSION: " . ($login->loginCheckPermissions() ? 'OK' : 'BAD') . "<br>";
|
||||||
echo "IS ADMIN: " . ($login->loginIsAdmin() ? 'OK' : 'BAD') . "<br>";
|
echo "IS ADMIN: " . ($login->loginIsAdmin() ? 'OK' : 'BAD') . "<br>";
|
||||||
echo "MIN ACCESS BASE: " . ($login->loginCheckAccessBase('admin') ? 'OK' : 'BAD') . "<br>";
|
echo "MIN ACCESS BASE: " . ($login->loginCheckAccessBase('admin') ? 'OK' : 'BAD') . "<br>";
|
||||||
@@ -118,8 +123,7 @@ if (isset($login->loginGetAcl()['unit'])) {
|
|||||||
print "Something went wrong with the login<br>";
|
print "Something went wrong with the login<br>";
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "<hr>";
|
// echo "<hr>";
|
||||||
|
|
||||||
// IP check: 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'CLIENT_IP' in _SERVER
|
// IP check: 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'CLIENT_IP' in _SERVER
|
||||||
// Agent check: 'HTTP_USER_AGENT'
|
// Agent check: 'HTTP_USER_AGENT'
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ HTML;
|
|||||||
$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',
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
|||||||
|
|
||||||
print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
|
print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
|
||||||
|
|
||||||
|
// This is now in \gullevek\dotenv\DotEnv::readEnvFile(...)
|
||||||
|
|
||||||
// test .env in local
|
// 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 "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>";
|
print "</body></html>";
|
||||||
// ;;
|
// ;;
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ $_SESSION['this_will_be_written'] = 'not empty';
|
|||||||
// open again with same name
|
// open again with same name
|
||||||
$session_name = 'class-test-session';
|
$session_name = 'class-test-session';
|
||||||
try {
|
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 id: " . $session_alt->getSessionId() . "<br>";
|
||||||
print "[4 SET] Current session auto write close: " . ($session_alt->checkAutoWriteClose() ? 'Yes' : 'No') . "<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>";
|
print "[START AGAIN] Current session id: " . $session_alt->getSessionId() . "<br>";
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class Login
|
|||||||
private ?int $edit_user_id;
|
private ?int $edit_user_id;
|
||||||
/** @var ?string the user cuid (note will be super seeded with uuid v4 later) */
|
/** @var ?string the user cuid (note will be super seeded with uuid v4 later) */
|
||||||
private ?string $edit_user_cuid;
|
private ?string $edit_user_cuid;
|
||||||
/** @var ?string UUIDv4, will superseed the ecuid and replace euid as login id */
|
/** @var ?string UUIDv4, will superseed the eucuid and replace euid as login id */
|
||||||
private ?string $edit_user_cuuid;
|
private ?string $edit_user_cuuid;
|
||||||
/** @var string _GET/_POST loginUserId parameter for non password login */
|
/** @var string _GET/_POST loginUserId parameter for non password login */
|
||||||
private string $login_user_id = '';
|
private string $login_user_id = '';
|
||||||
@@ -217,6 +217,16 @@ class Login
|
|||||||
'path' => '',
|
'path' => '',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** @var int resync interval time in minutes */
|
||||||
|
private const DEFAULT_AUTH_RESYNC_INTERVAL = 5 * 60;
|
||||||
|
/** @var int the session max garbage collection life time */
|
||||||
|
// private const DEFAULT_SESSION_GC_MAXLIFETIME = ;
|
||||||
|
private int $default_session_gc_maxlifetime;
|
||||||
|
/** @var int in how many minutes an auth resync is done */
|
||||||
|
private int $auth_resync_interval;
|
||||||
|
/** @var bool set the enhanced header security */
|
||||||
|
private bool $header_enhance_security = false;
|
||||||
|
|
||||||
/** @var \CoreLibs\Logging\Logging logger */
|
/** @var \CoreLibs\Logging\Logging logger */
|
||||||
public \CoreLibs\Logging\Logging $log;
|
public \CoreLibs\Logging\Logging $log;
|
||||||
/** @var \CoreLibs\DB\IO database */
|
/** @var \CoreLibs\DB\IO database */
|
||||||
@@ -248,156 +258,19 @@ class Login
|
|||||||
// attach session class
|
// attach session class
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
|
|
||||||
|
$this->default_session_gc_maxlifetime = (int)ini_get("session.gc_maxlifetime");
|
||||||
|
|
||||||
// set and check options
|
// set and check options
|
||||||
if (false === $this->loginSetOptions($options)) {
|
if (false === $this->loginSetOptions($options)) {
|
||||||
// on failure, exit
|
// on failure, exit
|
||||||
echo "<b>Could not set options</b>";
|
echo "<b>Could not set options</b>";
|
||||||
$this->loginTerminate('Could not set options', 3000);
|
$this->loginTerminate('Could not set options', 3000);
|
||||||
}
|
}
|
||||||
|
// init error array
|
||||||
// string key, msg: string, flag: e (error), o (ok)
|
$this->loginInitErrorMessages();
|
||||||
$this->login_error_msg = [
|
// acess right list
|
||||||
'0' => [
|
$this->loginLoadAccessRightList();
|
||||||
'msg' => 'No error',
|
// log allowed write flags
|
||||||
'flag' => 'o'
|
|
||||||
],
|
|
||||||
// actually obsolete
|
|
||||||
'100' => [
|
|
||||||
'msg' => '[EUCUUID] set from GET/POST!',
|
|
||||||
'flag' => 'e',
|
|
||||||
],
|
|
||||||
// query errors
|
|
||||||
'1009' => [
|
|
||||||
'msg' => 'Login query reading failed',
|
|
||||||
'flag' => 'e',
|
|
||||||
],
|
|
||||||
// user not found
|
|
||||||
'1010' => [
|
|
||||||
'msg' => 'Login Failed - Wrong Username or Password',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
// blowfish password wrong
|
|
||||||
/* '1011' => [
|
|
||||||
'msg' => 'Login Failed - Wrong Username or Password',
|
|
||||||
'flag' => 'e'
|
|
||||||
], */
|
|
||||||
// fallback md5 password wrong
|
|
||||||
'1012' => [
|
|
||||||
'msg' => 'Login Failed - Wrong Username or Password',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
// new password_hash wrong
|
|
||||||
'1013' => [
|
|
||||||
'msg' => 'Login Failed - Wrong Username or Password',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'1101' => [
|
|
||||||
'msg' => 'Login Failed - Login User ID must be validated',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'1102' => [
|
|
||||||
'msg' => 'Login Failed - Login User ID is outside valid date range',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'102' => [
|
|
||||||
'msg' => 'Login Failed - Please enter username and password',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'103' => [
|
|
||||||
'msg' => 'You do not have the rights to access this Page',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'104' => [
|
|
||||||
'msg' => 'Login Failed - User not enabled',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'105' => [
|
|
||||||
'msg' => 'Login Failed - User is locked',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'106' => [
|
|
||||||
'msg' => 'Login Failed - User is deleted',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'107' => [
|
|
||||||
'msg' => 'Login Failed - User in locked via date period',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'108' => [
|
|
||||||
'msg' => 'Login Failed - User is locked via Login User ID',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'109' => [
|
|
||||||
'msg' => 'Check permission query reading failed',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
// actually this is an illegal user, but I mask it
|
|
||||||
'220' => [
|
|
||||||
'msg' => 'Password change - The user could not be found',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'200' => [
|
|
||||||
'msg' => 'Password change - Please enter username and old password',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'201' => [
|
|
||||||
'msg' => 'Password change - The user could not be found',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'202' => [
|
|
||||||
'msg' => 'Password change - The old password is not correct',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'203' => [
|
|
||||||
'msg' => 'Password change - Please fill out both new password fields',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
'204' => [
|
|
||||||
'msg' => 'Password change - The new passwords do not match',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
// we should also not here WHAT is valid
|
|
||||||
'205' => [
|
|
||||||
'msg' => 'Password change - The new password is not in a valid format',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
// for OK password change
|
|
||||||
'300' => [
|
|
||||||
'msg' => 'Password change successful',
|
|
||||||
'flag' => 'o'
|
|
||||||
],
|
|
||||||
// this is bad bad error
|
|
||||||
'9999' => [
|
|
||||||
'msg' => 'Necessary crypt engine could not be found. Login is impossible',
|
|
||||||
'flag' => 'e'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
// read the current edit_access_right list into an array
|
|
||||||
$q = <<<SQL
|
|
||||||
SELECT
|
|
||||||
level, type, name
|
|
||||||
FROM
|
|
||||||
edit_access_right
|
|
||||||
WHERE
|
|
||||||
level >= 0
|
|
||||||
ORDER BY
|
|
||||||
level
|
|
||||||
SQL;
|
|
||||||
while (is_array($res = $this->db->dbReturn($q))) {
|
|
||||||
// level to description format (numeric)
|
|
||||||
$this->default_acl_list[$res['level']] = [
|
|
||||||
'type' => $res['type'],
|
|
||||||
'name' => $res['name']
|
|
||||||
];
|
|
||||||
$this->default_acl_list_type[(string)$res['type']] = (int)$res['level'];
|
|
||||||
}
|
|
||||||
// write that into the session
|
|
||||||
$this->session->setMany([
|
|
||||||
'LOGIN_DEFAULT_ACL_LIST' => $this->default_acl_list,
|
|
||||||
'LOGIN_DEFAULT_ACL_LIST_TYPE' => $this->default_acl_list_type,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->loginSetEditLogWriteTypeAvailable();
|
$this->loginSetEditLogWriteTypeAvailable();
|
||||||
|
|
||||||
// this will be deprecated
|
// this will be deprecated
|
||||||
@@ -425,6 +298,7 @@ class Login
|
|||||||
} else {
|
} else {
|
||||||
$this->log->critical($message, ['code' => $code]);
|
$this->log->critical($message, ['code' => $code]);
|
||||||
}
|
}
|
||||||
|
// TODO throw error and not exit
|
||||||
exit($code);
|
exit($code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,6 +451,20 @@ class Login
|
|||||||
}
|
}
|
||||||
$this->password_forgot = $options['forgot_flow'];
|
$this->password_forgot = $options['forgot_flow'];
|
||||||
|
|
||||||
|
// sync _SESSION acl settings
|
||||||
|
if (
|
||||||
|
!isset($options['auth_resync_interval']) ||
|
||||||
|
!is_numeric($options['auth_resync_interval']) ||
|
||||||
|
$options['auth_resync_interval'] < 0 ||
|
||||||
|
$options['auth_resync_interval'] > $this->default_session_gc_maxlifetime
|
||||||
|
) {
|
||||||
|
// default 5 minutues
|
||||||
|
$options['auth_resync_interval'] = self::DEFAULT_AUTH_RESYNC_INTERVAL;
|
||||||
|
} else {
|
||||||
|
$options['auth_resync_interval'] = (int)$options['auth_resync_interval'];
|
||||||
|
}
|
||||||
|
$this->auth_resync_interval = $options['auth_resync_interval'];
|
||||||
|
|
||||||
// *** LANGUAGE
|
// *** LANGUAGE
|
||||||
// LANG: LOCALE PATH
|
// LANG: LOCALE PATH
|
||||||
if (empty($options['locale_path'])) {
|
if (empty($options['locale_path'])) {
|
||||||
@@ -631,12 +519,210 @@ class Login
|
|||||||
$options['site_encoding'] = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
|
$options['site_encoding'] = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
|
||||||
SITE_ENCODING : 'UTF-8';
|
SITE_ENCODING : 'UTF-8';
|
||||||
}
|
}
|
||||||
|
// set enhancded security flag
|
||||||
|
if (
|
||||||
|
empty($options['enhanced_security']) ||
|
||||||
|
!is_bool($options['enhanced_security'])
|
||||||
|
) {
|
||||||
|
$options['enhanced_security'] = true;
|
||||||
|
}
|
||||||
|
$this->header_enhance_security = $options['enhanced_security'];
|
||||||
|
|
||||||
// write array to options
|
// write array to options
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the login error message array
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loginInitErrorMessages()
|
||||||
|
{
|
||||||
|
// string key, msg: string, flag: e (error), o (ok)
|
||||||
|
$this->login_error_msg = [
|
||||||
|
'0' => [
|
||||||
|
'msg' => 'No error',
|
||||||
|
'flag' => 'o'
|
||||||
|
],
|
||||||
|
// actually obsolete
|
||||||
|
'100' => [
|
||||||
|
'msg' => '[EUCUUID] set from GET/POST!',
|
||||||
|
'flag' => 'e',
|
||||||
|
],
|
||||||
|
// query errors
|
||||||
|
'1009' => [
|
||||||
|
'msg' => 'Login query reading failed',
|
||||||
|
'flag' => 'e',
|
||||||
|
],
|
||||||
|
// user not found
|
||||||
|
'1010' => [
|
||||||
|
'msg' => 'Login Failed - Wrong Username or Password',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
// general login error
|
||||||
|
'1011' => [
|
||||||
|
'msg' => 'Login Failed - General authentication error',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
// fallback md5 password wrong
|
||||||
|
'1012' => [
|
||||||
|
'msg' => 'Login Failed - Wrong Username or Password',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
// new password_hash wrong
|
||||||
|
'1013' => [
|
||||||
|
'msg' => 'Login Failed - Wrong Username or Password',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'1101' => [
|
||||||
|
'msg' => 'Login Failed - Login User ID must be validated',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'1102' => [
|
||||||
|
'msg' => 'Login Failed - Login User ID is outside valid date range',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'102' => [
|
||||||
|
'msg' => 'Login Failed - Please enter username and password',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'103' => [
|
||||||
|
'msg' => 'You do not have the rights to access this Page',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'104' => [
|
||||||
|
'msg' => 'Login Failed - User not enabled',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'105' => [
|
||||||
|
'msg' => 'Login Failed - User is locked',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'106' => [
|
||||||
|
'msg' => 'Login Failed - User is deleted',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'107' => [
|
||||||
|
'msg' => 'Login Failed - User in locked via date period',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'108' => [
|
||||||
|
'msg' => 'Login Failed - User is locked via Login User ID',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'109' => [
|
||||||
|
'msg' => 'Check permission query reading failed',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'110' => [
|
||||||
|
'msg' => 'Forced logout',
|
||||||
|
'flag' => '',
|
||||||
|
],
|
||||||
|
// actually this is an illegal user, but I mask it
|
||||||
|
'220' => [
|
||||||
|
'msg' => 'Password change - The user could not be found',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'200' => [
|
||||||
|
'msg' => 'Password change - Please enter username and old password',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'201' => [
|
||||||
|
'msg' => 'Password change - The user could not be found',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'202' => [
|
||||||
|
'msg' => 'Password change - The old password is not correct',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'203' => [
|
||||||
|
'msg' => 'Password change - Please fill out both new password fields',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
'204' => [
|
||||||
|
'msg' => 'Password change - The new passwords do not match',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
// we should also not here WHAT is valid
|
||||||
|
'205' => [
|
||||||
|
'msg' => 'Password change - The new password is not in a valid format',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
// for OK password change
|
||||||
|
'300' => [
|
||||||
|
'msg' => 'Password change successful',
|
||||||
|
'flag' => 'o'
|
||||||
|
],
|
||||||
|
// this is bad bad error
|
||||||
|
'9999' => [
|
||||||
|
'msg' => 'Necessary crypt engine could not be found. Login is impossible',
|
||||||
|
'flag' => 'e'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loads the access right list from the database
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loginLoadAccessRightList(): void
|
||||||
|
{
|
||||||
|
// read the current edit_access_right list into an array
|
||||||
|
$q = <<<SQL
|
||||||
|
SELECT
|
||||||
|
level, type, name
|
||||||
|
FROM
|
||||||
|
edit_access_right
|
||||||
|
WHERE
|
||||||
|
level >= 0
|
||||||
|
ORDER BY
|
||||||
|
level
|
||||||
|
SQL;
|
||||||
|
while (is_array($res = $this->db->dbReturn($q))) {
|
||||||
|
// level to description format (numeric)
|
||||||
|
$this->default_acl_list[$res['level']] = [
|
||||||
|
'type' => $res['type'],
|
||||||
|
'name' => $res['name']
|
||||||
|
];
|
||||||
|
$this->default_acl_list_type[(string)$res['type']] = (int)$res['level'];
|
||||||
|
}
|
||||||
|
// write that into the session
|
||||||
|
$this->session->setMany([
|
||||||
|
'LOGIN_DEFAULT_ACL_LIST' => $this->default_acl_list,
|
||||||
|
'LOGIN_DEFAULT_ACL_LIST_TYPE' => $this->default_acl_list_type,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Improves the application's security over HTTP(S) by setting specific headers
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function loginEnhanceHttpSecurity(): void
|
||||||
|
{
|
||||||
|
// skip if not wanted
|
||||||
|
if (!$this->header_enhance_security) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// remove exposure of PHP version (at least where possible)
|
||||||
|
header_remove('X-Powered-By');
|
||||||
|
// if the user is signed in
|
||||||
|
if ($this->permission_okay) {
|
||||||
|
// prevent clickjacking
|
||||||
|
header('X-Frame-Options: sameorigin');
|
||||||
|
// prevent content sniffing (MIME sniffing)
|
||||||
|
header('X-Content-Type-Options: nosniff');
|
||||||
|
|
||||||
|
// disable caching of potentially sensitive data
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate', true);
|
||||||
|
header('Expires: Thu, 19 Nov 1981 00:00:00 GMT', true);
|
||||||
|
header('Pragma: no-cache', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: validation checks
|
// MARK: validation checks
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -649,6 +735,7 @@ class Login
|
|||||||
* @param int $locked Locked because of too many invalid passwords
|
* @param int $locked Locked because of too many invalid passwords
|
||||||
* @param int $locked_period Locked because of time period set
|
* @param int $locked_period Locked because of time period set
|
||||||
* @param int $login_user_id_locked Locked from using Login User Id
|
* @param int $login_user_id_locked Locked from using Login User Id
|
||||||
|
* @param int $force_logout Force logout counter, if higher than session, permission is false
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function loginValidationCheck(
|
private function loginValidationCheck(
|
||||||
@@ -656,7 +743,8 @@ class Login
|
|||||||
int $enabled,
|
int $enabled,
|
||||||
int $locked,
|
int $locked,
|
||||||
int $locked_period,
|
int $locked_period,
|
||||||
int $login_user_id_locked
|
int $login_user_id_locked,
|
||||||
|
int $force_logout
|
||||||
): bool {
|
): bool {
|
||||||
$validation = false;
|
$validation = false;
|
||||||
if ($deleted) {
|
if ($deleted) {
|
||||||
@@ -674,6 +762,8 @@ class Login
|
|||||||
} elseif ($login_user_id_locked) {
|
} elseif ($login_user_id_locked) {
|
||||||
// user is locked, either set or auto set
|
// user is locked, either set or auto set
|
||||||
$this->login_error = 108;
|
$this->login_error = 108;
|
||||||
|
} elseif ($force_logout > $this->session->get('LOGIN_FORCE_LOGOUT')) {
|
||||||
|
$this->login_error = 110;
|
||||||
} else {
|
} else {
|
||||||
$validation = true;
|
$validation = true;
|
||||||
}
|
}
|
||||||
@@ -757,7 +847,112 @@ class Login
|
|||||||
return $login_id_ok;
|
return $login_id_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: login user action
|
/**
|
||||||
|
* write error data for login errors
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $res
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loginWriteLoginError(array $res)
|
||||||
|
{
|
||||||
|
if (!$this->login_error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$login_error_date_first = '';
|
||||||
|
if ($res['login_error_count'] == 0) {
|
||||||
|
$login_error_date_first = ", login_error_date_first = NOW()";
|
||||||
|
}
|
||||||
|
// update login error count for this user
|
||||||
|
$q = <<<SQL
|
||||||
|
UPDATE edit_user
|
||||||
|
SET
|
||||||
|
login_error_count = login_error_count + 1,
|
||||||
|
login_error_date_last = NOW()
|
||||||
|
{LOGIN_ERROR_SQL}
|
||||||
|
WHERE edit_user_id = $1
|
||||||
|
SQL;
|
||||||
|
$this->db->dbExecParams(
|
||||||
|
str_replace('{LOGIN_ERROR_SQL}', $login_error_date_first, $q),
|
||||||
|
[$res['edit_user_id']]
|
||||||
|
);
|
||||||
|
// totally lock the user if error max is reached
|
||||||
|
if (
|
||||||
|
$this->max_login_error_count != -1 &&
|
||||||
|
$res['login_error_count'] + 1 > $this->max_login_error_count
|
||||||
|
) {
|
||||||
|
// do some alert reporting in case this error is too big
|
||||||
|
// if strict is set, lock this user
|
||||||
|
// this needs manual unlocking by an admin user
|
||||||
|
if ($res['strict'] && !in_array($this->username, $this->lock_deny_users)) {
|
||||||
|
$q = <<<SQL
|
||||||
|
UPDATE edit_user
|
||||||
|
SET locked = 1
|
||||||
|
WHERE edit_user_id = $1
|
||||||
|
SQL;
|
||||||
|
// [$res['edit_user_id']]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the core edit_user table id/cuid/cuuid
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $res
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loginSetEditUserUidData(array $res)
|
||||||
|
{
|
||||||
|
// normal user processing
|
||||||
|
// set class var and session var
|
||||||
|
$this->edit_user_id = (int)$res['edit_user_id'];
|
||||||
|
$this->edit_user_cuid = (string)$res['cuid'];
|
||||||
|
$this->edit_user_cuuid = (string)$res['cuuid'];
|
||||||
|
$this->session->setMany([
|
||||||
|
'LOGIN_EUID' => $this->edit_user_id,
|
||||||
|
'LOGIN_EUCUID' => $this->edit_user_cuid,
|
||||||
|
'LOGIN_EUCUUID' => $this->edit_user_cuuid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check for re-loading of ACL data after a period of time
|
||||||
|
* or if any of the core session vars is not set
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loginAuthResync()
|
||||||
|
{
|
||||||
|
if (!$this->session->get('LOGIN_LAST_AUTH_RESYNC')) {
|
||||||
|
$this->session->set('LOGIN_LAST_AUTH_RESYNC', 0);
|
||||||
|
}
|
||||||
|
// reauth on missing session vars and timed out re-sync interval
|
||||||
|
$mandatory_session_vars = [
|
||||||
|
'LOGIN_USER_NAME', 'LOGIN_GROUP_NAME', 'LOGIN_EUCUID', 'LOGIN_EUCUUID',
|
||||||
|
'LOGIN_USER_ADDITIONAL_ACL', 'LOGIN_GROUP_ADDITIONAL_ACL',
|
||||||
|
'LOGIN_ADMIN', 'LOGIN_GROUP_ACL_LEVEL', 'LOGIN_PAGES_ACL_LEVEL', 'LOGIN_USER_ACL_LEVEL',
|
||||||
|
'LOGIN_UNIT', 'LOGIN_UNIT_DEFAULT_EACUID'
|
||||||
|
];
|
||||||
|
$force_reauth = false;
|
||||||
|
foreach ($mandatory_session_vars as $_session_var) {
|
||||||
|
if (!isset($_SESSION[$_session_var])) {
|
||||||
|
$force_reauth = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
$this->session->get('LOGIN_LAST_AUTH_RESYNC') + $this->auth_resync_interval <= time() &&
|
||||||
|
$force_reauth == false
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (($res = $this->loginLoadUserData($this->edit_user_cuuid)) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// set the session vars
|
||||||
|
$this->loginSetSession($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: MAIN LOGIN ACTION
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if user pressed login button this script is called,
|
* if user pressed login button this script is called,
|
||||||
@@ -769,6 +964,10 @@ class Login
|
|||||||
{
|
{
|
||||||
// if pressed login at least and is not yet loggined in
|
// if pressed login at least and is not yet loggined in
|
||||||
if ($this->edit_user_cuuid || (!$this->login && !$this->login_user_id)) {
|
if ($this->edit_user_cuuid || (!$this->login && !$this->login_user_id)) {
|
||||||
|
// run reload user data based on re-auth timeout, but only if we got a set cuuid
|
||||||
|
if ($this->edit_user_cuuid) {
|
||||||
|
$this->loginAuthResync();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if not username AND password where given
|
// if not username AND password where given
|
||||||
@@ -778,16 +977,105 @@ class Login
|
|||||||
$this->permission_okay = false;
|
$this->permission_okay = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// have to get the global stuff here for setting it later
|
// load user data, abort on error
|
||||||
// we have to get the themes in here too
|
if (($res = $this->loginLoadUserData()) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if login errors is half of max errors and the last login error
|
||||||
|
// was less than 10s ago, forbid any new login try
|
||||||
|
|
||||||
|
// check flow
|
||||||
|
// - user is enabled
|
||||||
|
// - user is not locked
|
||||||
|
// - password is readable
|
||||||
|
// - encrypted password matches
|
||||||
|
// - plain password matches
|
||||||
|
if (
|
||||||
|
!$this->loginValidationCheck(
|
||||||
|
(int)$res['deleted'],
|
||||||
|
(int)$res['enabled'],
|
||||||
|
(int)$res['locked'],
|
||||||
|
(int)$res['locked_period'],
|
||||||
|
(int)$res['login_user_id_locked'],
|
||||||
|
(int)$res['force_logout']
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// error set in method (104, 105, 106, 107, 108)
|
||||||
|
} elseif (
|
||||||
|
empty($this->username) &&
|
||||||
|
!empty($this->login_user_id) &&
|
||||||
|
!$this->loginLoginUserIdCheck(
|
||||||
|
(int)$res['login_user_id_valid_date'],
|
||||||
|
(int)$res['login_user_id_revalidate']
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// check done in loginLoginIdCheck method
|
||||||
|
// aborts on must revalidate and not valid (date range)
|
||||||
|
} elseif (
|
||||||
|
!empty($this->username) &&
|
||||||
|
!$this->loginPasswordCheck($res['password'])
|
||||||
|
) {
|
||||||
|
// none to be set, set in login password check
|
||||||
|
// this is not valid password input error here
|
||||||
|
// all error codes are set in loginPasswordCheck method
|
||||||
|
// also valid if login_user_id is ok
|
||||||
|
} else {
|
||||||
|
// check if the current password is an invalid hash and do a rehash and set password
|
||||||
|
// $this->debug('LOGIN', 'Hash: '.$res['password'].' -> VERIFY: '
|
||||||
|
// .($Password::passwordVerify($this->password, $res['password']) ? 'OK' : 'FAIL')
|
||||||
|
// .' => HASH: '.(Password::passwordRehashCheck($res['password']) ? 'NEW NEEDED' : 'OK'));
|
||||||
|
if (Password::passwordRehashCheck($res['password'])) {
|
||||||
|
// update password hash to new one now
|
||||||
|
$q = <<<SQL
|
||||||
|
UPDATE edit_user
|
||||||
|
SET password = $1
|
||||||
|
WHERE edit_user_id = $2
|
||||||
|
SQL;
|
||||||
|
$this->db->dbExecParams($q, [
|
||||||
|
Password::passwordSet($this->password),
|
||||||
|
$res['edit_user_id']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// normal user processing
|
||||||
|
// set class var and session var
|
||||||
|
$this->loginSetEditUserUidData($res);
|
||||||
|
// set the last login time stamp for normal login only (not for reauthenticate)
|
||||||
|
$this->db->dbExecParams(<<<SQL
|
||||||
|
UPDATE edit_user SET
|
||||||
|
last_login = NOW()
|
||||||
|
WHERE
|
||||||
|
edit_user_id = $1
|
||||||
|
SQL, [$this->edit_user_id]);
|
||||||
|
// set the session vars
|
||||||
|
$this->loginSetSession($res);
|
||||||
|
} // user was not enabled or other login error
|
||||||
|
// check for login error and write to the user
|
||||||
|
$this->loginWriteLoginError($res);
|
||||||
|
// if there was an login error, show login screen
|
||||||
|
if ($this->login_error) {
|
||||||
|
// reset the perm var, to confirm logout
|
||||||
|
$this->permission_okay = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load user data and all connect4ed settings
|
||||||
|
*
|
||||||
|
* @param ?string $edit_user_cuuid for re-auth
|
||||||
|
* @return array<string,mixed>|false
|
||||||
|
*/
|
||||||
|
private function loginLoadUserData(?string $edit_user_cuuid = null): array|false
|
||||||
|
{
|
||||||
$q = <<<SQL
|
$q = <<<SQL
|
||||||
SELECT
|
SELECT
|
||||||
eu.edit_user_id, eu.cuid, eu.cuuid, eu.username, eu.password,
|
eu.edit_user_id, eu.cuid, eu.cuuid, eu.username, eu.password, eu.email,
|
||||||
eu.edit_group_id,
|
eu.edit_group_id,
|
||||||
eg.name AS edit_group_name, eu.admin,
|
eg.name AS edit_group_name, eu.admin,
|
||||||
-- additinal acl lists
|
-- additinal acl lists
|
||||||
eu.additional_acl AS user_additional_acl,
|
eu.additional_acl AS user_additional_acl,
|
||||||
eg.additional_acl AS group_additional_acl,
|
eg.additional_acl AS group_additional_acl,
|
||||||
|
-- force logoutp counter
|
||||||
|
eu.force_logout,
|
||||||
-- login error + locked
|
-- login error + locked
|
||||||
eu.login_error_count, eu.login_error_date_last,
|
eu.login_error_count, eu.login_error_date_last,
|
||||||
eu.login_error_date_first, eu.strict, eu.locked,
|
eu.login_error_date_first, eu.strict, eu.locked,
|
||||||
@@ -802,8 +1090,6 @@ class Login
|
|||||||
OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after)
|
OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after)
|
||||||
)
|
)
|
||||||
) THEN 0::INT ELSE 1::INT END locked_period,
|
) THEN 0::INT ELSE 1::INT END locked_period,
|
||||||
-- debug (legacy)
|
|
||||||
eu.debug, eu.db_debug,
|
|
||||||
-- enabled
|
-- enabled
|
||||||
eu.enabled, eu.deleted,
|
eu.enabled, eu.deleted,
|
||||||
-- for checks only
|
-- for checks only
|
||||||
@@ -851,8 +1137,12 @@ class Login
|
|||||||
SQL;
|
SQL;
|
||||||
$params = [];
|
$params = [];
|
||||||
$replace_string = '';
|
$replace_string = '';
|
||||||
// either login_user_id OR password must be given
|
// if login is OK and we have edit_user_cuuid as parameter, then this is internal re-auth
|
||||||
if (!empty($this->login_user_id && empty($this->username))) {
|
// else login_user_id OR password must be given
|
||||||
|
if (!empty($edit_user_cuuid)) {
|
||||||
|
$replace_string = 'eu.cuuid = $1';
|
||||||
|
$params = [$this->edit_user_cuuid];
|
||||||
|
} elseif (!empty($this->login_user_id) && empty($this->username)) {
|
||||||
// check with login id if set and NO username
|
// check with login id if set and NO username
|
||||||
$replace_string = 'eu.login_user_id = $1';
|
$replace_string = 'eu.login_user_id = $1';
|
||||||
$params = [$this->login_user_id];
|
$params = [$this->login_user_id];
|
||||||
@@ -874,83 +1164,39 @@ class Login
|
|||||||
if (!empty($this->db->dbGetLastError())) {
|
if (!empty($this->db->dbGetLastError())) {
|
||||||
$this->login_error = 1009;
|
$this->login_error = 1009;
|
||||||
$this->permission_okay = false;
|
$this->permission_okay = false;
|
||||||
return;
|
return false;
|
||||||
} elseif (!is_array($res)) {
|
} elseif (!is_array($res)) {
|
||||||
// username is wrong, but we throw for wrong username
|
// username is wrong, but we throw for wrong username
|
||||||
// and wrong password the same error
|
// and wrong password the same error
|
||||||
|
// unless with have edit user cuuid set then we run an general ACL error
|
||||||
|
if (empty($edit_user_cuuid)) {
|
||||||
$this->login_error = 1010;
|
$this->login_error = 1010;
|
||||||
|
} else {
|
||||||
|
$this->login_error = 1011;
|
||||||
|
}
|
||||||
$this->permission_okay = false;
|
$this->permission_okay = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: login set all session variables
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set all the _SESSION variables
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $res user data loaded query result
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loginSetSession(array $res): void
|
||||||
|
{
|
||||||
|
// user has permission to THIS page
|
||||||
|
if ($this->login_error != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if login errors is half of max errors and the last login error
|
|
||||||
// was less than 10s ago, forbid any new login try
|
|
||||||
|
|
||||||
// check flow
|
|
||||||
// - user is enabled
|
|
||||||
// - user is not locked
|
|
||||||
// - password is readable
|
|
||||||
// - encrypted password matches
|
|
||||||
// - plain password matches
|
|
||||||
if (
|
|
||||||
!$this->loginValidationCheck(
|
|
||||||
(int)$res['deleted'],
|
|
||||||
(int)$res['enabled'],
|
|
||||||
(int)$res['locked'],
|
|
||||||
(int)$res['locked_period'],
|
|
||||||
(int)$res['login_user_id_locked']
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// error set in method (104, 105, 106, 107, 108)
|
|
||||||
} elseif (
|
|
||||||
empty($this->username) &&
|
|
||||||
!empty($this->login_user_id) &&
|
|
||||||
!$this->loginLoginUserIdCheck(
|
|
||||||
(int)$res['login_user_id_valid_date'],
|
|
||||||
(int)$res['login_user_id_revalidate']
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// check done in loginLoginIdCheck method
|
|
||||||
// aborts on must revalidate and not valid (date range)
|
|
||||||
} elseif (
|
|
||||||
!empty($this->username) &&
|
|
||||||
!$this->loginPasswordCheck($res['password'])
|
|
||||||
) {
|
|
||||||
// none to be set, set in login password check
|
|
||||||
// this is not valid password input error here
|
|
||||||
// all error codes are set in loginPasswordCheck method
|
|
||||||
// also valid if login_user_id is ok
|
|
||||||
} else {
|
|
||||||
// check if the current password is an invalid hash and do a rehash and set password
|
|
||||||
// $this->debug('LOGIN', 'Hash: '.$res['password'].' -> VERIFY: '
|
|
||||||
// .($Password::passwordVerify($this->password, $res['password']) ? 'OK' : 'FAIL')
|
|
||||||
// .' => HASH: '.(Password::passwordRehashCheck($res['password']) ? 'NEW NEEDED' : 'OK'));
|
|
||||||
if (Password::passwordRehashCheck($res['password'])) {
|
|
||||||
// update password hash to new one now
|
|
||||||
$q = <<<SQL
|
|
||||||
UPDATE edit_user
|
|
||||||
SET password = $1
|
|
||||||
WHERE edit_user_id = $2
|
|
||||||
SQL;
|
|
||||||
$this->db->dbExecParams($q, [
|
|
||||||
Password::passwordSet($this->password),
|
|
||||||
$res['edit_user_id']
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
// normal user processing
|
|
||||||
// set class var and session var
|
|
||||||
$this->edit_user_id = (int)$res['edit_user_id'];
|
|
||||||
$this->edit_user_cuid = (string)$res['cuid'];
|
|
||||||
$this->edit_user_cuuid = (string)$res['cuuid'];
|
|
||||||
$this->session->setMany([
|
|
||||||
'LOGIN_EUID' => $this->edit_user_id, // DEPRECATED
|
|
||||||
'LOGIN_EUCUID' => $this->edit_user_cuid,
|
|
||||||
'LOGIN_EUCUUID' => $this->edit_user_cuuid,
|
|
||||||
]);
|
|
||||||
// check if user is okay
|
|
||||||
$this->loginCheckPermissions();
|
|
||||||
if ($this->login_error == 0) {
|
|
||||||
// set the dit group id
|
// set the dit group id
|
||||||
$edit_group_id = $res["edit_group_id"];
|
$edit_group_id = $res["edit_group_id"];
|
||||||
|
$edit_user_id = (int)$res['edit_user_id'];
|
||||||
// update last revalidate flag
|
// update last revalidate flag
|
||||||
if (
|
if (
|
||||||
!empty($res['login_user_id']) &&
|
!empty($res['login_user_id']) &&
|
||||||
@@ -961,7 +1207,7 @@ class Login
|
|||||||
SET login_user_id_last_revalidate = NOW()
|
SET login_user_id_last_revalidate = NOW()
|
||||||
WHERE edit_user_id = $1
|
WHERE edit_user_id = $1
|
||||||
SQL;
|
SQL;
|
||||||
$this->db->dbExecParams($q, [$this->edit_user_id]);
|
$this->db->dbExecParams($q, [$edit_user_id]);
|
||||||
}
|
}
|
||||||
$locale = $res['locale'] ?? 'en';
|
$locale = $res['locale'] ?? 'en';
|
||||||
$encoding = $res['encoding'] ?? 'UTF-8';
|
$encoding = $res['encoding'] ?? 'UTF-8';
|
||||||
@@ -970,8 +1216,13 @@ class Login
|
|||||||
// DEBUG flag is deprecated
|
// DEBUG flag is deprecated
|
||||||
// 'DEBUG_ALL' => $this->db->dbBoolean($res['debug']),
|
// 'DEBUG_ALL' => $this->db->dbBoolean($res['debug']),
|
||||||
// 'DB_DEBUG' => $this->db->dbBoolean($res['db_debug']),
|
// 'DB_DEBUG' => $this->db->dbBoolean($res['db_debug']),
|
||||||
|
// login timestamp
|
||||||
|
'LOGIN_LAST_AUTH_RESYNC' => time(),
|
||||||
|
// current forced logout counter
|
||||||
|
'LOGIN_FORCE_LOGOUT' => $res['force_logout'],
|
||||||
// general info for user logged in
|
// general info for user logged in
|
||||||
'LOGIN_USER_NAME' => $res['username'],
|
'LOGIN_USER_NAME' => $res['username'],
|
||||||
|
'LOGIN_EMAIL' => $res['email'],
|
||||||
'LOGIN_ADMIN' => $res['admin'],
|
'LOGIN_ADMIN' => $res['admin'],
|
||||||
'LOGIN_GROUP_NAME' => $res['edit_group_name'],
|
'LOGIN_GROUP_NAME' => $res['edit_group_name'],
|
||||||
'LOGIN_USER_ACL_LEVEL' => $res['user_level'],
|
'LOGIN_USER_ACL_LEVEL' => $res['user_level'],
|
||||||
@@ -1014,7 +1265,7 @@ class Login
|
|||||||
login_error_date_first = NULL
|
login_error_date_first = NULL
|
||||||
WHERE edit_user_id = $1
|
WHERE edit_user_id = $1
|
||||||
SQL;
|
SQL;
|
||||||
$this->db->dbExecParams($q, [$this->edit_user_id]);
|
$this->db->dbExecParams($q, [$edit_user_id]);
|
||||||
}
|
}
|
||||||
$edit_page_ids = [];
|
$edit_page_ids = [];
|
||||||
$pages = [];
|
$pages = [];
|
||||||
@@ -1143,7 +1394,7 @@ class Login
|
|||||||
$eacuid = [];
|
$eacuid = [];
|
||||||
$unit_acl = [];
|
$unit_acl = [];
|
||||||
$unit_uid_lookup = [];
|
$unit_uid_lookup = [];
|
||||||
while (is_array($res = $this->db->dbReturnParams($q, [$this->edit_user_id]))) {
|
while (is_array($res = $this->db->dbReturnParams($q, [$edit_user_id]))) {
|
||||||
// read edit access data fields and drop them into the unit access array
|
// read edit access data fields and drop them into the unit access array
|
||||||
$q_sub = <<<SQL
|
$q_sub = <<<SQL
|
||||||
SELECT name, value
|
SELECT name, value
|
||||||
@@ -1171,6 +1422,10 @@ class Login
|
|||||||
'cuid' => $res['cuid'],
|
'cuid' => $res['cuid'],
|
||||||
];
|
];
|
||||||
// set the default unit
|
// set the default unit
|
||||||
|
$this->session->setMany([
|
||||||
|
'LOGIN_UNIT_DEFAULT_EAID' => null,
|
||||||
|
'LOGIN_UNIT_DEFAULT_EACUID' => null,
|
||||||
|
]);
|
||||||
if ($res['edit_default']) {
|
if ($res['edit_default']) {
|
||||||
$this->session->set('LOGIN_UNIT_DEFAULT_EAID', (int)$res['edit_access_id']); // DEPRECATED
|
$this->session->set('LOGIN_UNIT_DEFAULT_EAID', (int)$res['edit_access_id']); // DEPRECATED
|
||||||
$this->session->set('LOGIN_UNIT_DEFAULT_EACUID', (int)$res['cuid']);
|
$this->session->set('LOGIN_UNIT_DEFAULT_EACUID', (int)$res['cuid']);
|
||||||
@@ -1191,49 +1446,6 @@ class Login
|
|||||||
'LOGIN_EAID' => $eaid, // DEPRECATED
|
'LOGIN_EAID' => $eaid, // DEPRECATED
|
||||||
'LOGIN_EACUID' => $eacuid,
|
'LOGIN_EACUID' => $eacuid,
|
||||||
]);
|
]);
|
||||||
} // user has permission to THIS page
|
|
||||||
} // user was not enabled or other login error
|
|
||||||
if ($this->login_error && is_array($res)) {
|
|
||||||
$login_error_date_first = '';
|
|
||||||
if ($res['login_error_count'] == 0) {
|
|
||||||
$login_error_date_first = ", login_error_date_first = NOW()";
|
|
||||||
}
|
|
||||||
// update login error count for this user
|
|
||||||
$q = <<<SQL
|
|
||||||
UPDATE edit_user
|
|
||||||
SET
|
|
||||||
login_error_count = login_error_count + 1,
|
|
||||||
login_error_date_last = NOW()
|
|
||||||
{LOGIN_ERROR_SQL}
|
|
||||||
WHERE edit_user_id = $1
|
|
||||||
SQL;
|
|
||||||
$this->db->dbExecParams(
|
|
||||||
str_replace('{LOGIN_ERROR_SQL}', $login_error_date_first, $q),
|
|
||||||
[$res['edit_user_id']]
|
|
||||||
);
|
|
||||||
// totally lock the user if error max is reached
|
|
||||||
if (
|
|
||||||
$this->max_login_error_count != -1 &&
|
|
||||||
$res['login_error_count'] + 1 > $this->max_login_error_count
|
|
||||||
) {
|
|
||||||
// do some alert reporting in case this error is too big
|
|
||||||
// if strict is set, lock this user
|
|
||||||
// this needs manual unlocking by an admin user
|
|
||||||
if ($res['strict'] && !in_array($this->username, $this->lock_deny_users)) {
|
|
||||||
$q = <<<SQL
|
|
||||||
UPDATE edit_user
|
|
||||||
SET locked = 1
|
|
||||||
WHERE edit_user_id = $1
|
|
||||||
SQL;
|
|
||||||
// [$res['edit_user_id']]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if there was an login error, show login screen
|
|
||||||
if ($this->login_error) {
|
|
||||||
// reset the perm var, to confirm logout
|
|
||||||
$this->permission_okay = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: login set ACL
|
// MARK: login set ACL
|
||||||
@@ -1361,7 +1573,7 @@ class Login
|
|||||||
$this->acl['show_ea_extra'] = false;
|
$this->acl['show_ea_extra'] = false;
|
||||||
}
|
}
|
||||||
// set the default edit access
|
// set the default edit access
|
||||||
$this->acl['default_edit_access'] = $_SESSION['UNIT_DEFAULT'] ?? null;
|
$this->acl['default_edit_access'] = $_SESSION['LOGIN_UNIT_DEFAULT_EACUID'];
|
||||||
// integrate the type acl list, but only for the keyword -> level
|
// integrate the type acl list, but only for the keyword -> level
|
||||||
$this->acl['min'] = $this->default_acl_list_type;
|
$this->acl['min'] = $this->default_acl_list_type;
|
||||||
// set the full acl list too (lookup level number and get level data)
|
// set the full acl list too (lookup level number and get level data)
|
||||||
@@ -2159,7 +2371,7 @@ HTML;
|
|||||||
}
|
}
|
||||||
$q = <<<SQL
|
$q = <<<SQL
|
||||||
INSERT INTO {DB_SCHEMA}.edit_log (
|
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, ip_address, user_agent, referer, script_name, query_string, request_scheme, server_name,
|
ip, ip_address, user_agent, referer, script_name, query_string, request_scheme, server_name,
|
||||||
http_host, http_data, session_id,
|
http_host, http_data, session_id,
|
||||||
action_data
|
action_data
|
||||||
@@ -2198,7 +2410,7 @@ HTML;
|
|||||||
// row 2
|
// row 2
|
||||||
$_SERVER["REMOTE_ADDR"] ?? null,
|
$_SERVER["REMOTE_ADDR"] ?? null,
|
||||||
Json::jsonConvertArrayTo([
|
Json::jsonConvertArrayTo([
|
||||||
'REMOTE_ADDR' => $_SERVER["REMOTE_ADDR"],
|
'REMOTE_ADDR' => $_SERVER["REMOTE_ADDR"] ?? null,
|
||||||
'HTTP_X_FORWARDED_FOR' => !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ?
|
'HTTP_X_FORWARDED_FOR' => !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ?
|
||||||
explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])
|
explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])
|
||||||
: [],
|
: [],
|
||||||
@@ -2262,7 +2474,7 @@ HTML;
|
|||||||
// **** PUBLIC INTERNAL
|
// **** PUBLIC INTERNAL
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
|
|
||||||
// MARK: PUBLIC LOGIN CALL
|
// MARK: MASTER PUBLIC LOGIN CALL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main call that needs to be run to actaully check for login
|
* Main call that needs to be run to actaully check for login
|
||||||
@@ -2355,10 +2567,12 @@ HTML;
|
|||||||
|
|
||||||
// if username & password & !$euid start login
|
// if username & password & !$euid start login
|
||||||
$this->loginLoginUser();
|
$this->loginLoginUser();
|
||||||
// checks if $euid given check if user is okay for that side
|
// checks if $euid given check if user is okay for that site
|
||||||
$this->loginCheckPermissions();
|
$this->loginCheckPermissions();
|
||||||
// logsout user
|
// logout user
|
||||||
$this->loginLogoutUser();
|
$this->loginLogoutUser();
|
||||||
|
// set headers for enhanced security
|
||||||
|
$this->loginEnhanceHttpSecurity();
|
||||||
// ** LANGUAGE SET AFTER LOGIN **
|
// ** LANGUAGE SET AFTER LOGIN **
|
||||||
$this->loginSetLocale();
|
$this->loginSetLocale();
|
||||||
// load translator
|
// load translator
|
||||||
@@ -2513,7 +2727,7 @@ HTML;
|
|||||||
return $this->session->get('LOGIN_PAGES');
|
return $this->session->get('LOGIN_PAGES');
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: logged in uid(pk)/cuid/ecuuid
|
// MARK: logged in uid(pk)/eucuid/eucuuid
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current set EUID (edit user id)
|
* Get the current set EUID (edit user id)
|
||||||
@@ -2724,14 +2938,14 @@ HTML;
|
|||||||
if (empty($this->edit_user_cuuid)) {
|
if (empty($this->edit_user_cuuid)) {
|
||||||
return $this->permission_okay;
|
return $this->permission_okay;
|
||||||
}
|
}
|
||||||
// euid must match ecuid and ecuuid
|
// euid must match eucuid and eucuuid
|
||||||
// bail for previous wrong page match, eg if method is called twice
|
// bail for previous wrong page match, eg if method is called twice
|
||||||
if ($this->login_error == 103) {
|
if ($this->login_error == 103) {
|
||||||
return $this->permission_okay;
|
return $this->permission_okay;
|
||||||
}
|
}
|
||||||
$q = <<<SQL
|
$q = <<<SQL
|
||||||
SELECT
|
SELECT
|
||||||
ep.filename, eu.edit_user_id, eu.cuid, eu.cuuid,
|
ep.filename, eu.edit_user_id, eu.cuid, eu.cuuid, eu.force_logout,
|
||||||
-- base lock flags
|
-- base lock flags
|
||||||
eu.deleted, eu.enabled, eu.locked,
|
eu.deleted, eu.enabled, eu.locked,
|
||||||
-- date based lock
|
-- date based lock
|
||||||
@@ -2786,7 +3000,8 @@ HTML;
|
|||||||
(int)$res['enabled'],
|
(int)$res['enabled'],
|
||||||
(int)$res['locked'],
|
(int)$res['locked'],
|
||||||
(int)$res['locked_period'],
|
(int)$res['locked_period'],
|
||||||
(int)$res['login_user_id_locked']
|
(int)$res['login_user_id_locked'],
|
||||||
|
(int)$res['force_logout']
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// errors set in method
|
// errors set in method
|
||||||
@@ -2810,14 +3025,7 @@ HTML;
|
|||||||
$this->login_error = 103;
|
$this->login_error = 103;
|
||||||
}
|
}
|
||||||
// set all the internal vars
|
// set all the internal vars
|
||||||
$this->edit_user_id = (int)$res['edit_user_id'];
|
$this->loginSetEditUserUidData($res);
|
||||||
$this->edit_user_cuid = (string)$res['cuid'];
|
|
||||||
$this->edit_user_cuuid = (string)$res['cuuid'];
|
|
||||||
$this->session->setMany([
|
|
||||||
'LOGIN_EUID' => $this->edit_user_id, // DEPRECATED
|
|
||||||
'LOGIN_EUCUID' => $this->edit_user_cuid,
|
|
||||||
'LOGIN_EUCUUID' => $this->edit_user_cuuid,
|
|
||||||
]);
|
|
||||||
// if called from public, so we can check if the permissions are ok
|
// if called from public, so we can check if the permissions are ok
|
||||||
return $this->permission_okay;
|
return $this->permission_okay;
|
||||||
}
|
}
|
||||||
|
|||||||
68
www/lib/CoreLibs/ACL/LoginUserStatus.php
Normal file
68
www/lib/CoreLibs/ACL/LoginUserStatus.php
Normal 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__
|
||||||
@@ -358,7 +358,7 @@ class Backend
|
|||||||
}
|
}
|
||||||
$q = <<<SQL
|
$q = <<<SQL
|
||||||
INSERT INTO {DB_SCHEMA}.edit_log (
|
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,
|
ip, user_agent, referer, script_name, query_string, server_name, http_host,
|
||||||
http_accept, http_accept_charset, http_accept_encoding, session_id,
|
http_accept, http_accept_charset, http_accept_encoding, session_id,
|
||||||
action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded,
|
action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded,
|
||||||
|
|||||||
@@ -527,7 +527,9 @@ class ArrayHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From the array with key -> anything values return only the matching entries from key list
|
* 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]
|
* key list is a list[string]
|
||||||
* if key list is empty, return array as is
|
* if key list is empty, return array as is
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -21,21 +21,107 @@ class Session
|
|||||||
private string $session_id = '';
|
private string $session_id = '';
|
||||||
/** @var bool flag auto write close */
|
/** @var bool flag auto write close */
|
||||||
private bool $auto_write_close = false;
|
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
|
* init a session, if array is empty or array does not have session_name set
|
||||||
* then no auto init is run
|
* then no auto init is run
|
||||||
*
|
*
|
||||||
* @param string $session_name if set and not empty, will start session
|
* @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->initSession($session_name);
|
||||||
$this->auto_write_close = $auto_write_close;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: private methods
|
// 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
|
* Start session
|
||||||
* startSession should be called for complete check
|
* startSession should be called for complete check
|
||||||
@@ -72,6 +158,72 @@ class Session
|
|||||||
return false;
|
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
|
* check if session name is valid
|
||||||
*
|
*
|
||||||
@@ -151,6 +303,13 @@ class Session
|
|||||||
if (!$this->checkActiveSession()) {
|
if (!$this->checkActiveSession()) {
|
||||||
throw new \RuntimeException('[SESSION] Failed to activate session', 5);
|
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()) {
|
} elseif ($session_name != $this->getSessionName()) {
|
||||||
throw new \UnexpectedValueException(
|
throw new \UnexpectedValueException(
|
||||||
'[SESSION] Another session exists with a different name: ' . $this->getSessionName(),
|
'[SESSION] Another session exists with a different name: ' . $this->getSessionName(),
|
||||||
@@ -159,10 +318,12 @@ class Session
|
|||||||
}
|
}
|
||||||
// check session id
|
// check session id
|
||||||
if (false === ($session_id = $this->getSessionIdCall())) {
|
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
|
// set session id
|
||||||
$this->session_id = $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 flagged auto close, write close session
|
||||||
if ($this->auto_write_close) {
|
if ($this->auto_write_close) {
|
||||||
$this->writeClose();
|
$this->writeClose();
|
||||||
|
|||||||
@@ -1332,7 +1332,6 @@ class IO
|
|||||||
*/
|
*/
|
||||||
private function __dbCheckQueryParams(string $query, array $params): bool
|
private function __dbCheckQueryParams(string $query, array $params): bool
|
||||||
{
|
{
|
||||||
// $this->log->debug('DB QUERY PARAMS REGEX', ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS);
|
|
||||||
$placeholder_count = $this->__dbCountQueryParams($query);
|
$placeholder_count = $this->__dbCountQueryParams($query);
|
||||||
$params_count = count($params);
|
$params_count = count($params);
|
||||||
if ($params_count != $placeholder_count) {
|
if ($params_count != $placeholder_count) {
|
||||||
|
|||||||
@@ -18,17 +18,20 @@ class ConvertPlaceholder
|
|||||||
// NOTE some combinations are allowed, but the query will fail before this
|
// NOTE some combinations are allowed, but the query will fail before this
|
||||||
/** @var string split regex, entries before $ group */
|
/** @var string split regex, entries before $ group */
|
||||||
private const PATTERN_QUERY_SPLIT =
|
private const PATTERN_QUERY_SPLIT =
|
||||||
',|' // for ',' mostly in INSERT
|
'\?\?|' // UNKNOWN: double ??, is this to avoid something?
|
||||||
. '[(<>=]|' // general set for (, <, >, = in any query with any combination
|
. '[\(,]|' // for ',' and '(' mostly in INSERT or ANY()
|
||||||
. '(?:[\(,]\s*\-\-[\s\w]*)\r?\n|' // a comment that starts after a ( or ,
|
. '[<>=]|' // general set for <, >, = in any query with any combination
|
||||||
. '\^@|' // text search for start from text with ^@
|
. '\^@|' // text search for start from text with ^@
|
||||||
. '\|\||' // concats two elements
|
. '\|\||' // concats two elements
|
||||||
. '&&|' // array overlap
|
. '&&|' // array overlap
|
||||||
. '\-\|\-|' // range overlap
|
. '\-\|\-|' // range overlap for array
|
||||||
. '[^-]-{1}|' // single -, used in JSON too
|
. '[^-]-{1}|' // single -, used in JSON too
|
||||||
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-'; //JSON searches, Array searchs, etc
|
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{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
|
||||||
@@ -45,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
|
||||||
@@ -53,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
|
||||||
@@ -61,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
|
||||||
@@ -71,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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user