Compare commits

...

4 Commits

Author SHA1 Message Date
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
15 changed files with 240 additions and 101 deletions

View File

@@ -270,8 +270,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id) (OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN THEN
NEW.login_user_id_set_date = NOW(); 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 ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL; NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF; END IF;
RETURN NEW; RETURN NEW;
END; END;
@@ -595,12 +597,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested 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 password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login -- _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_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_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_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_revalidate_after INTERVAL, -- user must login to revalidated loginUserId 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_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block -- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages) additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS; ) 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_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.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 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_set_date IS 'loginUserId 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_last_revalidate IS 'set when username/password login is done and loginUserId is set';
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_valid_from IS 'loginUserId is valid from 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_valid_until IS 'loginUserId is valid until this date, <=';
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_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'; COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';
-- END: table/edit_user.sql -- END: table/edit_user.sql
-- START: table/edit_log.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) (OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN THEN
NEW.login_user_id_set_date = NOW(); 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 ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL; NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF; END IF;
RETURN NEW; RETURN NEW;
END; 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_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login -- _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_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_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_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_revalidate_after INTERVAL, -- user must login to revalidated loginUserId 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_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block -- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages) additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS; ) 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_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.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 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_set_date IS 'loginUserId 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_last_revalidate IS 'set when username/password login is done and loginUserId is set';
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_valid_from IS 'loginUserId is valid from 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_valid_until IS 'loginUserId is valid until this date, <=';
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_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'; COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';

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); -- ALTER TABLE edit_user ADD CONSTRAINT edit_user_login_user_id_key UNIQUE (login_user_id);
-- when above uid was set -- 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_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 -- 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_from TIMESTAMP WITHOUT TIME ZONE;
ALTER TABLE edit_user ADD login_user_id_valid_until 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) (OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN THEN
NEW.login_user_id_set_date = NOW(); 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 ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL; NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF; END IF;
RETURN NEW; RETURN NEW;
END; END;

View File

@@ -157,17 +157,18 @@ final class CoreLibsACLLoginTest extends TestCase
*/ */
public function loginProvider(): array public function loginProvider(): array
{ {
// 0: mock settings/override flag settings // 0[mock] : mock settings/override flag settings
// 2: get array IN // 1[get] : get array IN
// 1: post array IN // 2[post] : post array IN
// login_login, login_username, login_password, login_logout // login_login, login_username, login_password, login_logout
// change_password, pw_username, pw_old_password, pw_new_password, // change_password, pw_username, pw_old_password, pw_new_password,
// pw_new_password_confirm // pw_new_password_confirm
// 2: override session set // 3[session]: override session set
// 3: expected error code, 0 for all ok, 3000 for login page view // 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 // 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 // 5[return] : expected return array, eg login_error code,
return [ // or other info data to match
$tests = [
'load, no login' => [ 'load, no login' => [
// error code, only for exceptions // error code, only for exceptions
[ [
@@ -290,7 +291,7 @@ final class CoreLibsACLLoginTest extends TestCase
], ],
], ],
// login: all missing // login: all missing
'login: all missing' => [ 'login: failed: all missing' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
], ],
@@ -311,7 +312,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login: missing username // login: missing username
'login: missing username' => [ 'login: failed: missing username' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
], ],
@@ -332,7 +333,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login: missing password // login: missing password
'login: missing password' => [ 'login: failed: missing password' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
], ],
@@ -353,7 +354,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login: user not found // login: user not found
'login: user not found' => [ 'login: failed: user not found' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
], ],
@@ -377,7 +378,7 @@ final class CoreLibsACLLoginTest extends TestCase
// 9999: not valid password encoding // 9999: not valid password encoding
// 1013: normal password failed // 1013: normal password failed
// 1012: plain password check failed // 1012: plain password check failed
'login: invalid password' => [ 'login: failed: invalid password' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
], ],
@@ -399,7 +400,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login: ok (but deleted) // login: ok (but deleted)
'login: ok, but deleted' => [ 'login: ok -> failed: but deleted' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, '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, but not enabled' => [ 'login: ok -> failed: but not enabled' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -449,7 +450,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login: ok (but locked) // login: ok (but locked)
'login: ok, but locked' => [ 'login: ok -> failed: but locked' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -474,7 +475,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login: make user get locked strict // login: make user get locked strict
'login: ok, get locked, strict' => [ 'login: ok -> failed: get locked, strict' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -498,7 +499,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login ok, but in locked period (until) // 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', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -552,7 +553,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login ok, but in locked period (after) // 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', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -577,7 +578,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// login ok, but in locked period (until, after) // 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', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, '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, but login user id locked' => [ 'login: ok -> failed:, but loginUserId locked' => [
[ [
'page_name' => 'edit_users.php', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -830,7 +831,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// loginUserId check with revalidate on/off // 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', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -886,7 +887,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// loginUserId check with active time from only // 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', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -942,7 +943,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// loginUserId check with active time until only // 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', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -968,7 +969,7 @@ final class CoreLibsACLLoginTest extends TestCase
] ]
], ],
// loginUserId check with active time from/until // 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', 'page_name' => 'edit_users.php',
'edit_access_id' => 1, 'edit_access_id' => 1,
@@ -994,11 +995,53 @@ final class CoreLibsACLLoginTest extends TestCase
. 'Login Failed - Login User ID is outside valid date range' . '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: // other:
// login check edit access id of ID not null and not in array // login check edit access id of ID not null and not in array
// login OK, but during action user gets disabled/deleted/etc // 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'])) { if (!empty($mock_settings['test_login_user_id'])) {
self::$db->dbExec( self::$db->dbExec(
"UPDATE edit_user SET " "UPDATE edit_user SET "
. "login_user_id_set_date = NOW(), "
. "login_user_id = " . "login_user_id = "
. self::$db->dbEscapeLiteral($mock_settings['loginUserId']) . 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'])) { if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
$q_sub = ''; $q_sub = '';
if ($mock_settings['test_login_user_id_revalidate_after'] == 'on') { 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 "; . "login_user_id_revalidate_after = '1 day'::interval ";
} else { } 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 "; . "login_user_id_revalidate_after = '6 day'::interval ";
} }
self::$db->dbExec( self::$db->dbExec(
@@ -1220,6 +1262,50 @@ final class CoreLibsACLLoginTest extends TestCase
. self::$db->dbEscapeLiteral($mock_settings['test_username']) . 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 ( if (
!empty($mock_settings['test_login_user_id_valid_from']) || !empty($mock_settings['test_login_user_id_valid_from']) ||
!empty($mock_settings['test_login_user_id_valid_until']) !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 // always check, even on error or not set
if (!$login_mock->loginGetLoginUserIdUnclean()) { if (!$login_mock->loginGetLoginUserIdUnclean()) {
$this->assertEquals( $this->assertEquals(
@@ -1540,36 +1634,38 @@ final class CoreLibsACLLoginTest extends TestCase
. self::$db->dbEscapeLiteral($post['login_username']) . self::$db->dbEscapeLiteral($post['login_username'])
); );
} }
// if (!empty($mock_settings['test_login_user_id'])) { if (!empty($mock_settings['test_login_user_id'])) {
// self::$db->dbExec( self::$db->dbExec(
// "UPDATE edit_user SET " "UPDATE edit_user SET "
// . "login_user_id = NULL, " . "login_user_id = NULL, "
// . "login_user_id_set_date = NULL " // below to rows are automatcially reset
// . "WHERE LOWER(username) = " . "login_user_id_set_date = NULL, "
// . self::$db->dbEscapeLiteral($mock_settings['test_username']) . "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 " if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
// . "login_user_id_set_date = NULL, " self::$db->dbExec(
// . "login_user_id_revalidate_after = NULL " "UPDATE edit_user SET "
// . "WHERE LOWER(username) = " . "login_user_id_last_revalidate = NULL, "
// . self::$db->dbEscapeLiteral($mock_settings['test_username']) . "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']) if (
// ) { !empty($mock_settings['test_login_user_id_valid_from']) ||
// self::$db->dbExec( !empty($mock_settings['test_login_user_id_valid_until'])
// "UPDATE edit_user SET " ) {
// . "login_user_id_valid_from = NULL, " self::$db->dbExec(
// . "login_user_id_valid_until = NULL " "UPDATE edit_user SET "
// . "WHERE LOWER(username) = " . "login_user_id_valid_from = NULL, "
// . self::$db->dbEscapeLiteral($mock_settings['test_username']) . "login_user_id_valid_until = NULL "
// ); . "WHERE LOWER(username) = "
// } . self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
} }
// - loginGetAclList (null, invalid,) // - loginGetAclList (null, invalid,)

View File

@@ -36,9 +36,9 @@ final class CoreLibsDBExtendedArrayIOTest extends TestCase
* *
* @return void * @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( $this->markTestIncomplete(
'DB\Extended\ArrayIO Tests have not yet been implemented' 'DB\Extended\ArrayIO Tests have not yet been implemented'
); );

View File

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

View File

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

View File

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

View File

@@ -22,10 +22,10 @@ final class CoreLibsOutputProgressbarTest extends TestCase
*/ */
public function testOutputProgressbar() public function testOutputProgressbar()
{ {
/* $this->assertTrue(true, 'Output Progressbar Tests not implemented'); // $this->assertTrue(true, 'Output Progressbar Tests not implemented');
$this->markTestIncomplete( // $this->markTestIncomplete(
'Output\Progressbar Tests have not yet been implemented' // 'Output\Progressbar Tests have not yet been implemented'
); */ // );
$this->markTestSkipped('No implementation for Output\Progressbar at the moment'); $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) (OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN THEN
NEW.login_user_id_set_date = NOW(); 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 ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL; NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF; END IF;
RETURN NEW; RETURN NEW;
END; END;
@@ -595,12 +597,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested 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 password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login -- _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_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_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_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_revalidate_after INTERVAL, -- user must login to revalidated loginUserId 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_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block -- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages) additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS; ) 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_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.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 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_set_date IS 'loginUserId 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_last_revalidate IS 'set when username/password login is done and loginUserId is set';
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_valid_from IS 'loginUserId is valid from 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_valid_until IS 'loginUserId is valid until this date, <=';
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_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'; COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';
-- END: table/edit_user.sql -- END: table/edit_user.sql
-- START: table/edit_log.sql -- START: table/edit_log.sql

View File

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

View File

@@ -404,6 +404,7 @@ if ($form->my_page_name == 'edit_order') {
$elements[] = $form->formCreateElement('password_change_interval'); $elements[] = $form->formCreateElement('password_change_interval');
$elements[] = $form->formCreateElement('login_user_id'); $elements[] = $form->formCreateElement('login_user_id');
$elements[] = $form->formCreateElement('login_user_id_set_date'); $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_locked');
$elements[] = $form->formCreateElement('login_user_id_revalidate_after'); $elements[] = $form->formCreateElement('login_user_id_revalidate_after');
$elements[] = $form->formCreateElement('login_user_id_valid_from'); $elements[] = $form->formCreateElement('login_user_id_valid_from');

View File

@@ -159,6 +159,12 @@ $edit_users = [
'type' => 'view', 'type' => 'view',
'empty' => '-' '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' => [ 'login_user_id_locked' => [
'value' => $GLOBALS['login_user_id_locked'] ?? '', 'value' => $GLOBALS['login_user_id_locked'] ?? '',
'output_name' => 'loginUserId usage locked', 'output_name' => 'loginUserId usage locked',

View File

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