Compare commits
17 Commits
2b0434e36b
...
v9.24.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4971f62490 | ||
|
|
1cf4fdf31a | ||
|
|
d16b920966 | ||
|
|
ab52bf59b5 | ||
|
|
a8dd076aac | ||
|
|
c17ca1f847 | ||
|
|
e349613d60 | ||
|
|
e9cfdb4bf0 | ||
|
|
f966209e0a | ||
|
|
c13934de99 | ||
|
|
1e90bb677e | ||
|
|
540269e61f | ||
|
|
e793c3975b | ||
|
|
7d4c9724fe | ||
|
|
d1c4611431 | ||
|
|
8d3882a6fe | ||
|
|
fdefaca301 |
@@ -5,9 +5,9 @@ RETURNS TRIGGER AS
|
||||
$$
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
NEW.date_created := 'now';
|
||||
NEW.date_created := clock_timestamp();
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
NEW.date_updated := 'now';
|
||||
NEW.date_updated := clock_timestamp();
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
|
||||
@@ -7,11 +7,11 @@ DECLARE
|
||||
random_length INT = 25; -- that should be long enough
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
NEW.date_created := 'now';
|
||||
NEW.date_created := clock_timestamp();
|
||||
NEW.cuid := random_string(random_length);
|
||||
NEW.cuuid := gen_random_uuid();
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
NEW.date_updated := 'now';
|
||||
NEW.date_updated := clock_timestamp();
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
|
||||
@@ -8,12 +8,12 @@ DECLARE
|
||||
random_length INT = 32; -- long for massive data
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
NEW.date_created := 'now';
|
||||
NEW.date_created := clock_timestamp();
|
||||
IF NEW.uid IS NULL THEN
|
||||
NEW.uid := random_string(random_length);
|
||||
END IF;
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
NEW.date_updated := 'now';
|
||||
NEW.date_updated := clock_timestamp();
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
|
||||
@@ -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,
|
||||
euid INT, -- this is a foreign key, but I don't nedd to reference to it
|
||||
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
ecuid VARCHAR,
|
||||
ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
||||
eucuid VARCHAR,
|
||||
eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
||||
-- date_created equal, but can be overridden
|
||||
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
-- session ID if set
|
||||
|
||||
@@ -37,6 +37,8 @@ CREATE TABLE edit_user (
|
||||
protected SMALLINT NOT NULL DEFAULT 0,
|
||||
-- is admin user
|
||||
admin SMALLINT NOT NULL DEFAULT 0,
|
||||
-- force lgout counter
|
||||
force_logout INT DEFAULT 0,
|
||||
-- last login log
|
||||
last_login TIMESTAMP WITHOUT TIME ZONE,
|
||||
-- login error
|
||||
@@ -74,6 +76,7 @@ COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be lo
|
||||
COMMENT ON COLUMN edit_user.locked IS 'Locked from too many wrong password logins';
|
||||
COMMENT ON COLUMN edit_user.protected IS 'User can only be chnaged by admin user';
|
||||
COMMENT ON COLUMN edit_user.admin IS 'If set, this user is SUPER admin';
|
||||
COMMENT ON COLUMN edit_user.force_logout IS 'Counter for forced log out, if this one is higher than the session set one the session gets terminated';
|
||||
COMMENT ON COLUMN edit_user.last_login IS 'Last succesfull login tiemstamp';
|
||||
COMMENT ON COLUMN edit_user.login_error_count IS 'Number of failed logins, reset on successful login';
|
||||
COMMENT ON COLUMN edit_user.login_error_date_last IS 'Last login error date';
|
||||
|
||||
@@ -1185,7 +1185,6 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
foreach ($session as $session_var => $session_value) {
|
||||
$_SESSION[$session_var] = $session_value;
|
||||
}
|
||||
|
||||
/** @var \CoreLibs\ACL\Login&MockObject */
|
||||
$login_mock = $this->getMockBuilder(\CoreLibs\ACL\Login::class)
|
||||
->setConstructorArgs([
|
||||
@@ -1204,7 +1203,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
. 'locale' . DIRECTORY_SEPARATOR,
|
||||
]
|
||||
])
|
||||
->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin'])
|
||||
->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin', 'loginEnhanceHttpSecurity'])
|
||||
->getMock();
|
||||
$login_mock->expects($this->any())
|
||||
->method('loginTerminate')
|
||||
@@ -1222,6 +1221,10 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
->method('loginPrintLogin')
|
||||
->willReturnCallback(function () {
|
||||
});
|
||||
$login_mock->expects($this->any())
|
||||
->method('loginEnhanceHttpSecurity')
|
||||
->willReturnCallback(function () {
|
||||
});
|
||||
|
||||
// if mock_settings: enabled OFF
|
||||
// run DB update and set off
|
||||
|
||||
@@ -30,11 +30,11 @@ DECLARE
|
||||
random_length INT = 12; -- that should be long enough
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
NEW.date_created := 'now';
|
||||
NEW.date_created := clock_timestamp();
|
||||
NEW.cuid := random_string(random_length);
|
||||
NEW.cuuid := gen_random_uuid();
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
NEW.date_updated := 'now';
|
||||
NEW.date_updated := clock_timestamp();
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
@@ -581,6 +581,8 @@ CREATE TABLE edit_user (
|
||||
protected SMALLINT NOT NULL DEFAULT 0,
|
||||
-- is admin user
|
||||
admin SMALLINT NOT NULL DEFAULT 0,
|
||||
-- forced logout counter
|
||||
force_logout INT DEFAULT 0,
|
||||
-- last login log
|
||||
last_login TIMESTAMP WITHOUT TIME ZONE,
|
||||
-- login error
|
||||
@@ -650,8 +652,8 @@ CREATE TABLE edit_log (
|
||||
edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
euid INT, -- this is a foreign key, but I don't nedd to reference to it
|
||||
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
ecuid VARCHAR,
|
||||
ecuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
||||
eucuid VARCHAR,
|
||||
eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
|
||||
-- date_created equal, but can be overridden
|
||||
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
-- session ID if set
|
||||
@@ -697,6 +699,7 @@ CREATE TABLE edit_log (
|
||||
action_value VARCHAR, -- in action_data
|
||||
action_type VARCHAR, -- in action_data
|
||||
action_error VARCHAR -- in action_data
|
||||
) INHERITS (edit_generic) WITHOUT OIDS;
|
||||
-- END: table/edit_log.sql
|
||||
-- START: table/edit_log_overflow.sql
|
||||
-- AUTHOR: Clemens Schwaighofer
|
||||
|
||||
@@ -54,7 +54,9 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'sessionNameGlobals',
|
||||
false,
|
||||
[
|
||||
'auto_write_close' => false,
|
||||
],
|
||||
],
|
||||
'auto write close' => [
|
||||
'sessionNameAutoWriteClose',
|
||||
@@ -66,7 +68,9 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'sessionNameAutoWriteClose',
|
||||
true,
|
||||
[
|
||||
'auto_write_close' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -81,13 +85,14 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
* @param string $input
|
||||
* @param array<mixed> $mock_data
|
||||
* @param string $expected
|
||||
* @param array<string,mixed> $options
|
||||
* @return void
|
||||
*/
|
||||
public function testStartSession(
|
||||
string $input,
|
||||
array $mock_data,
|
||||
string $expected,
|
||||
?bool $auto_write_close,
|
||||
?array $options,
|
||||
): void {
|
||||
/** @var \CoreLibs\Create\Session&MockObject $session_mock */
|
||||
$session_mock = $this->createPartialMock(
|
||||
@@ -174,9 +179,14 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
4,
|
||||
'/^\[SESSION\] Failed to activate session/'
|
||||
],
|
||||
'expired session' => [
|
||||
\RuntimeException::class,
|
||||
5,
|
||||
'/^\[SESSION\] Expired session found/'
|
||||
],
|
||||
'not a valid session id returned' => [
|
||||
\UnexpectedValueException::class,
|
||||
5,
|
||||
6,
|
||||
'/^\[SESSION\] getSessionId did not return a session id/'
|
||||
], */
|
||||
];
|
||||
@@ -206,7 +216,8 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
$this->expectException($exception);
|
||||
$this->expectExceptionCode($exception_code);
|
||||
$this->expectExceptionMessageMatches($expected_error);
|
||||
new \CoreLibs\Create\Session($session_name);
|
||||
// cannot set ini after header sent, plus we are on command line there are no headers
|
||||
new \CoreLibs\Create\Session($session_name, ['session_strict' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -568,6 +568,9 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
'assert expected 12'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$this->assertTrue(true, 'Default fallback as true');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use CoreLibs\Logging\Logger\Level;
|
||||
/**
|
||||
* Test class for Logging
|
||||
* @coversDefaultClass \CoreLibs\Logging\ErrorMessages
|
||||
* @testdox \CoreLibs\Logging\ErrorMEssages method tests
|
||||
* @testdox \CoreLibs\Logging\ErrorMessages method tests
|
||||
*/
|
||||
final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
{
|
||||
|
||||
@@ -56,7 +56,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$decrypted,
|
||||
'Class call',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt indirect $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessIndirect(string $input, string $expected): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
// test indirect
|
||||
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
|
||||
$decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted);
|
||||
@@ -65,7 +82,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$decrypted,
|
||||
'Class Instance call',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt static $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessStatic(string $input, string $expected): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
// test static
|
||||
$encrypted = SymmetricEncryption::encryptKey($input, $key);
|
||||
$decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
@@ -114,13 +148,51 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$crypt = new SymmetricEncryption($key);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKey($key);
|
||||
$crypt->setKey($wrong_key);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt indirect with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailedIndirect(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$wrong_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class instance
|
||||
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt static with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailedStatic(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$wrong_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class static
|
||||
$encrypted = SymmetricEncryption::encryptKey($input, $key);
|
||||
@@ -190,6 +262,56 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKey
|
||||
* @testdox wrong key indirect $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyIndirect(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->encrypt('test');
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKey
|
||||
* @testdox wrong key static $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyStatic(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::encryptKey('test', $key);
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = SymmetricEncryption::encryptKey('test', $enc_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -232,6 +354,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($input, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext indirect $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertextIndirect(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->decrypt($input);
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($input, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext static $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertextStatic(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($input, $key);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
-- 20241203: update edit tables
|
||||
ALTER TABLE edit_generic ADD cuuid UUID DEFAULT gen_random_uuid();
|
||||
ALTER TABLE edit_log ADD ecuid VARCHAR;
|
||||
ALTER TABLE edit_log ADD ecuuid VARCHAR;
|
||||
ALTER TABLE edit_log ADD eucuid VARCHAR;
|
||||
ALTER TABLE edit_log ADD eucuuid VARCHAR;
|
||||
ALTER TABLE edit_log ADD action_sub_id VARCHAR;
|
||||
ALTER TABLE edit_log ADD http_data JSONB;
|
||||
ALTER TABLE edit_log ADD ip_address JSONB;
|
||||
ALTER TABLE edit_log ADD action_data JSONB;
|
||||
ALTER TABLE edit_log ADD request_scheme VARCHAR;
|
||||
ALTER TABLE edit_user ADD force_logout INT DEFAULT 0;
|
||||
COMMENT ON COLUMN edit_user.force_logout IS 'Counter for forced log out, if this one is higher than the session set one the session gets terminated';
|
||||
ALTER TABLE edit_user ADD last_login TIMESTAMP WITHOUT TIME ZONE;
|
||||
COMMENT ON COLUMN edit_user.last_login IS 'Last succesfull login tiemstamp';
|
||||
|
||||
-- update set_edit_gneric
|
||||
-- adds the created or updated date tags
|
||||
@@ -18,13 +22,17 @@ DECLARE
|
||||
random_length INT = 25; -- that should be long enough
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
NEW.date_created := 'now';
|
||||
NEW.date_created := clock_timestamp();
|
||||
NEW.cuid := random_string(random_length);
|
||||
NEW.cuuid := gen_random_uuid();
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
NEW.date_updated := 'now';
|
||||
NEW.date_updated := clock_timestamp();
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql';
|
||||
|
||||
--
|
||||
ALTER TABLE edit_log RENAME ecuid TO eucuid;
|
||||
ALTER TABLE edit_log RENAME ecuuid TO eucuuid;
|
||||
|
||||
@@ -5,4 +5,9 @@
|
||||
convertDeprecationsToExceptions="true"
|
||||
bootstrap="4dev/tests/bootstrap.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="deploy">
|
||||
<directory>4dev/tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
|
||||
@@ -18,7 +18,7 @@ require 'config.php';
|
||||
$LOG_FILE_ID = 'classTest-convert-colors';
|
||||
ob_end_flush();
|
||||
|
||||
use CoreLibs\Convert\Colors;
|
||||
// use CoreLibs\Convert\Colors;
|
||||
use CoreLibs\Convert\Color\Color;
|
||||
use CoreLibs\Convert\Color\Coordinates;
|
||||
use CoreLibs\Debug\Support as DgS;
|
||||
@@ -29,7 +29,6 @@ $log = new CoreLibs\Logging\Logging([
|
||||
'log_file_id' => $LOG_FILE_ID,
|
||||
'log_per_date' => true,
|
||||
]);
|
||||
$color_class = 'CoreLibs\Convert\Colors';
|
||||
|
||||
/**
|
||||
* print out a color block with info
|
||||
@@ -131,7 +130,8 @@ try {
|
||||
} catch (\LengthException $e) {
|
||||
print "*Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
|
||||
}
|
||||
print "<hr>";
|
||||
|
||||
/* print "<hr>";
|
||||
print "<h2>LEGACY</h2>";
|
||||
// B(valid)
|
||||
$rgb = [50, 20, 30];
|
||||
@@ -173,7 +173,7 @@ $hsb = [0, 0, 5];
|
||||
print "S::COLOR hsb->rgb: $hsb[0], $hsb[1], $hsb[2]: "
|
||||
. DgS::printAr(SetVarType::setArray(
|
||||
Colors::hsb2rgb($hsb[0], $hsb[1], $hsb[2])
|
||||
)) . "<br>";
|
||||
)) . "<br>"; */
|
||||
|
||||
print "<hr>";
|
||||
|
||||
|
||||
@@ -74,8 +74,8 @@ print "EL_O: <pre>" . print_r($el_o, true) . "</pre>";
|
||||
|
||||
echo "<hr>";
|
||||
print "buildHtml(): <pre>" . htmlentities($el_o->buildHtml()) . "</pre>";
|
||||
echo "<hr>";
|
||||
print "phfo(\$el_o): <pre>" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "</pre>";
|
||||
/* echo "<hr>";
|
||||
print "phfo(\$el_o): <pre>" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "</pre>"; */
|
||||
echo "<hr>";
|
||||
print "phfa(\$el_list): <pre>" . htmlentities($el_o::buildHtmlFromList($el_o_list, true)) . "</pre>";
|
||||
|
||||
|
||||
@@ -34,22 +34,21 @@ use CoreLibs\Debug\Support;
|
||||
echo "<br><b>LIST LOCALES</b><br>";
|
||||
|
||||
$locale = 'en_US.UTF-8';
|
||||
$locales = L10n::listLocales($locale);
|
||||
$locales = Language\L10n::listLocales($locale);
|
||||
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
|
||||
$locale = 'en.UTF-8';
|
||||
$locales = L10n::listLocales($locale);
|
||||
$locales = Language\L10n::listLocales($locale);
|
||||
print "[" . $locale . "] LOCALES: " . Support::printAr($locales) . "<br>";
|
||||
|
||||
echo "<br><b>PARSE LOCAL</b><br>";
|
||||
$locale = 'en_US.UTF-8';
|
||||
$locale_info = L10n::parseLocale($locale);
|
||||
$locale_info = Language\L10n::parseLocale($locale);
|
||||
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
|
||||
$locale = 'en.UTF-8';
|
||||
$locale_info = L10n::parseLocale($locale);
|
||||
$locale_info = Language\L10n::parseLocale($locale);
|
||||
print "[" . $locale . "] INFO: " . Support::printAr($locale_info) . "<br>";
|
||||
|
||||
echo "<br><b>AUTO DETECT</b><br>";
|
||||
|
||||
/* echo "<br><b>AUTO DETECT</b><br>";
|
||||
// DEPRECATED
|
||||
// $get_locale = Language\GetLocale::setLocale();
|
||||
// print "[AUTO, DEPRECATED]: " . Support::printAr($get_locale) . "<br>";
|
||||
@@ -103,6 +102,7 @@ $get_locale = Language\GetLocale::setLocaleFromSession(
|
||||
BASE . INCLUDES . LOCALE
|
||||
);
|
||||
print "[SESSION SET INVALID]: " . Support::printAr($get_locale) . "<br>";
|
||||
*/
|
||||
|
||||
// try to load non existing
|
||||
echo "<br><b>NEW TYPE</b><br>";
|
||||
|
||||
@@ -21,7 +21,10 @@ $SET_SESSION_NAME = EDIT_SESSION_NAME;
|
||||
use CoreLibs\Debug\Support;
|
||||
|
||||
// init login & backend class
|
||||
$session = new CoreLibs\Create\Session($SET_SESSION_NAME);
|
||||
$session = new CoreLibs\Create\Session($SET_SESSION_NAME, [
|
||||
'regenerate' => 'interval',
|
||||
'regenerate_interval' => 10, // every 10 seconds
|
||||
]);
|
||||
$log = new CoreLibs\Logging\Logging([
|
||||
'log_folder' => BASE . LOG,
|
||||
'log_file_id' => $LOG_FILE_ID,
|
||||
@@ -90,6 +93,8 @@ print <<<HTML
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
echo "SESSION ID: " . $session->getSessionIdCall() . "<br>";
|
||||
|
||||
echo "CHECK PERMISSION: " . ($login->loginCheckPermissions() ? 'OK' : 'BAD') . "<br>";
|
||||
echo "IS ADMIN: " . ($login->loginIsAdmin() ? 'OK' : 'BAD') . "<br>";
|
||||
echo "MIN ACCESS BASE: " . ($login->loginCheckAccessBase('admin') ? 'OK' : 'BAD') . "<br>";
|
||||
@@ -118,8 +123,7 @@ if (isset($login->loginGetAcl()['unit'])) {
|
||||
print "Something went wrong with the login<br>";
|
||||
}
|
||||
|
||||
echo "<hr>";
|
||||
|
||||
// echo "<hr>";
|
||||
// IP check: 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'CLIENT_IP' in _SERVER
|
||||
// Agent check: 'HTTP_USER_AGENT'
|
||||
|
||||
|
||||
@@ -34,10 +34,12 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
||||
|
||||
print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
|
||||
|
||||
// This is now in \gullevek\dotenv\DotEnv::readEnvFile(...)
|
||||
|
||||
// test .env in local
|
||||
$status = \CoreLibs\Get\DotEnv::readEnvFile('.', 'test.env');
|
||||
/* $status = \CoreLibs\Get\DotEnv::readEnvFile('.', 'test.env');
|
||||
print "test.env: STATUS: " . $status . "<br>";
|
||||
print "AFTER reading test.env file: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
|
||||
print "AFTER reading test.env file: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>"; */
|
||||
|
||||
print "</body></html>";
|
||||
// ;;
|
||||
|
||||
@@ -146,7 +146,7 @@ $_SESSION['this_will_be_written'] = 'not empty';
|
||||
// open again with same name
|
||||
$session_name = 'class-test-session';
|
||||
try {
|
||||
$session_alt = new Session($session_name, auto_write_close:true);
|
||||
$session_alt = new Session($session_name, ['auto_write_close' => true]);
|
||||
print "[4 SET] Current session id: " . $session_alt->getSessionId() . "<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>";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
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
|
||||
INSERT INTO {DB_SCHEMA}.edit_log (
|
||||
username, euid, ecuid, ecuuid, event_date, event, error, data, data_binary, page,
|
||||
username, euid, eucuid, eucuuid, event_date, event, error, data, data_binary, page,
|
||||
ip, user_agent, referer, script_name, query_string, server_name, http_host,
|
||||
http_accept, http_accept_charset, http_accept_encoding, session_id,
|
||||
action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded,
|
||||
|
||||
@@ -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]
|
||||
* if key list is empty, return array as is
|
||||
*
|
||||
|
||||
@@ -21,21 +21,107 @@ class Session
|
||||
private string $session_id = '';
|
||||
/** @var bool flag auto write close */
|
||||
private bool $auto_write_close = false;
|
||||
/** @var string regenerate option, default never */
|
||||
private string $regenerate = 'never';
|
||||
/** @var int regenerate interval either 1 to 100 for random or 0 to 3600 for interval */
|
||||
private int $regenerate_interval = 0;
|
||||
|
||||
/** @var array<string> allowed session id regenerate (rotate) options */
|
||||
private const ALLOWED_REGENERATE_OPTIONS = ['none', 'random', 'interval'];
|
||||
/** @var int default random interval */
|
||||
public const DEFAULT_REGENERATE_RANDOM = 100;
|
||||
/** @var int default rotate internval in minutes */
|
||||
public const DEFAULT_REGENERATE_INTERVAL = 5 * 60;
|
||||
/** @var int maximum time for regenerate interval is one hour */
|
||||
public const MAX_REGENERATE_INTERAL = 60 * 60;
|
||||
|
||||
/**
|
||||
* init a session, if array is empty or array does not have session_name set
|
||||
* then no auto init is run
|
||||
*
|
||||
* @param string $session_name if set and not empty, will start session
|
||||
* @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options
|
||||
*/
|
||||
public function __construct(string $session_name, bool $auto_write_close = false)
|
||||
{
|
||||
public function __construct(
|
||||
string $session_name,
|
||||
array $options = []
|
||||
) {
|
||||
$this->setOptions($options);
|
||||
$this->initSession($session_name);
|
||||
$this->auto_write_close = $auto_write_close;
|
||||
}
|
||||
|
||||
// MARK: private methods
|
||||
|
||||
/**
|
||||
* set session class options
|
||||
*
|
||||
* @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options
|
||||
* @return void
|
||||
*/
|
||||
private function setOptions(array $options): void
|
||||
{
|
||||
if (
|
||||
!isset($options['auto_write_close']) ||
|
||||
!is_bool($options['auto_write_close'])
|
||||
) {
|
||||
$options['auto_write_close'] = false;
|
||||
}
|
||||
$this->auto_write_close = $options['auto_write_close'];
|
||||
if (
|
||||
!isset($options['session_strict']) ||
|
||||
!is_bool($options['session_strict'])
|
||||
) {
|
||||
$options['session_strict'] = true;
|
||||
}
|
||||
// set strict options, on not started sessiononly
|
||||
if (
|
||||
$options['session_strict'] &&
|
||||
$this->getSessionStatus() === PHP_SESSION_NONE
|
||||
) {
|
||||
// use cookies to store session IDs
|
||||
ini_set('session.use_cookies', 1);
|
||||
// use cookies only (do not send session IDs in URLs)
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
// do not send session IDs in URLs
|
||||
ini_set('session.use_trans_sid', 0);
|
||||
}
|
||||
// session regenerate id options
|
||||
if (
|
||||
empty($options['regenerate']) ||
|
||||
!in_array($options['regenerate'], self::ALLOWED_REGENERATE_OPTIONS)
|
||||
) {
|
||||
$options['regenerate'] = 'never';
|
||||
}
|
||||
$this->regenerate = (string)$options['regenerate'];
|
||||
// for regenerate: 'random' (default 100)
|
||||
// regenerate_interval must be between (1 = always) and 100 (1 in 100)
|
||||
// for regenerate: 'interval' (default 5min)
|
||||
// regenerate_interval must be 0 = always, to 3600 (every hour)
|
||||
if (
|
||||
$options['regenerate'] == 'random' &&
|
||||
(
|
||||
!isset($options['regenerate_interval']) ||
|
||||
!is_numeric($options['regenerate_interval']) ||
|
||||
$options['regenerate_interval'] < 0 ||
|
||||
$options['regenerate_interval'] > 100
|
||||
)
|
||||
) {
|
||||
$options['regenerate_interval'] = self::DEFAULT_REGENERATE_RANDOM;
|
||||
}
|
||||
if (
|
||||
$options['regenerate'] == 'interval' &&
|
||||
(
|
||||
!isset($options['regenerate_interval']) ||
|
||||
!is_numeric($options['regenerate_interval']) ||
|
||||
$options['regenerate_interval'] < 1 ||
|
||||
$options['regenerate_interval'] > self::MAX_REGENERATE_INTERAL
|
||||
)
|
||||
) {
|
||||
$options['regenerate_interval'] = self::DEFAULT_REGENERATE_INTERVAL;
|
||||
}
|
||||
$this->regenerate_interval = (int)($options['regenerate_interval'] ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start session
|
||||
* startSession should be called for complete check
|
||||
@@ -72,6 +158,72 @@ class Session
|
||||
return false;
|
||||
}
|
||||
|
||||
// MARK: regenerate session
|
||||
|
||||
/**
|
||||
* auto rotate session id
|
||||
*
|
||||
* @return void
|
||||
* @throws \RuntimeException failure to regenerate session id
|
||||
* @throws \UnexpectedValueException failed to get new session id
|
||||
* @throws \RuntimeException failed to set new sesson id
|
||||
* @throws \UnexpectedValueException new session id generated does not match the new set one
|
||||
*/
|
||||
private function sessionRegenerateSessionId()
|
||||
{
|
||||
// never
|
||||
if ($this->regenerate == 'never') {
|
||||
return;
|
||||
}
|
||||
// regenerate
|
||||
if (
|
||||
!(
|
||||
// is not session obsolete
|
||||
empty($_SESSION['SESSION_REGENERATE_OBSOLETE']) &&
|
||||
(
|
||||
(
|
||||
// random
|
||||
$this->regenerate == 'random' &&
|
||||
mt_rand(1, $this->regenerate_interval) == 1
|
||||
) || (
|
||||
// interval type
|
||||
$this->regenerate == 'interval' &&
|
||||
($_SESSION['SESSION_REGENERATE_TIMESTAMP'] ?? 0) + $this->regenerate_interval < time()
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Set current session to expire in 1 minute
|
||||
$_SESSION['SESSION_REGENERATE_OBSOLETE'] = true;
|
||||
$_SESSION['SESSION_REGENERATE_EXPIRES'] = time() + 60;
|
||||
$_SESSION['SESSION_REGENERATE_TIMESTAMP'] = time();
|
||||
// Create new session without destroying the old one
|
||||
if (session_regenerate_id(false) === false) {
|
||||
throw new \RuntimeException('[SESSION] Session id regeneration failed', 1);
|
||||
}
|
||||
// Grab current session ID and close both sessions to allow other scripts to use them
|
||||
if (false === ($new_session_id = $this->getSessionIdCall())) {
|
||||
throw new \UnexpectedValueException('[SESSION] getSessionIdCall did not return a session id', 2);
|
||||
}
|
||||
$this->writeClose();
|
||||
// Set session ID to the new one, and start it back up again
|
||||
if (($get_new_session_id = session_id($new_session_id)) === false) {
|
||||
throw new \RuntimeException('[SESSION] set session_id failed', 3);
|
||||
}
|
||||
if ($get_new_session_id != $new_session_id) {
|
||||
throw new \UnexpectedValueException('[SESSION] new session id does not match the new set one', 4);
|
||||
}
|
||||
$this->session_id = $new_session_id;
|
||||
$this->startSessionCall();
|
||||
// Don't want this one to expire
|
||||
unset($_SESSION['SESSION_REGENERATE_OBSOLETE']);
|
||||
unset($_SESSION['SESSION_REGENERATE_EXPIRES']);
|
||||
}
|
||||
|
||||
// MARK: session validation
|
||||
|
||||
/**
|
||||
* check if session name is valid
|
||||
*
|
||||
@@ -151,6 +303,13 @@ class Session
|
||||
if (!$this->checkActiveSession()) {
|
||||
throw new \RuntimeException('[SESSION] Failed to activate session', 5);
|
||||
}
|
||||
if (
|
||||
!empty($_SESSION['SESSION_REGENERATE_OBSOLETE']) &&
|
||||
!empty($_SESSION['SESSION_REGENERATE_EXPIRES']) && $_SESSION['SESSION_REGENERATE_EXPIRES'] < time()
|
||||
) {
|
||||
$this->sessionDestroy();
|
||||
throw new \RuntimeException('[SESSION] Expired session found', 6);
|
||||
}
|
||||
} elseif ($session_name != $this->getSessionName()) {
|
||||
throw new \UnexpectedValueException(
|
||||
'[SESSION] Another session exists with a different name: ' . $this->getSessionName(),
|
||||
@@ -159,10 +318,12 @@ class Session
|
||||
}
|
||||
// check session id
|
||||
if (false === ($session_id = $this->getSessionIdCall())) {
|
||||
throw new \UnexpectedValueException('[SESSION] getSessionId did not return a session id', 6);
|
||||
throw new \UnexpectedValueException('[SESSION] getSessionIdCall did not return a session id', 7);
|
||||
}
|
||||
// set session id
|
||||
$this->session_id = $session_id;
|
||||
// run session id re-create from time to time
|
||||
$this->sessionRegenerateSessionId();
|
||||
// if flagged auto close, write close session
|
||||
if ($this->auto_write_close) {
|
||||
$this->writeClose();
|
||||
|
||||
@@ -49,7 +49,11 @@ class SymmetricEncryption
|
||||
*/
|
||||
public static function getInstance(string|null $key = null): self
|
||||
{
|
||||
if (empty(self::$instance)) {
|
||||
// new if no instsance or key is different
|
||||
if (
|
||||
empty(self::$instance) ||
|
||||
self::$instance->key != $key
|
||||
) {
|
||||
self::$instance = new self($key);
|
||||
}
|
||||
return self::$instance;
|
||||
@@ -130,7 +134,7 @@ class SymmetricEncryption
|
||||
*/
|
||||
private function encryptData(string $message, ?string $key): string
|
||||
{
|
||||
if (empty($this->key) || $key === null) {
|
||||
if ($key === null) {
|
||||
throw new \UnexpectedValueException('Key not set');
|
||||
}
|
||||
$key = $this->createKey($key);
|
||||
|
||||
Reference in New Issue
Block a user