Compare commits

..

10 Commits

Author SHA1 Message Date
Clemens Schwaighofer
7b5bddb529 Create\Email fixes
Remove not needed reaplce content count for first global replace
Move logger out of test only and log if a logger class is attached
2022-07-01 08:57:14 +09:00
Clemens Schwaighofer
0a6fdf1248 Use logged encoding from JSON debug block for non UTF-8 tests 2022-06-30 18:18:17 +09:00
Clemens Schwaighofer
3220180d58 Bug fix in Email for encoding subject/body with empty replace
Also store encoding in json log if test and debug print is given
2022-06-30 18:16:28 +09:00
Clemens Schwaighofer
8c8f14ec74 Fix logging per run to be setable not only on start
Move per run set into method.
Add set/get method and add set method override (set new) flag

Update phpUnit testing and move providers to test methods
2022-06-30 18:15:36 +09:00
Clemens Schwaighofer
643991c3fd Update Debug\Support, add Create\Email
update debug support to add html escape for html strings on request.
Default is keep as is. debugString gets new third parameter for this as
bool flag.

Add Create\Email to send basic text emails to several too addresses.
Content replace in subject and body is possible with {} entries.
Default encoding is UTF-8 but others can be set and content will be
converted to this.
The dynamic replace works on all data or can be set per receiver.
2022-06-28 17:29:31 +09:00
Clemens Schwaighofer
c81c46d426 Move read_env_file.php to deprecated folder 2022-06-23 14:46:19 +09:00
Clemens Schwaighofer
d97b173ee7 ACL\Login move public var to private: login
the former public var $login is now private and if it is set can be
checked with loginActionSet (true if login_login was in _POST as login
action.

Some info update for phpUnit ACL\Login test file
2022-06-23 09:12:46 +09:00
Clemens Schwaighofer
b61152f10e Skipped/Incomplete tests update 2022-06-23 07:09:19 +09:00
Clemens Schwaighofer
0c68ebe652 Login\ACL revalidate flow fixes
- DB function had wrong column name
- Queries in ACL\Login had wrong column name
- Renamed from login_user_id_last_login to login_user_id_last_revalidate
  to make it more clear what this column is
- add edit_user admin page output for this column
- add phpUnit test case for revalidate is needed and login with next
  loginUserId is ok again
2022-06-23 06:50:07 +09:00
Clemens Schwaighofer
31d0cdb8ad Fix revalidate after flow in ACL\Login
After revalidate time was reached, it was never reset because it used
the original loginUserId set date.
A new column has been added that gets reset every time the user logs in
with username and password if a loginUserId is set in the database
2022-06-22 19:38:03 +09:00
29 changed files with 1788 additions and 441 deletions

View File

@@ -270,8 +270,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;
@@ -595,12 +597,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login
login_user_id VARCHAR UNIQUE, -- the login uid, at least 32 chars
login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated login id after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for login user id, but still allow normal login
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS;
@@ -629,11 +632,12 @@ COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the
COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'login id was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'login id is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'login id is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for login id, user can still login normal';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';
-- END: table/edit_user.sql
-- START: table/edit_log.sql

View File

@@ -15,8 +15,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;

View File

@@ -55,12 +55,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login
login_user_id VARCHAR UNIQUE, -- the login uid, at least 32 chars
login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated login id after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for login user id, but still allow normal login
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS;
@@ -89,9 +90,10 @@ COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the
COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'login id was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'login id is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'login id is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for login id, user can still login normal';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';

View File

@@ -0,0 +1,81 @@
--
SELECT
eu.cuid, eu.username,
eu.lock_until, eu.lock_after,
CASE WHEN (
(eu.lock_until IS NULL
OR (eu.lock_until IS NOT NULL AND NOW() >= eu.lock_until))
AND (eu.lock_after IS NULL
OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after))
) THEN 0::INT ELSE 1::INT END locked_period
FROM edit_user eu
WHERE eu.username = 'empty';
UPDATE edit_user SET
lock_until = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_after = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_until = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_after = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET lock_until = NULL, lock_after = NULL WHERE username = 'empty';
--
SELECT
eu.cuid, eu.username,
eu.login_user_id, login_user_id_set_date, eu.login_user_id_last_revalidate,
(eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE AS reval_date, NOW()::DATE,
eu.login_user_id_valid_from, eu.login_user_id_valid_until,
eu.login_user_id_revalidate_after,
CASE WHEN (
(eu.login_user_id_valid_from IS NULL
OR (eu.login_user_id_valid_from IS NOT NULL AND NOW() >= eu.login_user_id_valid_from))
AND (eu.login_user_id_valid_until IS NULL
OR (eu.login_user_id_valid_until IS NOT NULL AND NOW() <= eu.login_user_id_valid_until))
) THEN 1::INT ELSE 0::INT END AS login_user_id_valid_date,
CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL
AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL
AND (eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE <= NOW()::DATE
THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate
FROM edit_user eu
WHERE eu.username = 'empty';
-- init
UPDATE edit_user SET login_user_id = random_string(5) WHERE username = 'empty';
-- outside valid
UPDATE edit_user SET
login_user_id_valid_from = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
login_user_id_valid_until = NOW() + '1 day'::interval
WHERE username = 'empty';
-- inside valid
UPDATE edit_user SET
login_user_id_valid_from = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
login_user_id_valid_until = NOW() - '1 day'::interval
WHERE username = 'empty';
-- revalidate must
UPDATE edit_user SET
login_user_id_last_revalidate = NOW() - '1 day'::interval,
login_user_id_revalidate_after = '1 day'::interval
WHERE username = 'empty';
-- revalidate not yet
UPDATE edit_user SET
login_user_id_last_revalidate = NOW(),
login_user_id_revalidate_after = '6 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET login_user_id_set_date = NULL, login_user_id_last_revalidate = NULL, login_user_id_valid_from = NULL, login_user_id_valid_until = NULL, login_user_id_revalidate_after = NULL WHERE username = 'empty';

View File

@@ -6,6 +6,7 @@ ALTER TABLE edit_user ADD login_user_id VARCHAR UNIQUE;
-- ALTER TABLE edit_user ADD CONSTRAINT edit_user_login_user_id_key UNIQUE (login_user_id);
-- when above uid was set
ALTER TABLE edit_user ADD login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE;
ALTER TABLE edit_user ADD login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE;
-- if set, from/until when the above uid is valid
ALTER TABLE edit_user ADD login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE;
ALTER TABLE edit_user ADD login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE;
@@ -33,8 +34,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;

View File

@@ -25,7 +25,7 @@ declare(strict_types=1);
* 1 for file loadable, but no data inside
* 2 for file not readable
* 3 for file not found
* @deprecated V6 Use \CoreLibs\Get\ReadEnvFile::readEnvFile()
* @deprecated V6 Use \CoreLibs\Get\DotEnv::readEnvFile()
*/
function readEnvFile(string $path = __DIR__, string $env_file = '.env'): int
{

View File

@@ -157,17 +157,18 @@ final class CoreLibsACLLoginTest extends TestCase
*/
public function loginProvider(): array
{
// 0: mock settings/override flag settings
// 2: get array IN
// 1: post array IN
// login_login, login_username, login_password, login_logout
// change_password, pw_username, pw_old_password, pw_new_password,
// pw_new_password_confirm
// 2: override session set
// 3: expected error code, 0 for all ok, 3000 for login page view
// note that 1000 (no db), 2000 (no session) must be tested too
// 4: expected return array, eg login_error code, or other info data to match
return [
// 0[mock] : mock settings/override flag settings
// 1[get] : get array IN
// 2[post] : post array IN
// login_login, login_username, login_password, login_logout
// change_password, pw_username, pw_old_password, pw_new_password,
// pw_new_password_confirm
// 3[session]: override session set
// 4[error] : expected error code, 0 for all ok, 3000 for login page view
// note that 1000 (no db), 2000 (no session) must be tested too
// 5[return] : expected return array, eg login_error code,
// or other info data to match
$tests = [
'load, no login' => [
// error code, only for exceptions
[
@@ -290,7 +291,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
],
// login: all missing
'login: all missing' => [
'login: failed: all missing' => [
[
'page_name' => 'edit_users.php',
],
@@ -311,7 +312,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: missing username
'login: missing username' => [
'login: failed: missing username' => [
[
'page_name' => 'edit_users.php',
],
@@ -332,7 +333,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: missing password
'login: missing password' => [
'login: failed: missing password' => [
[
'page_name' => 'edit_users.php',
],
@@ -353,7 +354,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: user not found
'login: user not found' => [
'login: failed: user not found' => [
[
'page_name' => 'edit_users.php',
],
@@ -377,7 +378,7 @@ final class CoreLibsACLLoginTest extends TestCase
// 9999: not valid password encoding
// 1013: normal password failed
// 1012: plain password check failed
'login: invalid password' => [
'login: failed: invalid password' => [
[
'page_name' => 'edit_users.php',
],
@@ -399,7 +400,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: ok (but deleted)
'login: ok, but deleted' => [
'login: ok -> failed: but deleted' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -424,7 +425,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: ok (but not enabled)
'login: ok, but not enabled' => [
'login: ok -> failed: but not enabled' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -449,7 +450,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: ok (but locked)
'login: ok, but locked' => [
'login: ok -> failed: but locked' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -474,7 +475,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: make user get locked strict
'login: ok, get locked, strict' => [
'login: ok -> failed: get locked, strict' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -498,7 +499,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but in locked period (until)
'login: ok, but locked period (until:on)' => [
'login: ok -> failed: but locked period (until:on)' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -552,7 +553,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but in locked period (after)
'login: ok, but locked period (after:on)' => [
'login: ok -> failed: but locked period (after:on)' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -577,7 +578,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but in locked period (until, after)
'login: ok, but locked period (until:on, after:on)' => [
'login: ok -> failed:, but locked period (until:on, after:on)' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -603,7 +604,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but login user id locked
'login: ok, but login user id locked' => [
'login: ok -> failed:, but loginUserId locked' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -830,7 +831,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with revalidate on/off
'login: ok, but revalidate trigger, _GET loginUserId' => [
'login: ok -> failed:, but revalidate trigger, _GET loginUserId' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -886,7 +887,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with active time from only
'login: ok, _GET loginUserId, but outside valid (from:on) ' => [
'login: ok -> failed:, _GET loginUserId, but outside valid (from:on) ' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -942,7 +943,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with active time until only
'login: ok, _GET loginUserId, but outside valid (until:on) ' => [
'login: ok -> failed:, _GET loginUserId, but outside valid (until:on) ' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -968,7 +969,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with active time from/until
'login: ok, _GET loginUserId, but outside valid (from:on,until:on) ' => [
'login: ok -> failed:, _GET loginUserId, but outside valid (from:on,until:on) ' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -994,11 +995,53 @@ final class CoreLibsACLLoginTest extends TestCase
. 'Login Failed - Login User ID is outside valid date range'
]
],
// TODO: Test that if we have n day check with login, that after login we can use parameter login again
'login: ok -> failed -> ok:, _GET loginUserId, but must revalidate, normal login, _GET loginUserId' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test',
'base_access' => 'list',
'page_access' => 'list',
'test_login_user_id_revalidate_reset' => true,
'test_login_user_id' => true,
'test_username' => 'admin',
'loginUserId' => '1234567890ABCDEFG',
// this error is thrown on first login round
'login_error' => 1101,
// get post as set sub arrays
'get' => [
'loginUserId' => '1234567890ABCDEFG',
],
'post' => [
'login_login' => 'Login',
'login_username' => 'admin',
'login_password' => 'admin',
],
],
// all empty get, post, session
[],
[],
[],
0,
[
'login_error' => 0,
'admin_flag' => true,
'check_access' => true,
'check_access_id' => 1,
'check_access_data' => 'value',
'base_access' => true,
'page_access' => true,
]
]
//
// other:
// login check edit access id of ID not null and not in array
// login OK, but during action user gets disabled/deleted/etc
];
return $tests;
}
/**
@@ -1196,7 +1239,6 @@ final class CoreLibsACLLoginTest extends TestCase
if (!empty($mock_settings['test_login_user_id'])) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_set_date = NOW(), "
. "login_user_id = "
. self::$db->dbEscapeLiteral($mock_settings['loginUserId'])
. " "
@@ -1207,10 +1249,10 @@ final class CoreLibsACLLoginTest extends TestCase
if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
$q_sub = '';
if ($mock_settings['test_login_user_id_revalidate_after'] == 'on') {
$q_sub = "login_user_id_set_date = NOW() - '1 day'::interval, "
$q_sub = "login_user_id_last_revalidate = NOW() - '1 day'::interval, "
. "login_user_id_revalidate_after = '1 day'::interval ";
} else {
$q_sub = "login_user_id_set_date = NOW(), "
$q_sub = "login_user_id_last_revalidate = NOW(), "
. "login_user_id_revalidate_after = '6 day'::interval ";
}
self::$db->dbExec(
@@ -1220,6 +1262,50 @@ final class CoreLibsACLLoginTest extends TestCase
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
if (!empty($mock_settings['test_login_user_id_revalidate_reset'])) {
// init dates data for revalidate frame,
// set to last revalidate 3 days ago and set revalidate frame to
// three days
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_last_revalidate = NOW() - '3 day'::interval, "
. "login_user_id_revalidate_after = '3 day'::interval "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
$_GET = $mock_settings['get'];
// login with loginUserId -> fail
try {
$login_mock->loginMainCall();
} catch (\Exception $e) {
$this->assertEquals(
$mock_settings['login_error'],
$login_mock->loginGetLastErrorCode(),
'loginUserId reset 1: Assert first loginUserId run failes'
);
}
$_GET = [];
// login with username and password -> reset -> ok
// set _POST data
$_POST = $mock_settings['post'];
try {
$login_mock->loginMainCall();
$this->assertEquals(
0,
$login_mock->loginGetLastErrorCode(),
'loginUserId reset 2: Assert username/password login is successful'
);
} catch (\Exception $e) {
// if we end up here we have an issue
$this->assertTrue(
false,
'loginUserId reset 2: FAILED successful login'
);
}
$_POST = [];
// logut and run normal login with loginUserId
$_GET = $mock_settings['get'];
}
if (
!empty($mock_settings['test_login_user_id_valid_from']) ||
!empty($mock_settings['test_login_user_id_valid_until'])
@@ -1461,6 +1547,14 @@ final class CoreLibsACLLoginTest extends TestCase
);
}
// if _POST login set check this is matching
if (!empty($post['login_login'])) {
$this->assertTrue(
$login_mock->loginActionRun(),
'Assert that post login_login was pressed'
);
}
// always check, even on error or not set
if (!$login_mock->loginGetLoginUserIdUnclean()) {
$this->assertEquals(
@@ -1540,36 +1634,38 @@ final class CoreLibsACLLoginTest extends TestCase
. self::$db->dbEscapeLiteral($post['login_username'])
);
}
// if (!empty($mock_settings['test_login_user_id'])) {
// self::$db->dbExec(
// "UPDATE edit_user SET "
// . "login_user_id = NULL, "
// . "login_user_id_set_date = NULL "
// . "WHERE LOWER(username) = "
// . self::$db->dbEscapeLiteral($mock_settings['test_username'])
// );
// }
// if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
// self::$db->dbExec(
// "UPDATE edit_user SET "
// . "login_user_id_set_date = NULL, "
// . "login_user_id_revalidate_after = NULL "
// . "WHERE LOWER(username) = "
// . self::$db->dbEscapeLiteral($mock_settings['test_username'])
// );
// }
// if (
// !empty($mock_settings['test_login_user_id_valid_from']) ||
// !empty($mock_settings['test_login_user_id_valid_until'])
// ) {
// self::$db->dbExec(
// "UPDATE edit_user SET "
// . "login_user_id_valid_from = NULL, "
// . "login_user_id_valid_until = NULL "
// . "WHERE LOWER(username) = "
// . self::$db->dbEscapeLiteral($mock_settings['test_username'])
// );
// }
if (!empty($mock_settings['test_login_user_id'])) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id = NULL, "
// below to rows are automatcially reset
. "login_user_id_set_date = NULL, "
. "login_user_id_last_revalidate = NULL "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_last_revalidate = NULL, "
. "login_user_id_revalidate_after = NULL "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
if (
!empty($mock_settings['test_login_user_id_valid_from']) ||
!empty($mock_settings['test_login_user_id_valid_until'])
) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_valid_from = NULL, "
. "login_user_id_valid_until = NULL "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
}
// - loginGetAclList (null, invalid,)

View File

@@ -0,0 +1,577 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for Create\Email
* @coversDefaultClass \CoreLibs\Create\Email
* @testdox \CoreLibs\Create\Email method tests
*/
final class CoreLibsCreateEmailTest extends TestCase
{
private static $log;
/**
* start DB conneciton, setup DB, etc
*
* @return void
*/
public static function setUpBeforeClass(): void
{
self::$log = new \CoreLibs\Debug\Logging([
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-Create-Email-Test',
'debug_all' => true,
'echo_all' => false,
'print_all' => true,
]);
}
/**
* Undocumented function
*
* @return array
*/
public function encodeEmailNameProvider(): array
{
// 0: email
// 1: name
// 2: encoding
// 3: expected
return [
'all empty' => [
'',
null,
null,
''
],
'email only' => [
'test@test.com',
null,
null,
'test@test.com'
],
'email and name' => [
'test@test.com',
'Test Name',
null,
'"Test Name" <test@test.com>'
],
'name in mime encoded, default UTF-8' => [
'test@test.com',
'日本語',
null,
'"=?UTF-8?B?5pel5pys6Kqe?=" <test@test.com>'
],
'name in mime encoded, UTF-8 parameter' => [
'test@test.com',
'日本語',
'UTF-8',
'"=?UTF-8?B?5pel5pys6Kqe?=" <test@test.com>'
],
// does internal UTF-8 to ISO-2022-JP convert
'encoding in ISO-2022-JP' => [
'test@test.com',
'日本語',
'ISO-2022-JP',
'"=?ISO-2022-JP?B?GyRCRnxLXA==?=" <test@test.com>'
]
];
}
/**
* Undocumented function
*
* @dataProvider encodeEmailNameProvider
* @testdox encode email $email, name $name, encoding $encoding will be $expected [$_dataName]
*
* @return void
*/
public function testEncodeEmailName(
string $email,
?string $name,
?string $encoding,
string $expected
): void {
if ($name === null && $encoding === null) {
$encoded_email = \CoreLibs\Create\Email::encodeEmailName($email);
} elseif ($encoding === null) {
$encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name);
} else {
$encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name, $encoding);
}
$this->assertEquals(
$expected,
$encoded_email
);
}
public function sendEmailProvider(): array
{
// 0: subject
// 1: body
// 2: from email
// 3: from name ('')
// 4: array for to email
// 5: replace content ([]/null)
// 6: encoding (UTF-8/null)
// 8: return status
// 9: expected content
return [
'all empty, fail -1' => [
'subject' => '',
'body' => '',
'from_email' => '',
'from_name' => '',
'to_email' => [],
'replace' => null,
'encoding' => null,
'expected_status' => -1,
'expected_content' => [],
],
'missing to entry, fail -2' => [
'subject' => 'SUBJECT',
'body' => 'BODY',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [],
'replace' => null,
'encoding' => null,
'expected_status' => -2,
'expected_content' => [],
],
'sending email 1' => [
'subject' => 'SUBJECT',
'body' => 'BODY',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT',
'body' => 'BODY',
]
],
],
'sending email 1, encoded' => [
'subject' => 'SUBJECT 日本語',
'body' => 'BODY 日本語',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6Kqe?=',
'body' => 'BODY 日本語',
]
],
],
'sending email 1, encoded subject ISO-2022-JP' => [
'subject' => 'SUBJECT 日本語',
'body' => 'BODY 日本語',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => 'ISO-2022-JP',
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?ISO-2022-JP?B?GyRCRnxLXDhsGyhC?=',
// body is stored as UTF-8 in log and here, so both must be translated
'body' => 'BODY 日本語',
]
],
],
'sending email 2' => [
'subject' => 'SUBJECT',
'body' => 'BODY',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'e1@test.com',
'e2@test.com'
],
'replace' => null,
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'e1@test.com',
'subject' => 'SUBJECT',
'body' => 'BODY',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'e2@test.com',
'subject' => 'SUBJECT',
'body' => 'BODY',
]
],
],
'sending email 1: dynamic' => [
'subject' => 'SUBJECT {FOO}',
'body' => 'BODY {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT foo',
'body' => 'BODY foo bar',
]
],
],
'sending email 1: dynamic encoded' => [
'subject' => 'SUBJECT 日本語 {FOO}',
'body' => 'BODY 日本語 {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbw==?=',
'body' => 'BODY 日本語 foo bar',
]
],
],
'sending email 1: dynamic, to override' => [
'subject' => 'SUBJECT {FOO}',
'body' => 'BODY {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 'test@test.com',
'replace' => [
'FOO' => 'foo to'
]
]
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT foo to',
'body' => 'BODY foo to bar',
]
],
],
'sending email 1: dynamic, to override encoded' => [
'subject' => 'SUBJECT 日本語 {FOO}',
'body' => 'BODY 日本語 {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 'test@test.com',
'replace' => [
'FOO' => 'foo to'
]
]
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbyB0bw==?=',
'body' => 'BODY 日本語 foo to bar',
]
],
],
'sending email 3: dynamic, to mixed override' => [
'subject' => 'SUBJECT {FOO}',
'body' => 'BODY {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 't1@test.com',
'replace' => [
'FOO' => 'foo to 1'
]
],
[
'email' => 't2@test.com',
'replace' => [
'FOO' => 'foo to 2'
]
],
[
'email' => 't3@test.com',
],
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't1@test.com',
'subject' => 'SUBJECT foo to 1',
'body' => 'BODY foo to 1 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't2@test.com',
'subject' => 'SUBJECT foo to 2',
'body' => 'BODY foo to 2 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't3@test.com',
'subject' => 'SUBJECT foo',
'body' => 'BODY foo bar',
],
],
],
'sending email 3: dynamic, to mixed override encoded' => [
'subject' => 'SUBJECT 日本語 {FOO}',
'body' => 'BODY 日本語 {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 't1@test.com',
'replace' => [
'FOO' => 'foo to 1'
]
],
[
'email' => 't2@test.com',
'replace' => [
'FOO' => 'foo to 2'
]
],
[
'email' => 't3@test.com',
],
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't1@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbyB0byAx?=',
'body' => 'BODY 日本語 foo to 1 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't2@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbyB0byAy?=',
'body' => 'BODY 日本語 foo to 2 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't3@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbw==?=',
'body' => 'BODY 日本語 foo bar',
],
],
],
];
}
/**
* Undocumented function
*
* @dataProvider sendEmailProvider
* @testdox email sending with expected status $expected_status [$_dataName]
*
* @param string $subject
* @param string $body
* @param string $from_email
* @param string $from_name
* @param array $to_email
* @param array|null $replace
* @param string|null $encoding
* @param int $expected_status
* @param array $expected_content
* @return void
*/
public function testSendEmail(
string $subject,
string $body,
string $from_email,
string $from_name,
array $to_email,
?array $replace,
?string $encoding,
int $expected_status,
array $expected_content
): void {
if ($replace === null) {
$replace = [];
}
if ($encoding === null) {
$encoding = 'UTF-8';
}
// force new set for each run
self::$log->setLogUniqueId(true);
// set on of unique log id
self::$log->setLogPer('run', true);
// init logger
$status = \CoreLibs\Create\Email::sendEmail(
$subject,
$body,
$from_email,
$from_name,
$to_email,
$replace,
$encoding,
true,
self::$log
);
$this->assertEquals(
$expected_status,
$status,
'Assert sending status'
);
// assert content: must load JSON from log file
if ($status == 2) {
// open file, get last entry with 'SEND EMAIL JSON' key
$file = file_get_contents(self::$log->getLogFileName());
if ($file !== false) {
// extract SEND EMAIL JSON line
$found = preg_match_all("/^.* <SEND EMAIL JSON> - (.*)$/m", $file, $matches);
// print "Found: $found | EMAIL: " . print_r($matches, true) . "\n";
if (!empty($matches[1])) {
foreach ($matches[1] as $pos => $email_json) {
$email = \CoreLibs\Convert\Json::jsonConvertToArray($email_json);
// print "EMAIL: " . print_r($email, true) . "\n";
$this->assertEquals(
$expected_content[$pos]['header']['From'] ?? 'MISSING FROM',
$email['header']['From'] ?? '',
'Email check: assert header from'
);
$this->assertEquals(
'text/plain; charset=' . $encoding ?? 'UTF-8',
$email['header']['Content-type'] ?? '',
'Email check: assert header content type'
);
$this->assertEquals(
'1.0',
$email['header']['MIME-Version'] ?? '',
'Email check: assert header mime version'
);
$this->assertEquals(
$expected_content[$pos]['to'] ?? 'MISSING TO',
$email['to'] ?? '',
'Email check: assert to'
);
$this->assertEquals(
$expected_content[$pos]['subject'] ?? 'MISSING SUBJECT',
$email['subject'] ?? '',
'Email check: assert subject'
);
// body must be translated back to encoding if encoding is not UTF-8
$this->assertEquals(
$encoding != 'UTF-8' ?
mb_convert_encoding($expected_content[$pos]['body'] ?? '', $encoding, 'UTF-8') :
$expected_content[$pos]['body'] ?? 'MISSING BODY',
$email['encoding'] != 'UTF-8' ?
mb_convert_encoding($email['body'] ?? '', $email['encoding'], 'UTF-8') :
$email['body'] ?? '',
'Email check: assert body'
);
}
}
}
}
}
}
// __END__

View File

@@ -13,7 +13,11 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsCreateHashTest extends TestCase
{
/**
* Undocumented function
*
* @return array
*/
public function hashData(): array
{
return [

View File

@@ -36,9 +36,9 @@ final class CoreLibsDBExtendedArrayIOTest extends TestCase
*
* @return void
*/
public function testDBIO()
public function testArrayDBIO()
{
$this->assertTrue(true, 'DB Extended ArrayIO Tests not implemented');
// $this->assertTrue(true, 'DB Extended ArrayIO Tests not implemented');
$this->markTestIncomplete(
'DB\Extended\ArrayIO Tests have not yet been implemented'
);

View File

@@ -15,8 +15,6 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsDebugLoggingTest extends TestCase
{
public $log;
/**
* test set for options BASIC
*
@@ -85,6 +83,47 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* init logging class
*
* @dataProvider optionsProvider
* @testdox init test [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testClassInit(?array $options, array $expected, array $override): void
{
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if ($options === null) {
$log = new \CoreLibs\Debug\Logging();
} else {
$log = new \CoreLibs\Debug\Logging($options);
}
// check that settings match
$this->assertEquals(
$expected['log_folder'],
$log->getSetting('log_folder')
);
$this->assertEquals(
$expected['debug_all'],
$log->getSetting('debug_output_all')
);
$this->assertEquals(
$expected['print_all'],
$log->getSetting('print_output_all')
);
// print "LOG: " . $log->getSetting('log_folder') . "\n";
// print "DEBUG: " . $log->getSetting('debug_output_all') . "\n";
// print "PRINT: " . $log->getSetting('print_output_all') . "\n";
}
/**
* adds log ID settings based on basic options
*
@@ -173,6 +212,52 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* test the setting and getting of LogId
*
* @covers ::setLogId
* @dataProvider logIdOptionsProvider
* @testdox log id set/get tests [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testLogId(?array $options, array $expected, array $override): void
{
// we need to set with file_id option, globals LOG_FILE_ID, constant LOG_FILE_ID
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if (!empty($override['globals'])) {
foreach ($override['globals'] as $var => $value) {
$GLOBALS[$var] = $value;
}
}
if ($options === null) {
$log = new \CoreLibs\Debug\Logging();
} else {
$log = new \CoreLibs\Debug\Logging($options);
}
// check current
$this->assertEquals(
$log->getLogId(),
$expected['log_file_id']
);
// we need to override now too
if (!empty($override['values'])) {
// check if we have values, set them post and assert
$log->setLogId($override['values']['log_file_id']);
$this->assertEquals(
$log->getLogId(),
$expected['set_log_file_id']
);
}
}
/**
* Undocumented function
*
@@ -180,6 +265,10 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function logLevelAllProvider(): array
{
// 0: type
// 1: flag
// 2: expected set
// 3: expected get
return [
'debug all true' => [
'debug',
@@ -208,6 +297,38 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* check set/get for log level all flag
*
* @dataProvider logLevelAllProvider
* @testdox set/get all log level $type with flag $flag [$_dataName]
*
* @param string $type
* @param bool $flag
* @param bool $expected_set
* @param bool $expected_get
* @return void
*/
public function testSetGetLogLevelAll(
string $type,
bool $flag,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$log->setLogLevelAll($type, $flag),
$expected_set
);
// get and check
$this->assertEquals(
$log->getLogLevelAll($type),
$expected_get
);
}
/**
* Undocumented function
*
@@ -215,6 +336,12 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function logLevelProvider(): array
{
// 0: type
// 1: flag
// 2: debug on (array)
// 3: expected set
// 4: level
// 5: expected get
return [
'set debug on for level A,B,C and check full set' => [
'debug',
@@ -287,6 +414,43 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* checks setting for per log info level
*
* @covers ::setLogLevel
* @dataProvider logLevelProvider
* @testdox set/get log level $type to $flag check with $level [$_dataName]
*
* @param string $type
* @param string $flag
* @param array $debug_on
* @param bool $expected_set
* @param string|null $level
* @param bool|array<mixed> $expected_get
* @return void
*/
public function testSetGetLogLevel(
string $type,
string $flag,
array $debug_on,
bool $expected_set,
?string $level,
$expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set
$this->assertEquals(
$log->setLogLevel($type, $flag, $debug_on),
$expected_set
);
// get, if level is null compare to?
$this->assertEquals(
$log->getLogLevel($type, $flag, $level),
$expected_get
);
}
/**
* Undocumented function
*
@@ -294,6 +458,10 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function logPerProvider(): array
{
// 0: type
// 1: set
// 2: expected set
// 3: expected get
return [
'level set true' => [
'level',
@@ -328,6 +496,68 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* set and get per log
* for level/class/page/run flags
*
* @covers ::setLogPer
* @dataProvider logPerProvider
* @testdox set/get log per $type with $set [$_dataName]
*
* @param string $type
* @param boolean $set
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPer(
string $type,
bool $set,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$log->setLogPer($type, $set),
$expected_set
);
// get and check
$this->assertEquals(
$log->getLogPer($type),
$expected_get
);
}
/**
* set the print log file date part
*
* @covers ::setGetLogPrintFileDate
* @testWith [true, true, true]
* [false, false, false]
* @testdox set/get log file date to $input [$_dataName]
*
* @param boolean $input
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPrintFileDate(bool $input, bool $expected_set, bool $expected_get): void
{
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$log->setGetLogPrintFileDate($input),
$expected_set
);
$this->assertEquals(
$log->setGetLogPrintFileDate(),
$expected_get
);
}
/**
* Undocumented function
*
@@ -369,6 +599,95 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* convert array to string with ## pre replace space holders
*
* @covers ::prAr
* @dataProvider prArProvider
* @testdox check prAr array to string conversion [$_dataName]
*
* @param array $input
* @param string $expected
* @return void
*/
public function testPrAr(array $input, string $expected): void
{
$log = new \CoreLibs\Debug\Logging();
$this->assertEquals(
$log->prAr($input),
$expected
);
}
/**
* Undocumented function
*
* @return array
*/
public function prBlProvider(): array
{
// 0: input flag (bool)
// 1: is true
// 2: is flase
// 3: epxected
return [
'true bool default' => [
true,
null,
null,
'true'
],
'false bool default' => [
false,
null,
null,
'false'
],
'true bool override' => [
true,
'ok',
'not ok',
'ok'
],
'false bool override' => [
false,
'ok',
'not ok',
'not ok'
],
];
}
/**
* check bool to string converter
*
* @covers ::prBl
* @dataProvider prBlProvider
* @testdox check prBl $input ($true/$false) is expected $false [$_dataName]
*
* @param bool $input
* @param string|null $true
* @param string|null $false
* @param string $expected
* @return void
*/
public function testPrBl(bool $input, ?string $true, ?string $false, string $expected): void
{
$log = new \CoreLibs\Debug\Logging();
$return = '';
if ($true === null && $false === null) {
$return = $log->prBl($input);
} elseif ($true !== null || $false !== null) {
$return = $log->prBl($input, $true ?? '', $false ?? '');
}
$this->assertEquals(
$expected,
$return
);
}
// from here are complex debug tests
/**
* Undocumented function
*
@@ -471,304 +790,6 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* init logging class
*
* @dataProvider optionsProvider
* @testdox init test [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testClassInit(?array $options, array $expected, array $override): void
{
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if ($options === null) {
$this->log = new \CoreLibs\Debug\Logging();
} else {
$this->log = new \CoreLibs\Debug\Logging($options);
}
// check that settings match
$this->assertEquals(
$expected['log_folder'],
$this->log->getSetting('log_folder')
);
$this->assertEquals(
$expected['debug_all'],
$this->log->getSetting('debug_output_all')
);
$this->assertEquals(
$expected['print_all'],
$this->log->getSetting('print_output_all')
);
// print "LOG: " . $this->log->getSetting('log_folder') . "\n";
// print "DEBUG: " . $this->log->getSetting('debug_output_all') . "\n";
// print "PRINT: " . $this->log->getSetting('print_output_all') . "\n";
}
/**
* test the setting and getting of LogId
*
* @covers ::setLogId
* @dataProvider logIdOptionsProvider
* @testdox log id set/get tests [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testLogId(?array $options, array $expected, array $override): void
{
// we need to set with file_id option, globals LOG_FILE_ID, constant LOG_FILE_ID
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if (!empty($override['globals'])) {
foreach ($override['globals'] as $var => $value) {
$GLOBALS[$var] = $value;
}
}
if ($options === null) {
$this->log = new \CoreLibs\Debug\Logging();
} else {
$this->log = new \CoreLibs\Debug\Logging($options);
}
// check current
$this->assertEquals(
$this->log->getLogId(),
$expected['log_file_id']
);
// we need to override now too
if (!empty($override['values'])) {
// check if we have values, set them post and assert
$this->log->basicSetLogId($override['values']['log_file_id']);
$this->assertEquals(
$this->log->getLogId(),
$expected['set_log_file_id']
);
}
}
/**
* check set/get for log level all flag
*
* @dataProvider logLevelAllProvider
* @testdox set/get all log level $type with flag $flag [$_dataName]
*
* @param string $type
* @param bool $flag
* @param bool $expected_set
* @param bool $expected_get
* @return void
*/
public function testSetGetLogLevelAll(
string $type,
bool $flag,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$this->log->setLogLevelAll($type, $flag),
$expected_set
);
// get and check
$this->assertEquals(
$this->log->getLogLevelAll($type),
$expected_get
);
}
/**
* checks setting for per log info level
*
* @covers ::setLogLevel
* @dataProvider logLevelProvider
* @testdox set/get log level $type to $flag check with $level [$_dataName]
*
* @param string $type
* @param string $flag
* @param array $debug_on
* @param bool $expected_set
* @param string|null $level
* @param bool|array<mixed> $expected_get
* @return void
*/
public function testSetGetLogLevel(
string $type,
string $flag,
array $debug_on,
bool $expected_set,
?string $level,
$expected_get
): void {
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set
$this->assertEquals(
$this->log->setLogLevel($type, $flag, $debug_on),
$expected_set
);
// get, if level is null compare to?
$this->assertEquals(
$this->log->getLogLevel($type, $flag, $level),
$expected_get
);
}
/**
* set and get per log
* for level/class/page/run flags
*
* @covers ::setLogPer
* @dataProvider logPerProvider
* @testdox set/get log per $type with $set [$_dataName]
*
* @param string $type
* @param boolean $set
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPer(
string $type,
bool $set,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$this->log->setLogPer($type, $set),
$expected_set
);
// get and check
$this->assertEquals(
$this->log->getLogPer($type),
$expected_get
);
}
/**
* set the print log file date part
*
* @covers ::setGetLogPrintFileDate
* @testWith [true, true, true]
* [false, false, false]
* @testdox set/get log file date to $input [$_dataName]
*
* @param boolean $input
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPrintFileDate(bool $input, bool $expected_set, bool $expected_get): void
{
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$this->log->setGetLogPrintFileDate($input),
$expected_set
);
$this->assertEquals(
$this->log->setGetLogPrintFileDate(),
$expected_get
);
}
/**
* convert array to string with ## pre replace space holders
*
* @covers ::prAr
* @dataProvider prArProvider
* @testdox check prAr array to string conversion [$_dataName]
*
* @param array $input
* @param string $expected
* @return void
*/
public function testPrAr(array $input, string $expected): void
{
$this->log = new \CoreLibs\Debug\Logging();
$this->assertEquals(
$this->log->prAr($input),
$expected
);
}
public function prBlProvider(): array
{
return [
'true bool default' => [
true,
null,
null,
'true'
],
'false bool default' => [
false,
null,
null,
'false'
],
'true bool override' => [
true,
'ok',
'not ok',
'ok'
],
'false bool override' => [
false,
'ok',
'not ok',
'not ok'
],
];
}
/**
* check bool to string converter
*
* @covers ::prBl
* @dataProvider prBlProvider
* @textdox check prBl $input ($true/$false) is expected $false [$_dataName]
*
* @param bool $input
* @param string|null $true
* @param string|null $false
* @param string $expected
* @return void
*/
public function testPrBl(bool $input, ?string $true, ?string $false, string $expected): void
{
$this->log = new \CoreLibs\Debug\Logging();
$return = '';
if ($true === null && $false === null) {
$return = $this->log->prBl($input);
} elseif ($true !== null || $false !== null) {
$return = $this->log->prBl($input, $true ?? '', $false ?? '');
}
$this->assertEquals(
$expected,
$return
);
}
// from here are complex debug tests
/**
* Test debug flow
*
@@ -824,11 +845,11 @@ final class CoreLibsDebugLoggingTest extends TestCase
// remove any files named /tmp/error_log_TestDebug*.log
array_map('unlink', glob($options['log_folder'] . 'error_msg_' . $options['file_id'] . '*.log'));
// init logger
$this->log = new \CoreLibs\Debug\Logging($options);
$log = new \CoreLibs\Debug\Logging($options);
// * debug (A/B)
// NULL check for strip/prefix
$this->assertEquals(
$this->log->debug(
$log->debug(
$debug_msg['level'],
$debug_msg['string'],
$debug_msg['strip'],
@@ -837,7 +858,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
$expected_debug
);
// * if print check data in log file
$log_file = $this->log->getLogFileName();
$log_file = $log->getLogFileName();
if (!empty($options['debug_all']) && !empty($options['print_all'])) {
// file name matching
$this->assertStringStartsWith(
@@ -866,10 +887,10 @@ final class CoreLibsDebugLoggingTest extends TestCase
);
}
// ** ECHO ON
$log_string = $this->log->printErrorMsg();
$log_string = $log->printErrorMsg();
// * print
if (!empty($options['debug_all']) && !empty($options['echo_all'])) {
// print $this->log->printErrorMsg() . "\n";
// print $log->printErrorMsg() . "\n";
// echo string must start with
$this->assertStringStartsWith(
$expected_string_start,
@@ -893,6 +914,77 @@ final class CoreLibsDebugLoggingTest extends TestCase
);
}
}
// TODO: setLogUniqueId/getLogUniqueId
/**
* Undocumented function
*
* @return array
*/
public function logUniqueIdProvider(): array
{
return [
'option set' => [
'option' => true,
'override' => false,
],
'direct set' => [
'option' => false,
'override' => false,
],
'override set' => [
'option' => false,
'override' => true,
],
'option and override set' => [
'option' => false,
'override' => true,
],
];
}
/**
* Undocumented function
*
* @covers ::setLogUniqueId
* @covers ::getLogUniqueId
* @dataProvider logUniqueIdProvider
* @testdox per run log id set test: option: $option, override: $override [$_dataName]
*
* @param bool $option
* @param bool $override
* @return void
*/
public function testLogUniqueId(bool $option, bool $override): void
{
if ($option === true) {
$log = new \CoreLibs\Debug\Logging(['per_run' => $option]);
} else {
$log = new \CoreLibs\Debug\Logging();
$log->setLogUniqueId();
}
$per_run_id = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id,
'assert per log run id 1st'
);
if ($override === true) {
$log->setLogUniqueId(true);
$per_run_id_2nd = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id_2nd,
'assert per log run id 2nd'
);
$this->assertNotEquals(
$per_run_id,
$per_run_id_2nd,
'1st and 2nd don\'t match'
);
}
}
}
// __END__

View File

@@ -120,6 +120,16 @@ final class CoreLibsDebugSupportTest extends TestCase
null,
'a string',
],
'string with html chars, encode' => [
'a string with <> &',
true,
'a string with &lt;&gt; &amp;',
],
'string with html chars' => [
'a string with <> &',
null,
'a string with <> &',
],
'a number' => [
1234,
null,
@@ -180,22 +190,41 @@ final class CoreLibsDebugSupportTest extends TestCase
*/
public function debugStringProvider(): array
{
// 0: input string
// 1: replace
// 2: html flag
// 3: expected
return [
'null string, default' => [
0 => null,
1 => null,
2 => '-'
null,
null,
null,
'-'
],
'empty string, ... replace' => [
0 => '',
1 => '...',
2 => '...'
'',
'...',
null,
'...'
],
'filled string' => [
0 => 'some string',
1 => null,
2 => 'some string'
]
'some string',
null,
null,
'some string'
],
'string with html chars, encode' => [
'a string with <> &',
'-',
true,
'a string with &lt;&gt; &amp;',
],
'string with html chars' => [
'a string with <> &',
'-',
null,
'a string with <> &',
],
];
}
@@ -366,12 +395,14 @@ final class CoreLibsDebugSupportTest extends TestCase
if (count($compare) == 10) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerMethodList()
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected 10'
);
} else {
$this->assertEquals(
$expected_group,
\CoreLibs\Debug\Support::getCallerMethodList()
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected group'
);
}
}
@@ -398,24 +429,33 @@ final class CoreLibsDebugSupportTest extends TestCase
*
* @cover ::debugString
* @dataProvider debugStringProvider
* @testdox debugString $input with replace $replace will be $expected [$_dataName]
* @testdox debugString $input with replace $replace and html $flag will be $expected [$_dataName]
*
* @param string|null $input
* @param string|null $replace
* @param string $expected
* @param bool|null $flag
* @param string $expected
* @return void
*/
public function testDebugString(?string $input, ?string $replace, string $expected)
public function testDebugString(?string $input, ?string $replace, ?bool $flag, string $expected): void
{
if ($replace === null) {
if ($replace === null && $flag === null) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input)
\CoreLibs\Debug\Support::debugString($input),
'assert all default'
);
} elseif ($flag === null) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input, $replace),
'assert flag default'
);
} else {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input, $replace)
\CoreLibs\Debug\Support::debugString($input, $replace, $flag),
'assert all set'
);
}
}

View File

@@ -22,7 +22,7 @@ final class CoreLibsOutputFormElementsTest extends TestCase
*/
public function testOutputFormElements()
{
$this->assertTrue(true, 'Output Form Elements Tests not implemented');
// $this->assertTrue(true, 'Output Form Elements Tests not implemented');
$this->markTestIncomplete(
'Output\Form\Elements Tests have not yet been implemented'
);

View File

@@ -22,7 +22,7 @@ final class CoreLibsOutputFormTokenTest extends TestCase
*/
public function testOutputFormToken()
{
$this->assertTrue(true, 'Output Form Token Tests not implemented');
// $this->assertTrue(true, 'Output Form Token Tests not implemented');
$this->markTestIncomplete(
'Output\Form\Token Tests have not yet been implemented'
);

View File

@@ -22,7 +22,7 @@ final class CoreLibsOutputImageTest extends TestCase
*/
public function testOutputImage()
{
$this->assertTrue(true, 'Output Image Tests not implemented');
// $this->assertTrue(true, 'Output Image Tests not implemented');
$this->markTestIncomplete(
'Output\Image Tests have not yet been implemented'
);

View File

@@ -22,10 +22,10 @@ final class CoreLibsOutputProgressbarTest extends TestCase
*/
public function testOutputProgressbar()
{
/* $this->assertTrue(true, 'Output Progressbar Tests not implemented');
$this->markTestIncomplete(
'Output\Progressbar Tests have not yet been implemented'
); */
// $this->assertTrue(true, 'Output Progressbar Tests not implemented');
// $this->markTestIncomplete(
// 'Output\Progressbar Tests have not yet been implemented'
// );
$this->markTestSkipped('No implementation for Output\Progressbar at the moment');
}
}

View File

@@ -270,8 +270,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;
@@ -595,12 +597,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login
login_user_id VARCHAR UNIQUE, -- the login uid, at least 32 chars
login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated login id after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for login user id, but still allow normal login
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS;
@@ -629,11 +632,12 @@ COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the
COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'login id was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'login id is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'login id is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for login id, user can still login normal';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';
-- END: table/edit_user.sql
-- START: table/edit_log.sql

View File

@@ -0,0 +1,123 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
// will be overwritten in config.master.php depending on location
$DEBUG_ALL_OVERRIDE = true; // set to 1 to debug on live/remote server locations
$DEBUG_ALL = true;
$PRINT_ALL = true;
$ECHO_ALL = true;
$DB_DEBUG = true;
if ($DEBUG_ALL) {
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
}
ob_start();
// basic class test file
define('USE_DATABASE', false);
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-create_email';
ob_end_flush();
// override echo all from config.master.php
$ECHO_ALL = true;
use CoreLibs\Create\Email;
use CoreLibs\Convert\Html;
$log = new CoreLibs\Debug\Logging([
'log_folder' => BASE . LOG,
'file_id' => $LOG_FILE_ID,
// add file date
'print_file_date' => true,
// set debug and print flags
'debug_all' => $DEBUG_ALL,
'echo_all' => $ECHO_ALL,
'print_all' => $PRINT_ALL,
]);
// define a list of from to color sets for conversion test
$PAGE_NAME = 'TEST CLASS: CREATE EMAIL';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$from_name = '日本語';
$from_email = 'test@test.com';
print "SET: $from_name / $from_email: "
. Html::htmlent(Email::encodeEmailName($from_email, $from_name)) . "<br>";
$status = Email::sendEmail(
'TEST',
'BODY',
'test@test.com',
'Test Name',
[
[
'name' => 'To 1',
'email' => 'to1@test.com'
],
],
[],
'UTF-8',
true,
$log
);
print "SENDING: " . $status . "<br>";
$status = Email::sendEmail(
'TEST {REPLACE}',
'BODY {OTHER}',
'test@test.com',
'Test Name',
[
[
'name' => 'To 1-A',
'email' => 'to1-a@test.com'
],
[
'name' => 'To 2-A',
'email' => 'to2-a@test.com',
'replace' => [
'OTHER' => '--FOR 2 A other--'
]
],
],
[
'REPLACE' => '**replaced**',
'OTHER' => '**other**'
],
'UTF-8',
true,
$log
);
print "SENDING: " . $status . "<br>";
$status = Email::sendEmail(
'TEST',
'BODY',
'test@test.com',
'Test Name',
['a@a.com', 'b@b.com'],
[],
'UTF-8',
true,
$log
);
print "SENDING: " . $status . "<br>";
// error message
print $log->printErrorMsg();
print "</body></html>";
// __END__

View File

@@ -71,6 +71,7 @@ print "S::PRINTBOOL(name): " . DebugSupport::printBool(true, 'Name') . "<br>";
print "S::PRINTBOOL(name, ok): " . DebugSupport::printBool(true, 'Name', 'ok') . "<br>";
print "S::PRINTBOOL(name, ok, not): " . DebugSupport::printBool(false, 'Name', 'ok', 'not') . "<br>";
print "S::DEBUSTRING(s): " . DebugSupport::debugString('SET') . "<br>";
print "S::DEBUSTRING(s&gt;): " . DebugSupport::debugString('<SET>') . "<br>";
print "S::DEBUSTRING(''): " . DebugSupport::debugString('') . "<br>";
print "S::DEBUSTRING(,s): " . DebugSupport::debugString(null, '{-}') . "<br>";

View File

@@ -63,6 +63,7 @@ print '<div><a href="class_test.password.php">Class Test: PASSWORD</a></div>';
print '<div><a href="class_test.math.php">Class Test: MATH</a></div>';
print '<div><a href="class_test.html.php">Class Test: HTML/ELEMENTS</a></div>';
print '<div><a href="class_test.email.php">Class Test: EMAIL</a></div>';
print '<div><a href="class_test.create_email.php">Class Test: CREATE EMAIL</a></div>';
print '<div><a href="class_test.uids.php">Class Test: UIDS</a></div>';
print '<div><a href="class_test.phpv.php">Class Test: PHP VERSION</a></div>';
print '<div><a href="class_test.hash.php">Class Test: HASH</a></div>';

View File

@@ -108,7 +108,7 @@ $data = [
];
// log action
// no log if login
if (!$login->login) {
if (!$login->loginActionRun()) {
$cms->adbEditLog('Submit', $data, 'BINARY');
}
//------------------------------ logging end

View File

@@ -404,6 +404,7 @@ if ($form->my_page_name == 'edit_order') {
$elements[] = $form->formCreateElement('password_change_interval');
$elements[] = $form->formCreateElement('login_user_id');
$elements[] = $form->formCreateElement('login_user_id_set_date');
$elements[] = $form->formCreateElement('login_user_id_last_revalidate');
$elements[] = $form->formCreateElement('login_user_id_locked');
$elements[] = $form->formCreateElement('login_user_id_revalidate_after');
$elements[] = $form->formCreateElement('login_user_id_valid_from');

View File

@@ -159,6 +159,12 @@ $edit_users = [
'type' => 'view',
'empty' => '-'
],
'login_user_id_last_revalidate' => [
'output_name' => 'loginUserId last revalidate date',
'value' => $GLOBALS['login_user_id_last_revalidate'] ?? '',
'type' => 'view',
'empty' => '-'
],
'login_user_id_locked' => [
'value' => $GLOBALS['login_user_id_locked'] ?? '',
'output_name' => 'loginUserId usage locked',

View File

@@ -79,12 +79,12 @@ class Login
/** @var string source, either _GET or _POST or empty */
private $login_user_id_source = '';
/** @var bool set to true if illegal characters where found in the login user id string */
private $login_unclear = false;
private $login_user_id_unclear = false;
// is set to one if login okay, or EUID is set and user is okay to access this page
/** @var bool */
private $permission_okay = false;
/** @var string pressed login */
public $login;
private $login = '';
/** @var string master action command */
private $action;
/** @var string login name */
@@ -540,6 +540,8 @@ class Login
. "eu.debug, eu.db_debug, "
// enabled
. "eu.enabled, eu.deleted, "
// for checks only
. "eu.login_user_id, "
// login id validation
. "CASE WHEN ("
. "(eu.login_user_id_valid_from IS NULL "
@@ -550,7 +552,7 @@ class Login
// check if user must login
. "CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL "
. "AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL "
. "AND (eu.login_user_id_set_date + eu.login_user_id_revalidate_after)::DATE "
. "AND (eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE "
. "<= NOW()::DATE "
. "THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, "
. "eu.login_user_id_locked, "
@@ -653,6 +655,15 @@ class Login
// check if user is okay
$this->loginCheckPermissions();
if ($this->login_error == 0) {
if (
!empty($res['login_user_id']) &&
!empty($this->username) && !empty($this->password)
) {
$q = "UPDATE edit_user SET "
. "login_user_id_last_revalidate = NOW() "
. "WHERE edit_user_id = " . $this->euid;
$this->db->dbExec($q);
}
// now set all session vars and read page permissions
$_SESSION['DEBUG_ALL'] = $this->db->dbBoolean($res['debug']);
$_SESSION['DB_DEBUG'] = $this->db->dbBoolean($res['db_debug']);
@@ -1553,7 +1564,7 @@ EOM;
);
// flag unclean input data
if ($login_user_id_changed > 0) {
$this->login_unclear = true;
$this->login_user_id_unclear = true;
// error for invalid user id?
$this->log->debug('LOGIN USER ID', 'Invalid characters: '
. $login_user_id_changed . ' in loginUserId: '
@@ -1891,7 +1902,7 @@ EOM;
// check if user must login
. "CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL "
. "AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL "
. "AND eu.login_user_id_set_date + eu.login_user_id_revalidate_after <= NOW()::DATE "
. "AND eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after <= NOW()::DATE "
. "THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, "
. "eu.login_user_id_locked "
//
@@ -2128,6 +2139,16 @@ EOM;
return false;
}
/**
* Returns true if login button was pressed
*
* @return bool If login action was run, return true
*/
public function loginActionRun(): bool
{
return empty($this->login) ? false : true;
}
/**
* Returns current set loginUserId or empty if unset
*
@@ -2156,7 +2177,7 @@ EOM;
*/
public function loginGetLoginUserIdUnclean(): bool
{
return $this->login_unclear;
return $this->login_user_id_unclear;
}
/**

View File

@@ -0,0 +1,246 @@
<?php
/*
* Create email class
*/
declare(strict_types=1);
namespace CoreLibs\Create;
/**
* sending simple text emails
*/
class Email
{
/**
* create mime encoded email part for to/from emails.
* If encoding is not UTF-8 it will convert the email name to target encoding
* FROM UTF-8
* Source data is ALWAYS seen as utf-8
*
* @param string $email E-Mail address
* @param string $email_name Name for the email address, in UTF-8, if not set, empty
* @param string $encoding Encoding, if not set UTF-8
* @return string Correctly encoded and build email string
*/
public static function encodeEmailName(
string $email,
string $email_name = '',
string $encoding = 'UTF-8'
): string {
if (!empty($email_name)) {
// if encoding is not UTF-8 then we convert
if ($encoding != 'UTF-8') {
$email_name = mb_convert_encoding($email_name, $encoding, 'UTF-8');
}
$email_name =
mb_encode_mimeheader(
mb_convert_kana(
$email_name,
'KV',
$encoding
),
$encoding
);
return '"' . $email_name . '" '
. '<' . (string)$email . '>';
} else {
return $email;
}
}
/**
* Subject/Body replace sub function
*
* @param string $subject Subject string, in UTF-8
* @param string $body Body string, in UTF-8
* @param array<string,string> $replace Replace the array as key -> value, in UTF-8
* @param string $encoding Encoding for subject encode mime header
* @return array<string> Pos 0: Subject, Pos 1: Body
*/
private static function replaceContent(
string $subject,
string $body,
array $replace,
string $encoding
): array {
foreach (['subject', 'body'] as $element) {
$$element = str_replace(
array_map(
function ($key) {
return '{' . $key . '}';
},
array_keys($replace)
),
array_values($replace),
$$element
);
}
// if encoding is NOT UTF-8 convert to target
if ($encoding != 'UTF-8') {
$subject = mb_convert_encoding($subject, $encoding, 'UTF-8');
$body = mb_convert_encoding($body, $encoding, 'UTF-8');
}
// we need to encodde the subject
$subject = mb_encode_mimeheader($subject, $encoding);
return [$subject, $body];
}
/**
* Send plain text email with possible to replace subject/body data
* either global or per to email set.
* replace to tags are in {} in the subject or body
*
* @param string $subject Mail subject, mandatory, in UTF-8
* @param string $body Mail body, mandatory, in UTF-8
* @param string $from_email From email, mandatory
* @param string $from_name From email name, in UTF-8
* if empty '' then not set
* @param array<mixed> $send_to_emails to email or array for email/replace
* If array: name/email/replace[key,value]
* name and replace must be in UTF-8
* At least one must be set
* @param array<string,string> $replace_content Subject/Body replace as
* search -> replace, in UTF-8
* @param string $encoding E-Mail encoding, default UTF-8
* @param bool $test test flag, default off
* @param \CoreLibs\Debug\Logging|null $log Logging class,
* only used if test flag is true
* @return int 2 test only, no sent
* 1 for ok,
* 0 for send not ok
* -1 for nothing set (emails, subject, body)
* -2 for empty to list
*/
public static function sendEmail(
string $subject,
string $body,
string $from_email,
string $from_name,
array $send_to_emails,
array $replace_content = [],
string $encoding = 'UTF-8',
bool $test = false,
?\CoreLibs\Debug\Logging $log = null
): int {
/** @var array<string> */
$to_emails = [];
/** @var array<string,array<string,string>> */
$to_replace = [];
/** @var string */
$out_subject = $subject;
/** @var string */
$out_body = $body;
// check basic set
if (empty($subject) || empty($body) || empty($from_email)) {
return -1;
}
// if not one valid to, abort
foreach ($send_to_emails as $to_email) {
// to_email can be string, then only to email
// else expect 'email' & 'name'
if (
is_array($to_email) &&
isset($to_email['email'])
) {
$_to_email = self::encodeEmailName(
$to_email['email'],
$to_email['name'] ?? '',
$encoding
);
$to_emails[] = $_to_email;
// if we have to replacement, this override replace content
if (isset($to_email['replace']) && count($to_email['replace'])) {
// merge with original replace content,
// to data will override original data
$to_replace[$_to_email] = array_merge(
$replace_content,
$to_email['replace']
);
}
} elseif (is_string($to_email)) {
$to_emails[] = $to_email;
}
}
if (!count($to_emails)) {
return -2;
}
// the email headers needed
$headers = [
'From' => self::encodeEmailName($from_email, $from_name, $encoding),
'Content-type' => "text/plain; charset=" . $encoding,
'MIME-Version' => "1.0",
];
// if we have a replace string, we need to do replace run
// only if there is no dedicated to replace
// also run replace if there is nothing to replace at all
// this will mime encode the subject
if (!count($to_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$replace_content,
$encoding
);
}
$mail_delivery_status = 1;
// send the email
foreach ($to_emails as $to_email) {
// default mail status is success
$mail_status = true;
// if there is a to replace, if not use the original replace content
if (count($to_replace)) {
$_replace = [];
if (!empty($to_replace[$to_email])) {
$_replace = $to_replace[$to_email];
} elseif (count($replace_content)) {
$_replace = $replace_content;
}
if (count($_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$_replace,
$encoding
);
}
}
// if we are in test mode, do not send an email and set status to 2
if ($test === false) {
$mail_status = mail($to_email, $out_subject, $out_body, $headers);
} else {
$mail_delivery_status = 2;
}
// log if an log instance exists
if ($log instanceof \CoreLibs\Debug\Logging) {
// build debug strings: convert to UTF-8 if not utf-8
$log->debug('SEND EMAIL', 'HEADERS: ' . $log->prAr($headers) . ', '
. 'ENCODING: ' . $encoding . ', '
. 'TO: ' . $to_email . ', '
. 'SUBJECT: ' . $out_subject . ', '
. 'BODY: ' . ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding)));
$log->debug('SEND EMAIL JSON', json_encode([
'encoding' => $encoding,
'header' => $headers,
'to' => $to_email,
'subject' => $out_subject,
'body' => ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding))
]) ?: '{}');
}
if (!$mail_status) {
$mail_delivery_status = 0;
}
}
return $mail_delivery_status;
}
}
// __END__

View File

@@ -293,15 +293,7 @@ class Logging
}
// set per run ID
if ($this->log_per_run) {
/* if (isset($GLOBALS['LOG_FILE_UNIQUE_ID'])) {
$this->log_file_unique_id = $GLOBALS['LOG_FILE_UNIQUE_ID'];
} */
if (!$this->log_file_unique_id) {
// $GLOBALS['LOG_FILE_UNIQUE_ID'] =
$this->log_file_unique_id =
date('Y-m-d_His') . '_U_'
. substr(hash('sha1', uniqid((string)mt_rand(), true)), 0, 8);
}
$this->setLogUniqueId();
}
}
@@ -394,7 +386,10 @@ class Logging
// write to file
// first check if max file size is is set and file is bigger
if ($this->log_max_filesize > 0 && ((filesize($fn) / 1024) > $this->log_max_filesize)) {
if (
$this->log_max_filesize > 0 &&
((filesize($fn) / 1024) > $this->log_max_filesize)
) {
// for easy purpose, rename file only to attach timestamp, nur sequence numbering
rename($fn, $fn . '.' . date("YmdHis"));
}
@@ -593,6 +588,10 @@ class Logging
return false;
}
$this->{'log_per_' . $type} = $set;
// if per run set unique id
if ($type == 'run' && $set == true) {
$this->setLogUniqueId();
}
return true;
}
@@ -610,6 +609,33 @@ class Logging
return $this->{'log_per_' . $type};
}
/**
* Sets a unique id based on current date (y/m/d, h:i:s) and a unique id (8 chars)
* if override is set to true it will be newly set, else if already set nothing changes
*
* @param bool $override True to force new set
* @return void
*/
public function setLogUniqueId(bool $override = false): void
{
if (!$this->log_file_unique_id || $override == true) {
$this->log_file_unique_id =
date('Y-m-d_His') . '_U_'
. substr(hash('sha1', uniqid((string)mt_rand(), true)), 0, 8);
}
}
/**
* Return current set log file unique id,
* empty string for not set
*
* @return string
*/
public function getLogUniqueId(): string
{
return $this->log_file_unique_id;
}
/**
* Set or get the log file date extension flag
* if null or empty parameter gets current flag

View File

@@ -8,6 +8,8 @@ declare(strict_types=1);
namespace CoreLibs\Debug;
use CoreLibs\Convert\Html;
class Support
{
/**
@@ -89,7 +91,7 @@ class Support
* Debug\Logging compatible output
*
* @param mixed $mixed
* @param bool $no_html set to true to use ##HTMLPRE##
* @param bool $no_html set to true to use ##HTMLPRE##or html escape
* @return string
*/
public static function printToString($mixed, bool $no_html = false): string
@@ -103,6 +105,12 @@ class Support
} elseif (is_array($mixed)) {
// use the pre one OR debug one
return self::printAr($mixed, $no_html);
} elseif (is_string($mixed)) {
if ($no_html) {
return Html::htmlent((string)$mixed);
} else {
return (string)$mixed;
}
} else {
// should be int/float/string
return (string)$mixed;
@@ -190,12 +198,19 @@ class Support
* @param string $replace [default '-'] What to replace the empty string with
* @return string String itself or the replaced value
*/
public static function debugString(?string $string, string $replace = '-'): string
{
public static function debugString(
?string $string,
string $replace = '-',
bool $no_html = false
): string {
if (empty($string)) {
return $replace;
$string = $replace;
}
if ($no_html) {
return Html::htmlent($string);
} else {
return $string;
}
return $string;
}
}

View File

@@ -27,6 +27,7 @@ return array(
'CoreLibs\\Convert\\Math' => $baseDir . '/lib/CoreLibs/Convert/Math.php',
'CoreLibs\\Convert\\MimeAppName' => $baseDir . '/lib/CoreLibs/Convert/MimeAppName.php',
'CoreLibs\\Convert\\MimeEncode' => $baseDir . '/lib/CoreLibs/Convert/MimeEncode.php',
'CoreLibs\\Create\\Email' => $baseDir . '/lib/CoreLibs/Create/Email.php',
'CoreLibs\\Create\\Hash' => $baseDir . '/lib/CoreLibs/Create/Hash.php',
'CoreLibs\\Create\\RandomKey' => $baseDir . '/lib/CoreLibs/Create/RandomKey.php',
'CoreLibs\\Create\\Session' => $baseDir . '/lib/CoreLibs/Create/Session.php',

View File

@@ -92,6 +92,7 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9
'CoreLibs\\Convert\\Math' => __DIR__ . '/../..' . '/lib/CoreLibs/Convert/Math.php',
'CoreLibs\\Convert\\MimeAppName' => __DIR__ . '/../..' . '/lib/CoreLibs/Convert/MimeAppName.php',
'CoreLibs\\Convert\\MimeEncode' => __DIR__ . '/../..' . '/lib/CoreLibs/Convert/MimeEncode.php',
'CoreLibs\\Create\\Email' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/Email.php',
'CoreLibs\\Create\\Hash' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/Hash.php',
'CoreLibs\\Create\\RandomKey' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/RandomKey.php',
'CoreLibs\\Create\\Session' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/Session.php',