It has to be int or null but because the SQL result is undefined (string) it needs to be converted on return if it is a numeric value, else null will be returned (it is the edit access id PK so it has to be numeric)
2507 lines
78 KiB
PHP
2507 lines
78 KiB
PHP
<?php
|
|
|
|
/*********************************************************************
|
|
* AUTHOR: Clemens Schwaighofer
|
|
* CREATED: 2000/06/01
|
|
* VERSION: 5.0.0
|
|
* RELEASED LICENSE: GNU GPL 3
|
|
* SHORT DESCRIPTON:
|
|
* ~ 2003/03/03: change the whole include file into one class
|
|
* advantages are a) can include before actuall call, can control it
|
|
* easer (login db, etc), etc etc etc
|
|
*
|
|
* a login lib that should stand out of all others
|
|
* will be a class one day
|
|
*
|
|
* descrption of session_vars
|
|
* DEBUG_ALL - set to one, prints out error_msg var at end of php execution
|
|
* DB_DEBUG - prints out database debugs (query, etc)
|
|
* GROUP_LEVEL - the level he can access (numeric)
|
|
* USER_NAME - login name from user
|
|
* LANG - lang to show edit interface (not yet used)
|
|
* DEFAULT_CHARSET - in connection with LANG (not yet used)
|
|
* PAGES - array of hashes
|
|
* edit_page_id - ID from the edit_pages table
|
|
* filename - name of the file
|
|
* page_name - name in menu
|
|
* menu - appears in menu
|
|
* popup - is a popup
|
|
* popup_x - if popup -> width
|
|
* popup_y - if popup -> height
|
|
* online - page is online (user can access)
|
|
* query_string - string to paste for popup (will change)
|
|
*
|
|
* HISTORY:
|
|
* 2010/12/21 (cs) merge back password change interface
|
|
* 2010/12/17 (cs) change that password can be blowfish encrypted,
|
|
* auto detects if other encryption is used (md5, std des)
|
|
* and tries to use them
|
|
* 2007/05/29 (cs) BUG with assign query and visible sub arrays to pages
|
|
* 2005/09/21 (cs) if error -> unset the session vars
|
|
* 2005/07/04 (cs) add a function to write into the edit log file
|
|
* 2005/07/01 (cs) start adepting login class to new edit interface layout
|
|
* 2005/03/31 (cs) fixed the class call with all debug vars
|
|
* 2004/11/17 (cs) unused var cleanup
|
|
* 2004/11/16 (cs) rewrite login so it uses a template and not just plain html.
|
|
* prepare it, so it will be able to use external stuff later
|
|
* (some interface has to be designed for that
|
|
* 2004/11/16 (cs) removed the mobile html part from login
|
|
* 2004/09/30 (cs) layout fix
|
|
* 2003-11-11: if user has debug 1 unset memlimit, because there can be serious
|
|
* problems with the query logging
|
|
* 2003-06-12: added flag to PAGES array
|
|
* changed the get vars from GLOBALS to _POST
|
|
* changed the session registration. no more GLOBAL vars are registered
|
|
* only _SESSION["..."]
|
|
* 2003-06-09: added mobile phone login possibility
|
|
* 2003-03-04: droped ADMIN and added GROUP_LEVEL
|
|
* 2003-03-03: started to change the include file function collection
|
|
* to become a class
|
|
* 2003-02-28: various advances and changes, but far from perfect
|
|
* decided to change it into a class for easier handling
|
|
* add also possibility to change what will stored in the
|
|
* login session ?
|
|
* 2000-06-01: created basic idea and functions
|
|
*********************************************************************/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace CoreLibs\ACL;
|
|
|
|
use CoreLibs\Security\Password;
|
|
use CoreLibs\Convert\Json;
|
|
|
|
class Login
|
|
{
|
|
/** @var ?int the user id var*/
|
|
private ?int $euid;
|
|
/** @var string _GET/_POST loginUserId parameter for non password login */
|
|
private string $login_user_id = '';
|
|
/** @var string source, either _GET or _POST or empty */
|
|
private string $login_user_id_source = '';
|
|
/** @var bool set to true if illegal characters where found in the login user id string */
|
|
private bool $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 bool $permission_okay = false;
|
|
/** @var string pressed login */
|
|
private string $login = '';
|
|
/** @var string master action command */
|
|
private string $action;
|
|
/** @var string login name */
|
|
private string $username;
|
|
/** @var string login password */
|
|
private string $password;
|
|
/** @var string logout button */
|
|
private string $logout;
|
|
/** @var bool if this is set to true, the user can change passwords */
|
|
private bool $password_change = false;
|
|
/** @var bool password change was successful */
|
|
private bool $password_change_ok = false;
|
|
// can we reset password and mail to user with new password set screen
|
|
/** @var bool */
|
|
private bool $password_forgot = false;
|
|
/** @var bool password forgot mail send ok */
|
|
// private $password_forgot_ok = false;
|
|
/** @var string */
|
|
private string $change_password;
|
|
/** @var string */
|
|
private string $pw_username;
|
|
/** @var string */
|
|
private string $pw_old_password;
|
|
/** @var string */
|
|
private string $pw_new_password;
|
|
/** @var string */
|
|
private string $pw_new_password_confirm;
|
|
/** @var array<string> array of users for which the password change is forbidden */
|
|
private array $pw_change_deny_users = [];
|
|
/** @var string */
|
|
private string $logout_target = '';
|
|
/** @var int */
|
|
private int $max_login_error_count = -1;
|
|
/** @var array<string> */
|
|
private array $lock_deny_users = [];
|
|
/** @var string */
|
|
private string $page_name = '';
|
|
|
|
/** @var int if we have password change we need to define some rules */
|
|
private int $password_min_length = 9;
|
|
/** @var int an true maxium min, can never be set below this */
|
|
private int $password_min_length_max = 9;
|
|
// max length is fixed as 255 (for input type max), if set highter
|
|
// it will be set back to 255
|
|
/** @var int */
|
|
private int $password_max_length = 255;
|
|
|
|
/** @var int minum password length */
|
|
public const PASSWORD_MIN_LENGTH = 9;
|
|
/** @var int maxium password lenght */
|
|
public const PASSWORD_MAX_LENGTH = 255;
|
|
/** @var string special characters for regex */
|
|
public const PASSWORD_SPECIAL_RANGE = '@$!%*?&';
|
|
/** @var string regex for lower case alphabet */
|
|
public const PASSWORD_LOWER = '(?=.*[a-z])';
|
|
/** @var string regex for upper case alphabet */
|
|
public const PASSWORD_UPPER = '(?=.*[A-Z])';
|
|
/** @var string regex for numbers */
|
|
public const PASSWORD_NUMBER = '(?=.*\d)';
|
|
/** @var string regex for special chanagers */
|
|
public const PASSWORD_SPECIAL = "(?=.*[" . self::PASSWORD_SPECIAL_RANGE . "])";
|
|
/** @var string regex for fixed allowed characters password regex */
|
|
public const PASSWORD_REGEX = "/^"
|
|
. self::PASSWORD_LOWER
|
|
. self::PASSWORD_UPPER
|
|
. self::PASSWORD_NUMBER
|
|
. self::PASSWORD_SPECIAL
|
|
. "[A-Za-z\d" . self::PASSWORD_SPECIAL_RANGE . "]"
|
|
. "{" . self::PASSWORD_MIN_LENGTH . "," . self::PASSWORD_MAX_LENGTH . "}"
|
|
. "$/";
|
|
|
|
/** @var array<string> can have several regexes, if nothing set, all is ok */
|
|
private array $password_valid_chars = [
|
|
// '^(?=.*\d)(?=.*[A-Za-z])[0-9A-Za-z!@#$%]{8,}$',
|
|
// '^(?.*(\pL)u)(?=.*(\pN)u)(?=.*([^\pL\pN])u).{8,}',
|
|
];
|
|
|
|
// login error code, can be matched to the array login_error_msg,
|
|
// which holds the string
|
|
/** @var int */
|
|
private int $login_error = 0;
|
|
/** @var array<mixed> all possible login error conditions */
|
|
private array $login_error_msg = [];
|
|
// this is an array holding all strings & templates passed
|
|
// rom the outside (translation)
|
|
/** @var array<mixed> */
|
|
private array $login_template = [
|
|
'strings' => [],
|
|
'password_change' => '',
|
|
'template' => ''
|
|
];
|
|
|
|
// acl vars
|
|
/** @var array<mixed> */
|
|
private array $acl = [];
|
|
/** @var array<mixed> */
|
|
private array $default_acl_list = [];
|
|
/** @var array<string,int> Reverse list to lookup level from type */
|
|
private array $default_acl_list_type = [];
|
|
/** @var int default ACL level to be based on if nothing set */
|
|
private int $default_acl_level = 0;
|
|
// login html, if we are on an ajax page
|
|
/** @var string|null */
|
|
private ?string $login_html = '';
|
|
/** @var bool */
|
|
private bool $login_is_ajax_page = false;
|
|
|
|
// settings
|
|
/** @var array<string,mixed> options */
|
|
private array $options = [];
|
|
/** @var array<string,string> locale options: locale, domain, encoding (opt), path */
|
|
private array $locale = [
|
|
'locale' => '',
|
|
'domain' => '',
|
|
'encoding' => '',
|
|
'path' => '',
|
|
];
|
|
|
|
/** @var \CoreLibs\Logging\Logging logger */
|
|
public \CoreLibs\Logging\Logging $log;
|
|
/** @var \CoreLibs\DB\IO database */
|
|
public \CoreLibs\DB\IO $db;
|
|
/** @var \CoreLibs\Language\L10n language */
|
|
public \CoreLibs\Language\L10n $l;
|
|
/** @var \CoreLibs\Create\Session session class */
|
|
public \CoreLibs\Create\Session $session;
|
|
|
|
/**
|
|
* constructor, does ALL, opens db, works through connection checks,
|
|
* finishes itself
|
|
*
|
|
* @param \CoreLibs\DB\IO $db Database connection class
|
|
* @param \CoreLibs\Logging\Logging $log Logging class
|
|
* @param \CoreLibs\Create\Session $session Session interface class
|
|
* @param array<string,mixed> $options Login ACL settings
|
|
* $auto_login [default true] DEPRECATED, moved into options
|
|
*/
|
|
public function __construct(
|
|
\CoreLibs\DB\IO $db,
|
|
\CoreLibs\Logging\Logging $log,
|
|
\CoreLibs\Create\Session $session,
|
|
array $options = []
|
|
) {
|
|
// attach db class
|
|
$this->db = $db;
|
|
// attach logger
|
|
$this->log = $log;
|
|
// attach session class
|
|
$this->session = $session;
|
|
|
|
// set and check options
|
|
if (false === $this->loginSetOptions($options)) {
|
|
// on failure, exit
|
|
echo "<b>Could not set options</b>";
|
|
$this->loginTerminate(4000);
|
|
}
|
|
|
|
// string key, msg: string, flag: e (error), o (ok)
|
|
$this->login_error_msg = [
|
|
'0' => [
|
|
'msg' => 'No error',
|
|
'flag' => 'o'
|
|
],
|
|
// actually obsolete
|
|
'100' => [
|
|
'msg' => '[EUID] came in as GET/POST!',
|
|
'flag' => 'e',
|
|
],
|
|
// query errors
|
|
'1009' => [
|
|
'msg' => 'Login query reading failed',
|
|
'flag' => 'e',
|
|
],
|
|
// user not found
|
|
'1010' => [
|
|
'msg' => 'Login Failed - Wrong Username or Password',
|
|
'flag' => 'e'
|
|
],
|
|
// blowfish password wrong
|
|
'1011' => [
|
|
'msg' => 'Login Failed - Wrong Username or Password',
|
|
'flag' => 'e'
|
|
],
|
|
// fallback md5 password wrong
|
|
'1012' => [
|
|
'msg' => 'Login Failed - Wrong Username or Password',
|
|
'flag' => 'e'
|
|
],
|
|
// new password_hash wrong
|
|
'1013' => [
|
|
'msg' => 'Login Failed - Wrong Username or Password',
|
|
'flag' => 'e'
|
|
],
|
|
'1101' => [
|
|
'msg' => 'Login Failed - Login User ID must be validated',
|
|
'flag' => 'e'
|
|
],
|
|
'1102' => [
|
|
'msg' => 'Login Failed - Login User ID is outside valid date range',
|
|
'flag' => 'e'
|
|
],
|
|
'102' => [
|
|
'msg' => 'Login Failed - Please enter username and password',
|
|
'flag' => 'e'
|
|
],
|
|
'103' => [
|
|
'msg' => 'You do not have the rights to access this Page',
|
|
'flag' => 'e'
|
|
],
|
|
'104' => [
|
|
'msg' => 'Login Failed - User not enabled',
|
|
'flag' => 'e'
|
|
],
|
|
'105' => [
|
|
'msg' => 'Login Failed - User is locked',
|
|
'flag' => 'e'
|
|
],
|
|
'106' => [
|
|
'msg' => 'Login Failed - User is deleted',
|
|
'flag' => 'e'
|
|
],
|
|
'107' => [
|
|
'msg' => 'Login Failed - User in locked via date period',
|
|
'flag' => 'e'
|
|
],
|
|
'108' => [
|
|
'msg' => 'Login Failed - User is locked via Login User ID',
|
|
'flag' => 'e'
|
|
],
|
|
'109' => [
|
|
'msg' => 'Check permission query reading failed',
|
|
'flag' => 'e'
|
|
],
|
|
// actually this is an illegal user, but I mask it
|
|
'220' => [
|
|
'msg' => 'Password change - The user could not be found',
|
|
'flag' => 'e'
|
|
],
|
|
'200' => [
|
|
'msg' => 'Password change - Please enter username and old password',
|
|
'flag' => 'e'
|
|
],
|
|
'201' => [
|
|
'msg' => 'Password change - The user could not be found',
|
|
'flag' => 'e'
|
|
],
|
|
'202' => [
|
|
'msg' => 'Password change - The old password is not correct',
|
|
'flag' => 'e'
|
|
],
|
|
'203' => [
|
|
'msg' => 'Password change - Please fill out both new password fields',
|
|
'flag' => 'e'
|
|
],
|
|
'204' => [
|
|
'msg' => 'Password change - The new passwords do not match',
|
|
'flag' => 'e'
|
|
],
|
|
// we should also not here WHAT is valid
|
|
'205' => [
|
|
'msg' => 'Password change - The new password is not in a valid format',
|
|
'flag' => 'e'
|
|
],
|
|
// for OK password change
|
|
'300' => [
|
|
'msg' => 'Password change successful',
|
|
'flag' => 'o'
|
|
],
|
|
// this is bad bad error
|
|
'9999' => [
|
|
'msg' => 'Necessary crypt engine could not be found. Login is impossible',
|
|
'flag' => 'e'
|
|
],
|
|
];
|
|
|
|
// init default ACL list array
|
|
$_SESSION['DEFAULT_ACL_LIST'] = [];
|
|
$_SESSION['DEFAULT_ACL_LIST_TYPE'] = [];
|
|
// read the current edit_access_right list into an array
|
|
$q = "SELECT level, type, name FROM edit_access_right "
|
|
. "WHERE level >= 0 ORDER BY level";
|
|
while (is_array($res = $this->db->dbReturn($q))) {
|
|
// level to description format (numeric)
|
|
$this->default_acl_list[$res['level']] = [
|
|
'type' => $res['type'],
|
|
'name' => $res['name']
|
|
];
|
|
$this->default_acl_list_type[(string)$res['type']] = (int)$res['level'];
|
|
}
|
|
// write that into the session
|
|
$_SESSION['DEFAULT_ACL_LIST'] = $this->default_acl_list;
|
|
$_SESSION['DEFAULT_ACL_LIST_TYPE'] = $this->default_acl_list_type;
|
|
|
|
// this will be deprecated
|
|
if ($this->options['auto_login'] === true) {
|
|
$this->loginMainCall();
|
|
}
|
|
}
|
|
|
|
// *************************************************************************
|
|
// **** PROTECTED INTERNAL
|
|
// *************************************************************************
|
|
|
|
/**
|
|
* Wrapper for exit calls
|
|
*
|
|
* @param int $code
|
|
* @return void
|
|
*/
|
|
protected function loginTerminate($code = 0): void
|
|
{
|
|
exit($code);
|
|
}
|
|
|
|
/**
|
|
* return current page name
|
|
*
|
|
* @return string Current page name
|
|
*/
|
|
protected function loginReadPageName(): string
|
|
{
|
|
// set internal page name as is
|
|
return \CoreLibs\Get\System::getPageName();
|
|
}
|
|
|
|
/**
|
|
* print out login HTML via echo
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function loginPrintLogin(): void
|
|
{
|
|
echo $this->loginGetLoginHTML();
|
|
}
|
|
|
|
// *************************************************************************
|
|
// **** PRIVATE INTERNAL
|
|
// *************************************************************************
|
|
|
|
/**
|
|
* Set options
|
|
* Current allowed:
|
|
* target <string>: site target
|
|
* debug <bool>
|
|
* auto_login <bool>: self start login process
|
|
* db_schema <string>
|
|
* password_min_length <int>
|
|
* default_acl_level <int>
|
|
* logout_target <string>: should default be '' or target path to where
|
|
* can_change <bool>: can change password (NOT IMPLEMENTED)
|
|
* forget_flow <boo>: reset password on forget (NOT IMPLEMENTED)
|
|
* locale_path <string>: absolue path to the locale folder
|
|
* site_locale <string>: what locale to load
|
|
* site_domain <string>: what domain (locale file name) to use
|
|
*
|
|
* @param array<string,mixed> $options Options array from class load
|
|
* @return bool True on ok, False on failure
|
|
*/
|
|
private function loginSetOptions(array $options): bool
|
|
{
|
|
// target and debug flag
|
|
if (
|
|
empty($options['target'])
|
|
) {
|
|
$options['target'] = 'test';
|
|
}
|
|
if (
|
|
empty($options['debug']) ||
|
|
!is_bool($options['debug'])
|
|
) {
|
|
$options['debug'] = false;
|
|
}
|
|
|
|
// AUTO LOGIN
|
|
if (
|
|
!isset($options['auto_login']) ||
|
|
!is_bool($options['auto_login'])
|
|
) {
|
|
// if set to true will run login call during class construction
|
|
$options['auto_login'] = false;
|
|
}
|
|
|
|
// DB SCHEMA
|
|
if (
|
|
empty($options['db_schema']) ||
|
|
// TODO more strict check
|
|
is_string($options['db_schema'])
|
|
) {
|
|
// get scham from db, else fallback to public
|
|
if (!empty($this->db->dbGetSchema(true))) {
|
|
$options['db_schema'] = $this->db->dbGetSchema(true);
|
|
} else {
|
|
$options['db_schema'] = 'public';
|
|
}
|
|
}
|
|
if ($this->db->dbGetSchema() != $options['db_schema']) {
|
|
$this->db->dbSetSchema($options['db_schema']);
|
|
}
|
|
|
|
// MIN PASSWORD LENGTH
|
|
// can only be in length of current defined min/max
|
|
if (
|
|
!empty($options['password_min_lenght']) &&
|
|
!is_numeric($options['password_min_length']) &&
|
|
$options['password_min_length'] >= self::PASSWORD_MIN_LENGTH &&
|
|
$options['password_min_length'] <= self::PASSWORD_MAX_LENGTH
|
|
) {
|
|
if (
|
|
false === $this->loginSetPasswordMinLength(
|
|
(int)$options['password_min_length']
|
|
)
|
|
) {
|
|
$options['password_min_length'] = self::PASSWORD_MIN_LENGTH;
|
|
}
|
|
}
|
|
|
|
// DEFAULT ACL LEVEL
|
|
if (
|
|
!isset($options['default_acl_level']) ||
|
|
!is_numeric($options['default_acl_level']) ||
|
|
$options['default_acl_level'] < 0 || $options['default_acl_level'] > 100
|
|
) {
|
|
$options['default_acl_level'] = 0;
|
|
if (defined('DEFAULT_ACL_LEVEL')) {
|
|
trigger_error(
|
|
'loginMainCall: DEFAULT_ACL_LEVEL should not be used',
|
|
E_USER_DEPRECATED
|
|
);
|
|
$options['default_acl_level'] = DEFAULT_ACL_LEVEL;
|
|
}
|
|
}
|
|
$this->default_acl_level = (int)$options['default_acl_level'];
|
|
|
|
// LOGOUT TARGET
|
|
if (!isset($options['logout_target'])) {
|
|
if (defined('LOGOUT_TARGET')) {
|
|
trigger_error(
|
|
'loginMainCall: LOGOUT_TARGET should not be used',
|
|
E_USER_DEPRECATED
|
|
);
|
|
$options['logout_target'] = LOGOUT_TARGET;
|
|
$this->logout_target = $options['logout_target'];
|
|
}
|
|
}
|
|
|
|
// *** PASSWORD SETTINGS
|
|
// User can change password
|
|
if (
|
|
!isset($options['can_change']) ||
|
|
!is_bool($options['can_change'])
|
|
) {
|
|
$options['can_change'] = false;
|
|
}
|
|
$this->password_change = $options['can_change'];
|
|
// User can trigger a forgot password flow
|
|
if (
|
|
!isset($options['forgot_flow']) ||
|
|
!is_bool($options['forgot_flow'])
|
|
) {
|
|
$options['forgot_flow'] = false;
|
|
}
|
|
$this->password_forgot = $options['forgot_flow'];
|
|
|
|
// *** LANGUAGE
|
|
// LANG: LOCALE PATH
|
|
if (empty($options['locale_path'])) {
|
|
// trigger deprecation error
|
|
trigger_error(
|
|
'loginSetOptions: misssing locale_path entry is deprecated',
|
|
E_USER_DEPRECATED
|
|
);
|
|
// set path
|
|
$options['locale_path'] = BASE . INCLUDES . LOCALE;
|
|
}
|
|
$_SESSION['LOCALE_PATH'] = $options['locale_path'];
|
|
// LANG: LOCALE
|
|
if (empty($options['site_locale'])) {
|
|
trigger_error(
|
|
'loginMainCall: SITE_LOCALE should not be used',
|
|
E_USER_DEPRECATED
|
|
);
|
|
$options['site_locale'] = defined('SITE_LOCALE') && !empty(SITE_LOCALE) ?
|
|
SITE_LOCALE : 'en.UTF-8';
|
|
}
|
|
// LANG: DOMAIN
|
|
if (empty($options['site_domain'])) {
|
|
// we need to get domain set from outside
|
|
$options['site_domain'] = 'admin';
|
|
if (
|
|
defined('SITE_DOMAIN')
|
|
) {
|
|
// trigger deprecation error
|
|
trigger_error(
|
|
'loginSetOptions: misssing site_domain entry is deprecated (SITE_DOMAIN)',
|
|
E_USER_DEPRECATED
|
|
);
|
|
// set domain
|
|
$options['site_domain'] = SITE_DOMAIN;
|
|
} elseif (
|
|
defined('CONTENT_PATH')
|
|
) {
|
|
// trigger deprecation error
|
|
trigger_error(
|
|
'loginSetOptions: misssing site_domain entry is deprecated (CONTENT_PATH)',
|
|
E_USER_DEPRECATED
|
|
);
|
|
$options['set_domain'] = str_replace(DIRECTORY_SEPARATOR, '', CONTENT_PATH);
|
|
}
|
|
}
|
|
$_SESSION['DEFAULT_DOMAIN'] = $options['site_domain'];
|
|
// LANG: ENCODING
|
|
if (empty($options['site_encoding'])) {
|
|
trigger_error(
|
|
'loginMainCall: SITE_ENCODING should not be used',
|
|
E_USER_DEPRECATED
|
|
);
|
|
$options['site_encoding'] = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
|
|
SITE_ENCODING : 'UTF-8';
|
|
}
|
|
|
|
// write array to options
|
|
$this->options = $options;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks for all flags and sets error codes for each
|
|
* In order:
|
|
* delete > enable > lock > period lock > login user id lock
|
|
*
|
|
* @param int $deleted User deleted check
|
|
* @param int $enabled User not enabled check
|
|
* @param int $locked Locked because of too many invalid passwords
|
|
* @param int $locked_period Locked because of time period set
|
|
* @param int $login_user_id_locked Locked from using Login User Id
|
|
* @return bool
|
|
*/
|
|
private function loginValidationCheck(
|
|
int $deleted,
|
|
int $enabled,
|
|
int $locked,
|
|
int $locked_period,
|
|
int $login_user_id_locked
|
|
): bool {
|
|
$validation = false;
|
|
if ($deleted) {
|
|
// user is deleted
|
|
$this->login_error = 106;
|
|
} elseif (!$enabled) {
|
|
// user is not enabled
|
|
$this->login_error = 104;
|
|
} elseif ($locked) {
|
|
// user is locked, either set or auto set
|
|
$this->login_error = 105;
|
|
} elseif ($locked_period) {
|
|
// locked date trigger
|
|
$this->login_error = 107;
|
|
} elseif ($login_user_id_locked) {
|
|
// user is locked, either set or auto set
|
|
$this->login_error = 108;
|
|
} else {
|
|
$validation = true;
|
|
}
|
|
return $validation;
|
|
}
|
|
|
|
/**
|
|
* checks if password is valid, sets internal error login variable
|
|
*
|
|
* @param string $hash password hash
|
|
* @param string $password submitted password
|
|
* @return bool true or false on password ok or not
|
|
*/
|
|
private function loginPasswordCheck(string $hash, string $password = ''): bool
|
|
{
|
|
// check with what kind of prefix the password begins:
|
|
// $2a$ or $2y$: BLOWFISCH
|
|
// $1$: MD5
|
|
// $ and one alphanumeric letter, 13 chars long, but nor $ at the end: STD_DESC
|
|
// if no $ => normal password
|
|
// NOW, if we have a password encoded, but not the correct encoder available, throw special error
|
|
$password_ok = false;
|
|
if (!$password) {
|
|
$password = $this->password;
|
|
}
|
|
// first, errors on missing encryption
|
|
if (
|
|
// below is all deprecated. all the ones below will always be true
|
|
// all the crypt standards are always set
|
|
// FIXME: remove this error code
|
|
/** @phpstan-ignore-next-line Why? */
|
|
(preg_match("/^\\$2(a|y)\\$/", $hash) && CRYPT_BLOWFISH != 1) ||
|
|
/** @phpstan-ignore-next-line Why? */
|
|
(preg_match("/^\\$1\\$/", $hash) && CRYPT_MD5 != 1) ||
|
|
/** @phpstan-ignore-next-line Why? */
|
|
(preg_match("/^\\$[0-9A-Za-z.]{12}$/", $hash) && CRYPT_STD_DES != 1)
|
|
) {
|
|
// this means password cannot be decrypted because of missing crypt methods
|
|
$this->login_error = 9999;
|
|
} elseif (
|
|
preg_match("/^\\$2y\\$/", $hash) &&
|
|
!Password::passwordVerify($password, $hash)
|
|
) {
|
|
// this is the new password hash method, is only $2y$
|
|
// all others are not valid anymore
|
|
$this->login_error = 1013;
|
|
} elseif (
|
|
!preg_match("/^\\$2(a|y)\\$/", $hash) &&
|
|
!preg_match("/^\\$1\\$/", $hash) &&
|
|
!preg_match("/^\\$[0-9A-Za-z.]{12}$/", $hash) &&
|
|
$hash != $password
|
|
) {
|
|
// check old plain password, case sensitive
|
|
$this->login_error = 1012;
|
|
} else {
|
|
// all ok
|
|
$password_ok = true;
|
|
}
|
|
return $password_ok;
|
|
}
|
|
|
|
/**
|
|
* Check if Login User ID is allowed to login
|
|
*
|
|
* @param int $login_user_id_valid_date
|
|
* @param int $login_user_id_revalidate
|
|
* @return bool
|
|
*/
|
|
private function loginLoginUserIdCheck(
|
|
int $login_user_id_valid_date,
|
|
int $login_user_id_revalidate
|
|
): bool {
|
|
$login_id_ok = false;
|
|
if ($login_user_id_revalidate) {
|
|
$this->login_error = 1101;
|
|
} elseif (!$login_user_id_valid_date) {
|
|
$this->login_error = 1102;
|
|
} else {
|
|
$login_id_ok = true;
|
|
}
|
|
return $login_id_ok;
|
|
}
|
|
|
|
/**
|
|
* if user pressed login button this script is called,
|
|
* but only if there is no preview euid set
|
|
*
|
|
* @return void has not return
|
|
*/
|
|
private function loginLoginUser(): void
|
|
{
|
|
// if pressed login at least and is not yet loggined in
|
|
if ($this->euid || (!$this->login && !$this->login_user_id)) {
|
|
return;
|
|
}
|
|
// if not username AND password where given
|
|
// OR no login_user_id
|
|
if (!($this->username && $this->password) && !$this->login_user_id) {
|
|
$this->login_error = 102;
|
|
$this->permission_okay = false;
|
|
return;
|
|
}
|
|
// have to get the global stuff here for setting it later
|
|
// we have to get the themes in here too
|
|
$q = "SELECT eu.edit_user_id, eu.username, eu.password, "
|
|
. "eu.edit_group_id, "
|
|
. "eg.name AS edit_group_name, eu.admin, "
|
|
// additinal acl lists
|
|
. "eu.additional_acl AS user_additional_acl, "
|
|
. "eg.additional_acl AS group_additional_acl, "
|
|
// login error + locked
|
|
. "eu.login_error_count, eu.login_error_date_last, "
|
|
. "eu.login_error_date_first, eu.strict, eu.locked, "
|
|
// date based lock
|
|
. "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, "
|
|
// debug (legacy)
|
|
. "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 "
|
|
. "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, "
|
|
// 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_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, "
|
|
// language
|
|
. "el.short_name AS locale, el.iso_name AS encoding, "
|
|
// levels
|
|
. "eareu.level AS user_level, eareu.type AS user_type, "
|
|
. "eareg.level AS group_level, eareg.type AS group_type, "
|
|
// colors
|
|
. "first.header_color AS first_header_color, "
|
|
. "second.header_color AS second_header_color, second.template "
|
|
. "FROM edit_user eu "
|
|
. "LEFT JOIN edit_scheme second ON "
|
|
. "(second.edit_scheme_id = eu.edit_scheme_id AND second.enabled = 1), "
|
|
. "edit_language el, edit_group eg, "
|
|
. "edit_access_right eareu, "
|
|
. "edit_access_right eareg, "
|
|
. "edit_scheme first "
|
|
. "WHERE first.edit_scheme_id = eg.edit_scheme_id "
|
|
. "AND eu.edit_group_id = eg.edit_group_id "
|
|
. "AND eu.edit_language_id = el.edit_language_id "
|
|
. "AND eu.edit_access_right_id = eareu.edit_access_right_id "
|
|
. "AND eg.edit_access_right_id = eareg.edit_access_right_id "
|
|
. "AND "
|
|
// either login_user_id OR password must be given
|
|
. (!empty($this->login_user_id && empty($this->username)) ?
|
|
// check with login id if set and NO username
|
|
"eu.login_user_id = " . $this->db->dbEscapeLiteral($this->login_user_id) . " " :
|
|
// password match is done in script, against old plain or new blowfish encypted
|
|
"LOWER(username) = " . $this->db->dbEscapeLiteral(strtolower($this->username)) . " "
|
|
);
|
|
// reset any query data that might exist
|
|
$this->db->dbCacheReset($q);
|
|
// never cache return data
|
|
$res = $this->db->dbReturn($q, $this->db::NO_CACHE);
|
|
// query was not run successful
|
|
if (!empty($this->db->dbGetLastError())) {
|
|
$this->login_error = 1009;
|
|
$this->permission_okay = false;
|
|
return;
|
|
} elseif (!is_array($res)) {
|
|
// username is wrong, but we throw for wrong username
|
|
// and wrong password the same error
|
|
$this->login_error = 1010;
|
|
$this->permission_okay = false;
|
|
return;
|
|
}
|
|
// if login errors is half of max errors and the last login error
|
|
// was less than 10s ago, forbid any new login try
|
|
|
|
// check flow
|
|
// - user is enabled
|
|
// - user is not locked
|
|
// - password is readable
|
|
// - encrypted password matches
|
|
// - plain password matches
|
|
if (
|
|
!$this->loginValidationCheck(
|
|
(int)$res['deleted'],
|
|
(int)$res['enabled'],
|
|
(int)$res['locked'],
|
|
(int)$res['locked_period'],
|
|
(int)$res['login_user_id_locked']
|
|
)
|
|
) {
|
|
// error set in method (104, 105, 106, 107, 108)
|
|
} elseif (
|
|
empty($this->username) &&
|
|
!empty($this->login_user_id) &&
|
|
!$this->loginLoginUserIdCheck(
|
|
(int)$res['login_user_id_valid_date'],
|
|
(int)$res['login_user_id_revalidate']
|
|
)
|
|
) {
|
|
// check done in loginLoginIdCheck method
|
|
// aborts on must revalidate and not valid (date range)
|
|
} elseif (
|
|
!empty($this->username) &&
|
|
!$this->loginPasswordCheck($res['password'])
|
|
) {
|
|
// none to be set, set in login password check
|
|
// this is not valid password input error here
|
|
// all error codes are set in loginPasswordCheck method
|
|
// also valid if login_user_id is ok
|
|
} else {
|
|
// check if the current password is an invalid hash and do a rehash and set password
|
|
// $this->debug('LOGIN', 'Hash: '.$res['password'].' -> VERIFY: '
|
|
// .($Password::passwordVerify($this->password, $res['password']) ? 'OK' : 'FAIL')
|
|
// .' => HASH: '.(Password::passwordRehashCheck($res['password']) ? 'NEW NEEDED' : 'OK'));
|
|
if (Password::passwordRehashCheck($res['password'])) {
|
|
// update password hash to new one now
|
|
$q = "UPDATE edit_user "
|
|
. "SET password = '" . $this->db->dbEscapeString(Password::passwordSet($this->password))
|
|
. "' WHERE edit_user_id = " . $res['edit_user_id'];
|
|
$this->db->dbExec($q);
|
|
}
|
|
// normal user processing
|
|
// set class var and session var
|
|
$_SESSION['EUID'] = $this->euid = (int)$res['edit_user_id'];
|
|
// 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']);
|
|
// general info for user logged in
|
|
$_SESSION['USER_NAME'] = $res['username'];
|
|
$_SESSION['ADMIN'] = $res['admin'];
|
|
$_SESSION['GROUP_NAME'] = $res['edit_group_name'];
|
|
$_SESSION['USER_ACL_LEVEL'] = $res['user_level'];
|
|
$_SESSION['USER_ACL_TYPE'] = $res['user_type'];
|
|
$_SESSION['USER_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['user_additional_acl']);
|
|
$_SESSION['GROUP_ACL_LEVEL'] = $res['group_level'];
|
|
$_SESSION['GROUP_ACL_TYPE'] = $res['group_type'];
|
|
$_SESSION['GROUP_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['group_additional_acl']);
|
|
// deprecated TEMPLATE setting
|
|
$_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : '';
|
|
$_SESSION['HEADER_COLOR'] = !empty($res['second_header_color']) ?
|
|
$res['second_header_color'] :
|
|
$res['first_header_color'];
|
|
// missing # before, this is for legacy data, will be deprecated
|
|
if (preg_match("/^[\dA-Fa-f]{6,8}$/", $_SESSION['HEADER_COLOR'])) {
|
|
$_SESSION['HEADER_COLOR'] = '#' . $_SESSION['HEADER_COLOR'];
|
|
}
|
|
// TODO: make sure that header color is valid:
|
|
// # + 6 hex
|
|
// # + 8 hex (alpha)
|
|
// rgb(), rgba(), hsl(), hsla()
|
|
// rgb: nnn.n for each
|
|
// hsl: nnn.n for first, nnn.n% for 2nd, 3rd
|
|
// Check\Colors::validateColor()
|
|
// LANGUAGE/LOCALE/ENCODING:
|
|
$_SESSION['LANG'] = $res['locale'] ?? 'en';
|
|
$_SESSION['DEFAULT_CHARSET'] = $res['encoding'] ?? 'UTF-8';
|
|
$_SESSION['DEFAULT_LOCALE'] = $_SESSION['LANG']
|
|
. '.' . strtoupper($_SESSION['DEFAULT_CHARSET']);
|
|
$_SESSION['DEFAULT_LANG'] = $_SESSION['LANG'] . '_'
|
|
. strtolower(str_replace('-', '', $_SESSION['DEFAULT_CHARSET']));
|
|
// reset any login error count for this user
|
|
if ($res['login_error_count'] > 0) {
|
|
$q = "UPDATE edit_user "
|
|
. "SET login_error_count = 0, login_error_date_last = NULL, "
|
|
. "login_error_date_first = NULL "
|
|
. "WHERE edit_user_id = " . $res['edit_user_id'];
|
|
$this->db->dbExec($q);
|
|
}
|
|
$edit_page_ids = [];
|
|
$pages = [];
|
|
$pages_acl = [];
|
|
// set pages access
|
|
$q = "SELECT ep.edit_page_id, ep.cuid, epca.cuid AS content_alias_uid, "
|
|
. "ep.hostname, ep.filename, ep.name AS edit_page_name, "
|
|
. "ep.order_number AS edit_page_order, ep.menu, "
|
|
. "ep.popup, ep.popup_x, ep.popup_y, ep.online, ear.level, ear.type "
|
|
. "FROM edit_page ep "
|
|
. "LEFT JOIN edit_page epca ON (epca.edit_page_id = ep.content_alias_edit_page_id)"
|
|
. ", edit_page_access epa, edit_access_right ear "
|
|
. "WHERE ep.edit_page_id = epa.edit_page_id "
|
|
. "AND ear.edit_access_right_id = epa.edit_access_right_id "
|
|
. "AND epa.enabled = 1 AND epa.edit_group_id = " . $res["edit_group_id"] . " "
|
|
. "ORDER BY ep.order_number";
|
|
while ($res = $this->db->dbReturn($q)) {
|
|
if (!is_array($res)) {
|
|
break;
|
|
}
|
|
// page id array for sub data readout
|
|
$edit_page_ids[$res['edit_page_id']] = $res['cuid'];
|
|
// create the array for pages
|
|
$pages[$res['cuid']] = [
|
|
'edit_page_id' => $res['edit_page_id'],
|
|
'cuid' => $res['cuid'],
|
|
// for reference of content data on a differen page
|
|
'content_alias_uid' => $res['content_alias_uid'],
|
|
'hostname' => $res['hostname'],
|
|
'filename' => $res['filename'],
|
|
'page_name' => $res['edit_page_name'],
|
|
'order' => $res['edit_page_order'],
|
|
'menu' => $res['menu'],
|
|
'popup' => $res['popup'],
|
|
'popup_x' => $res['popup_x'],
|
|
'popup_y' => $res['popup_y'],
|
|
'online' => $res['online'],
|
|
'acl_level' => $res['level'],
|
|
'acl_type' => $res['type'],
|
|
'query' => [],
|
|
'visible' => []
|
|
];
|
|
// make reference filename -> level
|
|
$pages_acl[$res['filename']] = $res['level'];
|
|
} // for each page
|
|
// get the visible groups for all pages and write them to the pages
|
|
$q = "SELECT epvg.edit_page_id, name, flag "
|
|
. "FROM edit_visible_group evp, edit_page_visible_group epvg "
|
|
. "WHERE evp.edit_visible_group_id = epvg.edit_visible_group_id "
|
|
. "AND epvg.edit_page_id IN (" . join(', ', array_keys($edit_page_ids)) . ") "
|
|
. "ORDER BY epvg.edit_page_id";
|
|
while (is_array($res = $this->db->dbReturn($q))) {
|
|
$pages[$edit_page_ids[$res['edit_page_id']]]['visible'][$res['name']] = $res['flag'];
|
|
}
|
|
// get the same for the query strings
|
|
$q = "SELECT eqs.edit_page_id, name, value, dynamic FROM edit_query_string eqs "
|
|
. "WHERE enabled = 1 AND edit_page_id "
|
|
. "IN (" . join(', ', array_keys($edit_page_ids)) . ") "
|
|
. "ORDER BY eqs.edit_page_id";
|
|
while (is_array($res = $this->db->dbReturn($q))) {
|
|
$pages[$edit_page_ids[$res['edit_page_id']]]['query'][] = [
|
|
'name' => $res['name'],
|
|
'value' => $res['value'],
|
|
'dynamic' => $res['dynamic']
|
|
];
|
|
}
|
|
// get the page content and add them to the page
|
|
$q = "SELECT epc.edit_page_id, epc.name, epc.uid, epc.order_number, "
|
|
. "epc.online, ear.level, ear.type "
|
|
. "FROM edit_page_content epc, edit_access_right ear "
|
|
. "WHERE epc.edit_access_right_id = ear.edit_access_right_id AND "
|
|
. "epc.edit_page_id IN (" . join(', ', array_keys($edit_page_ids)) . ") "
|
|
. "ORDER BY epc.order_number";
|
|
while (is_array($res = $this->db->dbReturn($q))) {
|
|
$pages[$edit_page_ids[$res['edit_page_id']]]['content'][$res['uid']] = [
|
|
'name' => $res['name'],
|
|
'uid' => $res['uid'],
|
|
'online' => $res['online'],
|
|
'order' => $res['order_number'],
|
|
// access name and level
|
|
'acl_type' => $res['type'],
|
|
'acl_level' => $res['level']
|
|
];
|
|
}
|
|
// write back the pages data to the output array
|
|
$_SESSION['PAGES'] = $pages;
|
|
$_SESSION['PAGES_ACL_LEVEL'] = $pages_acl;
|
|
// load the edit_access user rights
|
|
$q = "SELECT ea.edit_access_id, level, type, ea.name, "
|
|
. "ea.color, ea.uid, edit_default, ea.additional_acl "
|
|
. "FROM edit_access_user eau, edit_access_right ear, edit_access ea "
|
|
. "WHERE eau.edit_access_id = ea.edit_access_id "
|
|
. "AND eau.edit_access_right_id = ear.edit_access_right_id "
|
|
. "AND eau.enabled = 1 AND edit_user_id = " . $this->euid . " "
|
|
. "ORDER BY ea.name";
|
|
$unit_access = [];
|
|
$eauid = [];
|
|
$unit_acl = [];
|
|
while (is_array($res = $this->db->dbReturn($q))) {
|
|
// read edit access data fields and drop them into the unit access array
|
|
$q_sub = "SELECT name, value "
|
|
. "FROM edit_access_data "
|
|
. "WHERE enabled = 1 AND edit_access_id = " . $res['edit_access_id'];
|
|
$ea_data = [];
|
|
while (is_array($res_sub = $this->db->dbReturn($q_sub))) {
|
|
$ea_data[$res_sub['name']] = $res_sub['value'];
|
|
}
|
|
// build master unit array
|
|
$unit_access[$res['edit_access_id']] = [
|
|
'id' => (int)$res['edit_access_id'],
|
|
'acl_level' => $res['level'],
|
|
'acl_type' => $res['type'],
|
|
'name' => $res['name'],
|
|
'uid' => $res['uid'],
|
|
'color' => $res['color'],
|
|
'default' => $res['edit_default'],
|
|
'additional_acl' => Json::jsonConvertToArray($res['additional_acl']),
|
|
'data' => $ea_data
|
|
];
|
|
// set the default unit
|
|
if ($res['edit_default']) {
|
|
$_SESSION['UNIT_DEFAULT'] = (int)$res['edit_access_id'];
|
|
}
|
|
$_SESSION['UNIT_UID'][$res['uid']] = (int)$res['edit_access_id'];
|
|
// sub arrays for simple access
|
|
array_push($eauid, $res['edit_access_id']);
|
|
$unit_acl[$res['edit_access_id']] = $res['level'];
|
|
}
|
|
$_SESSION['UNIT'] = $unit_access;
|
|
$_SESSION['UNIT_ACL_LEVEL'] = $unit_acl;
|
|
$_SESSION['EAID'] = $eauid;
|
|
} // user has permission to THIS page
|
|
} // user was not enabled or other login error
|
|
if ($this->login_error && is_array($res)) {
|
|
$login_error_date_first = '';
|
|
if ($res['login_error_count'] == 0) {
|
|
$login_error_date_first = ", login_error_date_first = NOW()";
|
|
}
|
|
// update login error count for this user
|
|
$q = "UPDATE edit_user "
|
|
. "SET login_error_count = login_error_count + 1, "
|
|
. "login_error_date_last = NOW() " . $login_error_date_first . " "
|
|
. "WHERE edit_user_id = " . $res['edit_user_id'];
|
|
$this->db->dbExec($q);
|
|
// totally lock the user if error max is reached
|
|
if (
|
|
$this->max_login_error_count != -1 &&
|
|
$res['login_error_count'] + 1 > $this->max_login_error_count
|
|
) {
|
|
// do some alert reporting in case this error is too big
|
|
// if strict is set, lock this user
|
|
// this needs manual unlocking by an admin user
|
|
if ($res['strict'] && !in_array($this->username, $this->lock_deny_users)) {
|
|
$q = "UPDATE edit_user SET locked = 1 WHERE edit_user_id = " . $res['edit_user_id'];
|
|
}
|
|
}
|
|
}
|
|
// if there was an login error, show login screen
|
|
if ($this->login_error) {
|
|
// reset the perm var, to confirm logout
|
|
$this->permission_okay = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* sets all the basic ACLs
|
|
* init set the basic acl the user has, based on the following rules
|
|
* - init set from config DEFAULT ACL
|
|
* - if page ACL is set, it overrides the default ACL
|
|
* - if group ACL is set, it overrides the page ACL
|
|
* - if user ACL is set, it overrides the group ACL
|
|
* set the page ACL
|
|
* - default ACL set
|
|
* - set group ACL if not default overrides default ACL
|
|
* - set page ACL if not default overrides group ACL
|
|
* set edit access ACL and set default edit access group
|
|
* - if an account ACL is set, set this parallel, account ACL overrides user ACL if it applies
|
|
* - if edit access ACL level is set, use this, else use page
|
|
* set all base ACL levels as a list keyword -> ACL number
|
|
*
|
|
* @return void has no return
|
|
*/
|
|
private function loginSetAcl(): void
|
|
{
|
|
// only set acl if we have permission okay
|
|
if (!$this->permission_okay) {
|
|
return;
|
|
}
|
|
// username (login), group name
|
|
$this->acl['user_name'] = $_SESSION['USER_NAME'];
|
|
$this->acl['group_name'] = $_SESSION['GROUP_NAME'];
|
|
// set additional acl
|
|
$this->acl['additional_acl'] = [
|
|
'user' => $_SESSION['USER_ADDITIONAL_ACL'],
|
|
'group' => $_SESSION['GROUP_ADDITIONAL_ACL'],
|
|
];
|
|
// we start with the default acl
|
|
$this->acl['base'] = $this->default_acl_level;
|
|
|
|
// set admin flag and base to 100
|
|
if (!empty($_SESSION['ADMIN'])) {
|
|
$this->acl['admin'] = 1;
|
|
$this->acl['base'] = 100;
|
|
} else {
|
|
$this->acl['admin'] = 0;
|
|
// now go throw the flow and set the correct ACL
|
|
// user > page > group
|
|
// group ACL 0
|
|
if ($_SESSION['GROUP_ACL_LEVEL'] != -1) {
|
|
$this->acl['base'] = $_SESSION['GROUP_ACL_LEVEL'];
|
|
}
|
|
// page ACL 1
|
|
if (
|
|
isset($_SESSION['PAGES_ACL_LEVEL'][$this->page_name]) &&
|
|
$_SESSION['PAGES_ACL_LEVEL'][$this->page_name] != -1
|
|
) {
|
|
$this->acl['base'] = $_SESSION['PAGES_ACL_LEVEL'][$this->page_name];
|
|
}
|
|
// user ACL 2
|
|
if ($_SESSION['USER_ACL_LEVEL'] != -1) {
|
|
$this->acl['base'] = $_SESSION['USER_ACL_LEVEL'];
|
|
}
|
|
}
|
|
$_SESSION['BASE_ACL_LEVEL'] = $this->acl['base'];
|
|
|
|
// set the current page acl
|
|
// start with base acl
|
|
// set group if not -1, overrides default
|
|
// set page if not -1, overrides group set
|
|
$this->acl['page'] = $this->acl['base'];
|
|
if ($_SESSION['GROUP_ACL_LEVEL'] != -1) {
|
|
$this->acl['page'] = $_SESSION['GROUP_ACL_LEVEL'];
|
|
}
|
|
if (
|
|
isset($_SESSION['PAGES_ACL_LEVEL'][$this->page_name]) &&
|
|
$_SESSION['PAGES_ACL_LEVEL'][$this->page_name] != -1
|
|
) {
|
|
$this->acl['page'] = $_SESSION['PAGES_ACL_LEVEL'][$this->page_name];
|
|
}
|
|
|
|
$this->acl['unit_id'] = null;
|
|
$this->acl['unit_name'] = null;
|
|
$this->acl['unit_uid'] = null;
|
|
$this->acl['unit'] = [];
|
|
$this->acl['unit_detail'] = [];
|
|
|
|
// PER ACCOUNT (UNIT/edit access)->
|
|
foreach ($_SESSION['UNIT'] as $ea_id => $unit) {
|
|
// if admin flag is set, all units are set to 100
|
|
if (!empty($this->acl['admin'])) {
|
|
$this->acl['unit'][$ea_id] = $this->acl['base'];
|
|
} else {
|
|
if ($unit['acl_level'] != -1) {
|
|
$this->acl['unit'][$ea_id] = $unit['acl_level'];
|
|
} else {
|
|
$this->acl['unit'][$ea_id] = $this->acl['base'];
|
|
}
|
|
}
|
|
// detail name/level set
|
|
$this->acl['unit_detail'][$ea_id] = [
|
|
'name' => $unit['name'],
|
|
'uid' => $unit['uid'],
|
|
'level' => $this->default_acl_list[$this->acl['unit'][$ea_id]]['name'] ?? -1,
|
|
'default' => $unit['default'],
|
|
'data' => $unit['data'],
|
|
'additional_acl' => $unit['additional_acl']
|
|
];
|
|
// set default
|
|
if (!empty($unit['default'])) {
|
|
$this->acl['unit_id'] = $unit['id'];
|
|
$this->acl['unit_name'] = $unit['name'];
|
|
$this->acl['unit_uid'] = $unit['uid'];
|
|
}
|
|
}
|
|
// flag if to show extra edit access drop downs (because user has multiple groups assigned)
|
|
if (count($_SESSION['UNIT']) > 1) {
|
|
$this->acl['show_ea_extra'] = true;
|
|
} else {
|
|
$this->acl['show_ea_extra'] = false;
|
|
}
|
|
// set the default edit access
|
|
$this->acl['default_edit_access'] = $_SESSION['UNIT_DEFAULT'] ?? null;
|
|
// integrate the type acl list, but only for the keyword -> level
|
|
$this->acl['min'] = $this->default_acl_list_type;
|
|
// set the full acl list too (lookup level number and get level data)
|
|
$this->acl['acl_list'] = $this->default_acl_list;
|
|
// debug
|
|
// $this->debug('ACL', $this->print_ar($this->acl));
|
|
}
|
|
|
|
/**
|
|
* set locale
|
|
* if invalid, set to empty string
|
|
*
|
|
* @return void
|
|
*/
|
|
private function loginSetLocale(): void
|
|
{
|
|
// ** LANGUAGE SET AFTER LOGIN **
|
|
// set the locale
|
|
if (
|
|
!empty($_SESSION['DEFAULT_LOCALE']) &&
|
|
preg_match("/^[-A-Za-z0-9_.@]+$/", $_SESSION['DEFAULT_LOCALE'])
|
|
) {
|
|
$locale = $_SESSION['DEFAULT_LOCALE'];
|
|
} elseif (
|
|
!preg_match("/^[-A-Za-z0-9_.@]+$/", $this->options['site_locale'])
|
|
) {
|
|
$locale = $this->options['site_locale'];
|
|
} else {
|
|
$locale = '';
|
|
}
|
|
// set the charset
|
|
preg_match('/(?:\\.(?P<charset>[-A-Za-z0-9_]+))/', $locale, $matches);
|
|
$locale_encoding = $matches['charset'] ?? '';
|
|
if (!empty($locale_encoding)) {
|
|
$encoding = strtoupper($locale_encoding);
|
|
} elseif (
|
|
!empty($_SESSION['DEFAULT_CHARSET']) &&
|
|
preg_match("/^[-A-Za-z0-9_]+$/", $_SESSION['DEFAULT_CHARSET'])
|
|
) {
|
|
$encoding = $_SESSION['DEFAULT_CHARSET'];
|
|
} elseif (
|
|
!preg_match("/^[-A-Za-z0-9_]+$/", $this->options['site_encoding'])
|
|
) {
|
|
$encoding = $this->options['site_encoding'];
|
|
} else {
|
|
$encoding = '';
|
|
}
|
|
// check domain
|
|
$domain = $this->options['site_domain'];
|
|
if (
|
|
!preg_match("/^\w+$/", $this->options['site_domain'])
|
|
) {
|
|
$domain = '';
|
|
}
|
|
$path = $this->options['locale_path'];
|
|
if (!is_dir($path)) {
|
|
$path = '';
|
|
}
|
|
// domain and path are a must set from class options
|
|
$this->locale = [
|
|
'locale' => $locale,
|
|
'domain' => $domain,
|
|
'encoding' => $encoding,
|
|
'path' => $path,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* checks if the password is in a valid format
|
|
*
|
|
* @param string $password the new password
|
|
* @return bool true or false if valid password or not
|
|
*/
|
|
private function loginPasswordChangeValidPassword(string $password): bool
|
|
{
|
|
$is_valid_password = true;
|
|
// check for valid in regex arrays in list
|
|
if (is_array($this->password_valid_chars)) {
|
|
foreach ($this->password_valid_chars as $password_valid_chars) {
|
|
if (!preg_match("/$password_valid_chars/", $password)) {
|
|
$is_valid_password = false;
|
|
}
|
|
}
|
|
}
|
|
// check for min length
|
|
if (
|
|
strlen($password) < $this->password_min_length ||
|
|
strlen($password) > $this->password_max_length
|
|
) {
|
|
$is_valid_password = false;
|
|
}
|
|
return $is_valid_password;
|
|
}
|
|
|
|
/**
|
|
* dummy declare for password forget
|
|
*
|
|
* @return void has no return
|
|
*/
|
|
private function loginPasswordForgot(): void
|
|
{
|
|
// will do some password recovert, eg send email
|
|
}
|
|
|
|
/**
|
|
* changes a user password
|
|
*
|
|
* @return void has no return
|
|
*/
|
|
private function loginPasswordChange(): void
|
|
{
|
|
// only continue if password change button pressed
|
|
if (!$this->change_password) {
|
|
return;
|
|
}
|
|
$event = 'Password Change';
|
|
$data = '';
|
|
// check that given username is NOT in the deny list, else silent skip (with error log)
|
|
if (!in_array($this->pw_username, $this->pw_change_deny_users)) {
|
|
// init the edit user id variable
|
|
$edit_user_id = '';
|
|
// cehck if either username or old password is not set
|
|
if (!$this->pw_username || !$this->pw_old_password) {
|
|
$this->login_error = 200;
|
|
$data = 'Missing username or old password.';
|
|
}
|
|
// check user exist, if not -> error
|
|
if (!$this->login_error) {
|
|
$q = "SELECT edit_user_id "
|
|
. "FROM edit_user "
|
|
. "WHERE enabled = 1 "
|
|
. "AND username = '" . $this->db->dbEscapeString($this->pw_username) . "'";
|
|
$res = $this->db->dbReturnRow($q);
|
|
if (
|
|
!is_array($res) ||
|
|
empty($res['edit_user_id'])
|
|
) {
|
|
// username wrong
|
|
$this->login_error = 201;
|
|
$data = 'User could not be found';
|
|
}
|
|
}
|
|
// check old passwords match -> error
|
|
if (!$this->login_error) {
|
|
$q = "SELECT edit_user_id, password "
|
|
. "FROM edit_user "
|
|
. "WHERE enabled = 1 "
|
|
. "AND username = '" . $this->db->dbEscapeString($this->pw_username) . "'";
|
|
$edit_user_id = '';
|
|
$res = $this->db->dbReturnRow($q);
|
|
if (is_array($res)) {
|
|
$edit_user_id = $res['edit_user_id'];
|
|
}
|
|
if (
|
|
!is_array($res) ||
|
|
empty($res['edit_user_id']) ||
|
|
!$this->loginPasswordCheck(
|
|
$res['old_password_hash'],
|
|
$this->pw_old_password
|
|
)
|
|
) {
|
|
// old password wrong
|
|
$this->login_error = 202;
|
|
$data = 'The old password does not match';
|
|
}
|
|
}
|
|
// check if new passwords were filled out -> error
|
|
if (!$this->login_error) {
|
|
if (!$this->pw_new_password || !$this->pw_new_password_confirm) {
|
|
$this->login_error = 203;
|
|
$data = 'Missing new password or new password confirm.';
|
|
}
|
|
}
|
|
// check new passwords both match -> error
|
|
if (!$this->login_error) {
|
|
if ($this->pw_new_password != $this->pw_new_password_confirm) {
|
|
$this->login_error = 204;
|
|
$data = 'The new passwords do not match';
|
|
}
|
|
}
|
|
// password shall match to something in minimum length or form
|
|
if (!$this->login_error) {
|
|
if (!$this->loginPasswordChangeValidPassword($this->pw_new_password)) {
|
|
$this->login_error = 205;
|
|
$data = 'The new password string is not valid';
|
|
}
|
|
}
|
|
// no error change this users password
|
|
if (!$this->login_error && $edit_user_id) {
|
|
// update the user (edit_user_id) with the new password
|
|
$q = "UPDATE edit_user "
|
|
. "SET password = "
|
|
. "'" . $this->db->dbEscapeString(Password::passwordSet($this->pw_new_password)) . "' "
|
|
. "WHERE edit_user_id = " . $edit_user_id;
|
|
$this->db->dbExec($q);
|
|
$data = 'Password change for user "' . $this->pw_username . '"';
|
|
$this->password_change_ok = true;
|
|
}
|
|
} else {
|
|
// illegal user error
|
|
$this->login_error = 220;
|
|
$data = 'Illegal user for password change: ' . $this->pw_username;
|
|
}
|
|
// log this password change attempt
|
|
$this->writeLog($event, $data, $this->login_error, $this->pw_username);
|
|
}
|
|
|
|
/**
|
|
* creates the login html part if no permission (error) is set
|
|
* this does not print anything yet
|
|
*
|
|
* @return string|null html data for login page, or null for nothing
|
|
*/
|
|
private function loginCreateLoginHTML(): ?string
|
|
{
|
|
$html_string = null;
|
|
// if permission is ok, return null
|
|
if ($this->permission_okay) {
|
|
return $html_string;
|
|
}
|
|
// set the templates now
|
|
$this->loginSetTemplates();
|
|
// if there is a global logout target ...
|
|
if (file_exists($this->logout_target)) {
|
|
$LOGOUT_TARGET = $this->logout_target;
|
|
} else {
|
|
$LOGOUT_TARGET = '';
|
|
}
|
|
|
|
$html_string = (string)$this->login_template['template'];
|
|
|
|
$locales = $this->l->parseLocale($this->l->getLocale());
|
|
$this->login_template['strings']['LANGUAGE'] = $locales['lang'] ?? 'en';
|
|
|
|
// if password change is okay
|
|
if ($this->password_change) {
|
|
$html_string_password_change = $this->login_template['password_change'];
|
|
|
|
// pre change the data in the PASSWORD_CHANGE_DIV first
|
|
foreach ($this->login_template['strings'] as $string => $data) {
|
|
if ($data) {
|
|
$html_string_password_change = str_replace(
|
|
'{' . $string . '}',
|
|
$data,
|
|
$html_string_password_change
|
|
);
|
|
}
|
|
}
|
|
// print error messagae
|
|
if ($this->login_error) {
|
|
$html_string_password_change = str_replace(
|
|
'{ERROR_MSG}',
|
|
$this->loginGetErrorMsg($this->login_error) . '<br>',
|
|
$html_string_password_change
|
|
);
|
|
} else {
|
|
$html_string_password_change = str_replace(
|
|
'{ERROR_MSG}',
|
|
'<br>',
|
|
$html_string_password_change
|
|
);
|
|
}
|
|
// if pw change action, show the float again
|
|
if ($this->change_password && !$this->password_change_ok) {
|
|
$html_string_password_change = str_replace(
|
|
'{PASSWORD_CHANGE_SHOW}',
|
|
'<script language="JavaScript">'
|
|
. 'ShowHideDiv(\'pw_change_div\');</script>',
|
|
$html_string_password_change
|
|
);
|
|
} else {
|
|
$html_string_password_change = str_replace(
|
|
'{PASSWORD_CHANGE_SHOW}',
|
|
'',
|
|
$html_string_password_change
|
|
);
|
|
}
|
|
$this->login_template['strings']['PASSWORD_CHANGE_DIV'] = $html_string_password_change;
|
|
}
|
|
|
|
// put in the logout redirect string
|
|
if ($this->logout && $LOGOUT_TARGET) {
|
|
$html_string = str_replace(
|
|
'{LOGOUT_TARGET}',
|
|
'<meta http-equiv="refresh" content="0; URL=' . $LOGOUT_TARGET . '">',
|
|
$html_string
|
|
);
|
|
} else {
|
|
$html_string = str_replace('{LOGOUT_TARGET}', '', $html_string);
|
|
}
|
|
|
|
// print error messagae
|
|
if ($this->login_error) {
|
|
$html_string = str_replace(
|
|
'{ERROR_MSG}',
|
|
$this->loginGetErrorMsg($this->login_error) . '<br>',
|
|
$html_string
|
|
);
|
|
} elseif ($this->password_change_ok && $this->password_change) {
|
|
$html_string = str_replace(
|
|
'{ERROR_MSG}',
|
|
$this->loginGetErrorMsg(300) . '<br>',
|
|
$html_string
|
|
);
|
|
} else {
|
|
$html_string = str_replace('{ERROR_MSG}', '<br>', $html_string);
|
|
}
|
|
|
|
// create the replace array context
|
|
foreach ($this->login_template['strings'] as $string => $data) {
|
|
$html_string = str_replace('{' . $string . '}', $data, $html_string);
|
|
}
|
|
// return the created HTML here
|
|
return $html_string;
|
|
}
|
|
|
|
/**
|
|
* last function called, writes log and prints out error msg and
|
|
* exists script if permission 0
|
|
*
|
|
* @return bool true on permission ok, false on permission wrong
|
|
*/
|
|
private function loginCloseClass(): bool
|
|
{
|
|
// write to LOG table ...
|
|
if ($this->login_error || $this->login || $this->logout) {
|
|
$username = '';
|
|
// $password = '';
|
|
// set event
|
|
if ($this->login) {
|
|
$event = 'Login';
|
|
} elseif ($this->logout) {
|
|
$event = 'Logout';
|
|
} else {
|
|
$event = 'No Permission';
|
|
}
|
|
// prepare for log
|
|
if ($this->euid) {
|
|
// get user from user table
|
|
$q = "SELECT username FROM edit_user WHERE edit_user_id = " . $this->euid;
|
|
$username = '';
|
|
if (is_array($res = $this->db->dbReturnRow($q))) {
|
|
$username = $res['username'];
|
|
}
|
|
} // if euid is set, get username (or try)
|
|
$this->writeLog($event, '', $this->login_error, $username);
|
|
} // write log under certain settings
|
|
// now close DB connection
|
|
// $this->error_msg = $this->_login();
|
|
if (!$this->permission_okay) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* checks if there are external templates, if not uses internal fallback ones
|
|
*
|
|
* @return void has no return
|
|
*/
|
|
private function loginSetTemplates(): void
|
|
{
|
|
$strings = [
|
|
'HTML_TITLE' => $this->l->__('LOGIN'),
|
|
'TITLE' => $this->l->__('LOGIN'),
|
|
'USERNAME' => $this->l->__('Username'),
|
|
'PASSWORD' => $this->l->__('Password'),
|
|
'LOGIN' => $this->l->__('Login'),
|
|
'ERROR_MSG' => '',
|
|
'LOGOUT_TARGET' => '',
|
|
'PASSWORD_CHANGE_BUTTON_VALUE' => $this->l->__('Change Password')
|
|
];
|
|
|
|
// if password change is okay
|
|
if ($this->password_change) {
|
|
$strings = array_merge($strings, [
|
|
'TITLE_PASSWORD_CHANGE' => 'Change Password for User',
|
|
'OLD_PASSWORD' => $this->l->__('Old Password'),
|
|
'NEW_PASSWORD' => $this->l->__('New Password'),
|
|
'NEW_PASSWORD_CONFIRM' => $this->l->__('New Password confirm'),
|
|
'CLOSE' => $this->l->__('Close'),
|
|
'JS_SHOW_HIDE' => "function ShowHideDiv(id) { "
|
|
. "element = document.getElementById(id); "
|
|
. "if (element.className == 'visible' || !element.className) element.className = 'hidden'; "
|
|
. "else element.className = 'visible'; }",
|
|
'PASSWORD_CHANGE_BUTTON' => '<input type="button" name="pw_change" value="'
|
|
. $strings['PASSWORD_CHANGE_BUTTON_VALUE']
|
|
. '" OnClick="ShowHideDiv(\'pw_change_div\');">'
|
|
]);
|
|
// TODO: submit or JS to set target page as ajax call
|
|
// NOTE: for the HTML block I ignore line lengths
|
|
// phpcs:disable
|
|
$this->login_template['password_change'] = <<<HTML
|
|
<div id="pw_change_div" class="hidden" style="position: absolute; top: 30px; left: 50px; width: 400px; height: 220px; background-color: white; border: 1px solid black; padding: 25px;">
|
|
<table>
|
|
<tr><td class="norm" align="center" colspan="2"><h3>{TITLE_PASSWORD_CHANGE}</h3></td></tr>
|
|
<tr><td class="norm" colspan="2">{ERROR_MSG}</td></tr>
|
|
<tr><td class="norm" align="right">{USERNAME}</td><td><input type="text" name="pw_username" value=""></td></tr>
|
|
<tr><td class="norm" align="right">{OLD_PASSWORD}</td>
|
|
<td><input type="password" name="pw_old_password" value=""></td></tr>
|
|
<tr><td class="norm" align="right">{NEW_PASSWORD}</td>
|
|
<td><input type="password" name="pw_new_password" value=""></td></tr>
|
|
<tr><td class="norm" align="right">{NEW_PASSWORD_CONFIRM}</td>
|
|
<td><input type="password" name="pw_new_password_confirm" value=""></td></tr>
|
|
<tr><td></td>
|
|
<td><input type="submit" name="change_password" value="{PASSWORD_CHANGE_BUTTON_VALUE}">
|
|
<input type="button" name="pw_change" value="{CLOSE}" OnClick="ShowHideDiv('pw_change_div');"></td></tr>
|
|
</table>
|
|
</div>
|
|
{PASSWORD_CHANGE_SHOW}
|
|
HTML;
|
|
// phpcs:enable
|
|
}
|
|
if ($this->password_forgot) {
|
|
}
|
|
if (!$this->password_change && !$this->password_forgot) {
|
|
$strings = array_merge($strings, [
|
|
'JS_SHOW_HIDE' => '',
|
|
'PASSWORD_CHANGE_BUTTON' => '',
|
|
'PASSWORD_CHANGE_DIV' => ''
|
|
]);
|
|
}
|
|
|
|
// first check if all strings are set from outside,
|
|
// if not, set with default ones
|
|
foreach ($strings as $string => $data) {
|
|
if (!array_key_exists($string, $this->login_template['strings'])) {
|
|
$this->login_template['strings'][$string] = $data;
|
|
}
|
|
}
|
|
|
|
// now check templates
|
|
// TODO: submit or JS to set target page as ajax call
|
|
if (!$this->login_template['template']) {
|
|
$this->login_template['template'] = <<<HTML
|
|
<!DOCTYPE html>
|
|
<html lang="{LANGUAGE}">
|
|
<head>
|
|
<title>{HTML_TITLE}</title>
|
|
<style type="text/css">
|
|
.norm { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10px; line-height: 15px; color: #000000}
|
|
h3 { font-size: 18px; }
|
|
.visible { visibility: visible; }
|
|
.hidden { visibility: hidden; display: none; }
|
|
</style>
|
|
<script language="JavaScript">
|
|
<!--
|
|
{JS_SHOW_HIDE}
|
|
//-->
|
|
</script>
|
|
{LOGOUT_TARGET}
|
|
</head>
|
|
|
|
<body bgcolor="#FFFFFF">
|
|
<br>
|
|
<br>
|
|
<br>
|
|
<form method="post">
|
|
<table width="500" border="0" cellpadding="2" cellspacing="1">
|
|
<tr>
|
|
<td class="norm" align="right">
|
|
<h3>{TITLE}</h3>
|
|
</td>
|
|
<td> </td>
|
|
</tr>
|
|
<tr>
|
|
<td class="norm" colspan="2" align="center">
|
|
{ERROR_MSG}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="right" class="norm">{USERNAME}</td>
|
|
<td><input type="text" name="login_username"></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="right" class="norm">{PASSWORD}</td>
|
|
<td><input type="password" name="login_password"></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="right"></td>
|
|
<td>
|
|
<input type="submit" name="login_login" value="{LOGIN}">
|
|
{PASSWORD_CHANGE_BUTTON}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="right">
|
|
<br><br>
|
|
</td>
|
|
<td> </td>
|
|
</tr>
|
|
</table>
|
|
{PASSWORD_CHANGE_DIV}
|
|
</form>
|
|
</body>
|
|
</html>
|
|
HTML;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* writes detailed data into the edit user log table (keep log what user does)
|
|
*
|
|
* @param string $event string of what has been done
|
|
* @param string $data data information (id, etc)
|
|
* @param string|int $error error id (mostly an int)
|
|
* @param string $username login user username
|
|
* @return void has no return
|
|
*/
|
|
private function writeLog(
|
|
string $event,
|
|
string $data,
|
|
string|int $error = '',
|
|
string $username = ''
|
|
): void {
|
|
if ($this->login) {
|
|
$this->action = 'Login';
|
|
} elseif ($this->logout) {
|
|
$this->action = 'Logout';
|
|
} else {
|
|
$this->action = '';
|
|
}
|
|
$_data_binary = [
|
|
'_SESSION' => $_SESSION,
|
|
'_GET' => $_GET,
|
|
'_POST' => $_POST,
|
|
'_FILES' => $_FILES,
|
|
'error' => $this->login_error
|
|
];
|
|
$data_binary = $this->db->dbEscapeBytea((string)bzcompress(serialize($_data_binary)));
|
|
// SQL querie for log entry
|
|
$q = "INSERT INTO edit_log "
|
|
. "(username, password, euid, event_date, event, error, data, data_binary, page, "
|
|
. "ip, user_agent, referer, script_name, query_string, server_name, http_host, "
|
|
. "http_accept, http_accept_charset, http_accept_encoding, session_id, "
|
|
. "action, action_id, action_yes, action_flag, action_menu, action_loaded, "
|
|
. "action_value, action_error) "
|
|
. "VALUES ('" . $this->db->dbEscapeString($username) . "', 'PASSWORD', "
|
|
. ($this->euid ? $this->euid : 'NULL') . ", "
|
|
. "NOW(), '" . $this->db->dbEscapeString($event) . "', "
|
|
. "'" . $this->db->dbEscapeString((string)$error) . "', "
|
|
. "'" . $this->db->dbEscapeString($data) . "', '" . $data_binary . "', "
|
|
. "'" . $this->page_name . "', ";
|
|
foreach (
|
|
[
|
|
'REMOTE_ADDR', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'SCRIPT_FILENAME',
|
|
'QUERY_STRING', 'SERVER_NAME', 'HTTP_HOST', 'HTTP_ACCEPT',
|
|
'HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_ENCODING'
|
|
] as $server_code
|
|
) {
|
|
if (array_key_exists($server_code, $_SERVER)) {
|
|
$q .= "'" . $this->db->dbEscapeString($_SERVER[$server_code]) . "', ";
|
|
} else {
|
|
$q .= "NULL, ";
|
|
}
|
|
}
|
|
$q .= "'" . $this->session->getSessionId() . "', ";
|
|
$q .= "'" . $this->db->dbEscapeString($this->action) . "', ";
|
|
$q .= "'" . $this->db->dbEscapeString($this->username) . "', ";
|
|
$q .= "NULL, ";
|
|
$q .= "'" . $this->db->dbEscapeString((string)$this->login_error) . "', ";
|
|
$q .= "NULL, NULL, ";
|
|
$q .= "'" . $this->db->dbEscapeString((string)$this->permission_okay) . "', ";
|
|
$q .= "NULL)";
|
|
$this->db->dbExec($q, 'NULL');
|
|
}
|
|
|
|
// *************************************************************************
|
|
// **** PUBLIC INTERNAL
|
|
// *************************************************************************
|
|
|
|
/**
|
|
* Main call that needs to be run to actaully check for login
|
|
* If this is not called, no login checks are done, unless the class
|
|
* is initialzied with the legacy call parameter.
|
|
* If ajax_page is true or AJAX_PAGE global var is true then the internal
|
|
* ajax flag will be set and no echo or exit will be done.
|
|
*
|
|
* @param bool $ajax_page [false] Set to true to never print out anythng
|
|
* @return void
|
|
*/
|
|
public function loginMainCall(bool $ajax_page = false): void
|
|
{
|
|
// start with no error
|
|
$this->login_error = 0;
|
|
// set db special errors
|
|
if (!$this->db->dbGetConnectionStatus()) {
|
|
$this->login_error = 1;
|
|
echo 'Could not connect to DB<br>';
|
|
// if I can't connect to the DB to auth exit hard. No access allowed
|
|
$this->loginTerminate(1000);
|
|
}
|
|
// initial the session if there is no session running already
|
|
// check if session exists and could be created
|
|
if ($this->session->checkActiveSession() === false) {
|
|
$this->login_error = 2;
|
|
echo '<b>No active session found</b>';
|
|
$this->loginTerminate(2000);
|
|
}
|
|
// set internal page name
|
|
$this->page_name = $this->loginReadPageName();
|
|
|
|
// set global is ajax page for if we show the data directly,
|
|
// or need to pass it back
|
|
// to the continue AJAX class for output back to the user
|
|
$this->login_is_ajax_page = false;
|
|
if ($ajax_page === true || !empty($GLOBALS['AJAX_PAGE'])) {
|
|
$this->login_is_ajax_page = true;
|
|
}
|
|
|
|
// attach outside uid for login post > get > empty
|
|
$this->login_user_id = $_POST['loginUserId'] ?? $_GET['loginUserId'] ?? '';
|
|
// cleanup only alphanumeric
|
|
if (!empty($this->login_user_id)) {
|
|
// set post/get only if actually set
|
|
if (isset($_POST['loginUserId'])) {
|
|
$this->login_user_id_source = 'POST';
|
|
} elseif (isset($_GET['loginUserId'])) {
|
|
$this->login_user_id_source = 'GET';
|
|
}
|
|
// clean login user id
|
|
$login_user_id_changed = 0;
|
|
$this->login_user_id = preg_replace(
|
|
"/[^A-Za-z0-9]/",
|
|
'',
|
|
$this->login_user_id,
|
|
-1,
|
|
$login_user_id_changed
|
|
);
|
|
// flag unclean input data
|
|
if ($login_user_id_changed > 0) {
|
|
$this->login_user_id_unclear = true;
|
|
// error for invalid user id?
|
|
$this->log->error('LOGIN USER ID: Invalid characters: '
|
|
. $login_user_id_changed . ' in loginUserId: '
|
|
. $this->login_user_id . ' (' . $this->login_user_id_source . ')');
|
|
}
|
|
}
|
|
// if there is none, there is none, saves me POST/GET check
|
|
$this->euid = array_key_exists('EUID', $_SESSION) ? (int)$_SESSION['EUID'] : 0;
|
|
// get login vars, are so, can't be changed
|
|
// prepare
|
|
// pass on vars to Object vars
|
|
$this->login = $_POST['login_login'] ?? '';
|
|
$this->username = $_POST['login_username'] ?? '';
|
|
$this->password = $_POST['login_password'] ?? '';
|
|
$this->logout = $_POST['login_logout'] ?? '';
|
|
// password change vars
|
|
$this->change_password = $_POST['change_password'] ?? '';
|
|
$this->pw_username = $_POST['pw_username'] ?? '';
|
|
$this->pw_old_password = $_POST['pw_old_password'] ?? '';
|
|
$this->pw_new_password = $_POST['pw_new_password'] ?? '';
|
|
$this->pw_new_password_confirm = $_POST['pw_new_password_confirm'] ?? '';
|
|
// disallow user list for password change
|
|
$this->pw_change_deny_users = ['admin'];
|
|
// max login counts before error reporting
|
|
$this->max_login_error_count = 10;
|
|
// users that never get locked, even if they are set strict
|
|
$this->lock_deny_users = ['admin'];
|
|
|
|
// if username & password & !$euid start login
|
|
$this->loginLoginUser();
|
|
// checks if $euid given check if user is okay for that side
|
|
$this->loginCheckPermissions();
|
|
// logsout user
|
|
$this->loginLogoutUser();
|
|
// ** LANGUAGE SET AFTER LOGIN **
|
|
$this->loginSetLocale();
|
|
// load translator
|
|
$this->l = new \CoreLibs\Language\L10n(
|
|
$this->locale['locale'],
|
|
$this->locale['domain'],
|
|
$this->locale['path']
|
|
);
|
|
// if the password change flag is okay, run the password change method
|
|
if ($this->password_change) {
|
|
$this->loginPasswordChange();
|
|
}
|
|
// password forgot
|
|
if ($this->password_forgot) {
|
|
$this->loginPasswordForgot();
|
|
}
|
|
// if !$euid || permission not okay, print login screan
|
|
$this->login_html = $this->loginCreateLoginHTML();
|
|
// closing all connections, depending on error status, exit
|
|
if (!$this->loginCloseClass()) {
|
|
// if variable AJAX flag is not set, show output
|
|
// else pass through for ajax work
|
|
if ($this->login_is_ajax_page === false) {
|
|
// the login screen if we hav no login permission and
|
|
// login screen html data
|
|
if ($this->login_html !== null) {
|
|
// echo $this->login_html;
|
|
$this->loginPrintLogin();
|
|
}
|
|
// exit so we don't process anything further, at all
|
|
$this->loginTerminate(3000);
|
|
} else {
|
|
// if we are on an ajax page reset any POST/GET array data to avoid
|
|
// any accidentical processing going on
|
|
$_POST = [];
|
|
$_GET = [];
|
|
// set the action to login so we can trigger special login html return
|
|
$_POST['action'] = 'login';
|
|
$_POST['login_exit'] = 3000;
|
|
$_POST['login_error'] = $this->loginGetLastErrorCode();
|
|
$_POST['login_error_text'] = $this->loginGetErrorMsg(
|
|
$this->loginGetLastErrorCode(),
|
|
true
|
|
);
|
|
$_POST['login_html'] = $this->login_html;
|
|
// NOTE: this part needs to be catched by the frontend AJAX
|
|
// and some function needs to then set something like this
|
|
// document.getElementsByTagName('html')[0].innerHTML = data.content.login_html;
|
|
}
|
|
}
|
|
// set acls for this user/group and this page
|
|
$this->loginSetAcl();
|
|
}
|
|
|
|
/**
|
|
* Returns current set login_html content
|
|
*
|
|
* @return string login page html content, created, empty string if none
|
|
*/
|
|
public function loginGetLoginHTML(): string
|
|
{
|
|
return $this->login_html ?? '';
|
|
}
|
|
|
|
/**
|
|
* return the current set page name or empty string for nothing set
|
|
*
|
|
* @return string current page name set
|
|
*/
|
|
public function loginGetPageName(): string
|
|
{
|
|
return $this->page_name;
|
|
}
|
|
|
|
/**
|
|
* Returns the current flag if this call is for an ajax type apge
|
|
*
|
|
* @return bool True for yes, False for normal HTML return
|
|
*/
|
|
public function loginGetAjaxFlag(): bool
|
|
{
|
|
return $this->login_is_ajax_page;
|
|
}
|
|
|
|
/**
|
|
* returns the last set error code
|
|
*
|
|
* @return int Last set error code, 0 for no error
|
|
*/
|
|
public function loginGetLastErrorCode(): int
|
|
{
|
|
return $this->login_error;
|
|
}
|
|
|
|
/**
|
|
* return set error message
|
|
* if nothing found for given code, return general error message
|
|
*
|
|
* @param int $code The error code for which we want the error string
|
|
* @param bool $text If set to true, do not use HTML code
|
|
* @return string Error string
|
|
*/
|
|
public function loginGetErrorMsg(int $code, bool $text = false): string
|
|
{
|
|
$string = '';
|
|
if (
|
|
!empty($this->login_error_msg[(string)$code]['msg']) &&
|
|
!empty($this->login_error_msg[(string)$code]['flag'])
|
|
) {
|
|
$error_str_prefix = '';
|
|
switch ($this->login_error_msg[(string)$code]['flag']) {
|
|
case 'e':
|
|
$error_str_prefix = ($text ? '' : '<span style="color: red;">')
|
|
. $this->l->__('Fatal Error:')
|
|
. ($text ? '' : '</span>');
|
|
break;
|
|
case 'o':
|
|
$error_str_prefix = $this->l->__('Success:');
|
|
break;
|
|
}
|
|
$string = $error_str_prefix . ' '
|
|
. ($text ? '' : '<b>')
|
|
. $this->login_error_msg[(string)$code]['msg']
|
|
. ($text ? '' : '</b>');
|
|
} elseif (!empty($code)) {
|
|
$string = $this->l->__('LOGIN: undefined error message');
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Sets the minium length and checks on valid.
|
|
* Current max length is 255 characters
|
|
*
|
|
* @param int $length set the minimum length
|
|
* @return bool true/false on success
|
|
*/
|
|
public function loginSetPasswordMinLength(int $length): bool
|
|
{
|
|
// check that numeric, positive numeric, not longer than max input string lenght
|
|
// and not short than min password length
|
|
if (
|
|
$length >= $this->password_min_length_max &&
|
|
$length <= $this->password_max_length &&
|
|
$length <= self::PASSWORD_MAX_LENGTH
|
|
) {
|
|
$this->password_min_length = $length;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* return password min/max length values as selected
|
|
* min: return current minimum lenght
|
|
* max: return current set maximum length
|
|
* min_length: get the fixed minimum password length
|
|
*
|
|
* @param string $select Can be min/max or min_length
|
|
* @return int
|
|
*/
|
|
public function loginGetPasswordLenght(string $select): int
|
|
{
|
|
$value = 0;
|
|
switch (strtolower($select)) {
|
|
case 'min':
|
|
case 'lower':
|
|
$value = $this->password_min_length;
|
|
break;
|
|
case 'max':
|
|
case 'upper':
|
|
$value = $this->password_max_length;
|
|
break;
|
|
case 'minimum_length':
|
|
case 'min_length':
|
|
case 'length':
|
|
$value = $this->password_min_length_max;
|
|
break;
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Set the maximum login errors a user can have before getting locked
|
|
* if the user has the strict lock setting turned on
|
|
*
|
|
* @param int $times Value can be -1 (no locking) or greater than 0
|
|
* @return bool True on sueccess set, or false on error
|
|
*/
|
|
public function loginSetMaxLoginErrorCount(int $times): bool
|
|
{
|
|
if ($times == -1 || $times > 0) {
|
|
$this->max_login_error_count = $times;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the current maximum login error count
|
|
*
|
|
* @return int Current set max login error count, Can be -1 or greater than 0
|
|
*/
|
|
public function loginGetMaxLoginErrorCount(): int
|
|
{
|
|
return $this->max_login_error_count;
|
|
}
|
|
|
|
/**
|
|
* if a user pressed on logout, destroyes session and unsets all global vars
|
|
*
|
|
* @return void has no return
|
|
*/
|
|
public function loginLogoutUser(): void
|
|
{
|
|
// must be either logout or error
|
|
if (!$this->logout && !$this->login_error) {
|
|
return;
|
|
}
|
|
// unset session vars set/used in this login
|
|
$this->session->sessionDestroy();
|
|
// unset euid
|
|
$this->euid = null;
|
|
// then prints the login screen again
|
|
$this->permission_okay = false;
|
|
}
|
|
|
|
/**
|
|
* for every page the user access this script checks if he is allowed to do so
|
|
*
|
|
* @return bool permission okay as true/false
|
|
*/
|
|
public function loginCheckPermissions(): bool
|
|
{
|
|
// start with not allowed
|
|
$this->permission_okay = false;
|
|
// bail for no euid (no login)
|
|
if (empty($this->euid)) {
|
|
return $this->permission_okay;
|
|
}
|
|
// bail for previous wrong page match, eg if method is called twice
|
|
if ($this->login_error == 103) {
|
|
return $this->permission_okay;
|
|
}
|
|
$q = "SELECT ep.filename, "
|
|
// base lock flags
|
|
. "eu.deleted, eu.enabled, eu.locked, "
|
|
// date based lock
|
|
. "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, "
|
|
// login id validation
|
|
. "login_user_id, "
|
|
. "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, "
|
|
// 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_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 "
|
|
//
|
|
. "FROM edit_page ep, edit_page_access epa, edit_group eg, edit_user eu "
|
|
. "WHERE ep.edit_page_id = epa.edit_page_id "
|
|
. "AND eg.edit_group_id = epa.edit_group_id "
|
|
. "AND eg.edit_group_id = eu.edit_group_id "
|
|
. "AND eu.edit_user_id = " . $this->euid . " "
|
|
. "AND ep.filename = '" . $this->page_name . "' "
|
|
. "AND eg.enabled = 1 AND epa.enabled = 1";
|
|
$res = $this->db->dbReturnRow($q);
|
|
if (!is_array($res)) {
|
|
$this->login_error = 109;
|
|
return $this->permission_okay;
|
|
}
|
|
if (
|
|
!$this->loginValidationCheck(
|
|
(int)$res['deleted'],
|
|
(int)$res['enabled'],
|
|
(int)$res['locked'],
|
|
(int)$res['locked_period'],
|
|
(int)$res['login_user_id_locked']
|
|
)
|
|
) {
|
|
// errors set in method
|
|
return $this->permission_okay;
|
|
}
|
|
// if login user id parameter and no username, check period here
|
|
if (
|
|
empty($this->username) &&
|
|
!empty($this->login_user_id) &&
|
|
!$this->loginLoginUserIdCheck(
|
|
(int)$res['login_user_id_valid_date'],
|
|
(int)$res['login_user_id_revalidate']
|
|
)
|
|
) {
|
|
// errors set in method
|
|
return $this->permission_okay;
|
|
}
|
|
if (isset($res['filename']) && $res['filename'] == $this->page_name) {
|
|
$this->permission_okay = true;
|
|
} else {
|
|
$this->login_error = 103;
|
|
}
|
|
// if called from public, so we can check if the permissions are ok
|
|
return $this->permission_okay;
|
|
}
|
|
|
|
/**
|
|
* Return current permission status;
|
|
*
|
|
* @return bool True for permission ok, False for not
|
|
*/
|
|
public function loginGetPermissionOkay(): bool
|
|
{
|
|
return $this->permission_okay;
|
|
}
|
|
|
|
/**
|
|
* Check if source (page, base) is matching to the given min access string
|
|
* min access string must be valid access level string (eg read, mod, write)
|
|
* This does not take in account admin flag set
|
|
*
|
|
* @param string $source a valid base level string eg base, page
|
|
* @param string $min_access a valid min level string, eg read, mod, siteadmin
|
|
* @return bool True for valid access, False for invalid
|
|
*/
|
|
public function loginCheckAccess(string $source, string $min_access): bool
|
|
{
|
|
if (!in_array($source, ['page', 'base'])) {
|
|
$source = 'base';
|
|
}
|
|
if (
|
|
empty($this->acl['min'][$min_access]) ||
|
|
empty($this->acl[$source])
|
|
) {
|
|
return false;
|
|
}
|
|
// phan claims $this->acl['min'] can be null, but above should skip
|
|
/** @phan-suppress-next-line PhanTypeArraySuspiciousNullable */
|
|
if ($this->acl[$source] >= $this->acl['min'][$min_access]) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* check if min accesss string (eg, read, mod, etc) is matchable
|
|
* EQUAL to BASE set right
|
|
*
|
|
* @param string $min_access
|
|
* @return bool
|
|
*/
|
|
public function loginCheckAccessBase(string $min_access): bool
|
|
{
|
|
return $this->loginCheckAccess('base', $min_access);
|
|
}
|
|
|
|
/**
|
|
* check if min accesss string (eg, read, mod, etc) is matchable
|
|
* EQUAL to PAGE set right
|
|
*
|
|
* @param string $min_access
|
|
* @return bool
|
|
*/
|
|
public function loginCheckAccessPage(string $min_access): bool
|
|
{
|
|
return $this->loginCheckAccess('page', $min_access);
|
|
}
|
|
|
|
/**
|
|
* Return ACL array as is
|
|
*
|
|
* @return array<mixed>
|
|
*/
|
|
public function loginGetAcl(): array
|
|
{
|
|
return $this->acl;
|
|
}
|
|
|
|
/**
|
|
* return full default acl list or a list entry if level is set and found
|
|
* for getting level from list type
|
|
* $login->loginGetAclList('list')['level'] ?? 0
|
|
*
|
|
* @param int|null $level Level to get or null/empty for full list
|
|
* @return array<string,mixed> Full default ACL level list or level entry if found
|
|
*/
|
|
public function loginGetAclList(?int $level = null): array
|
|
{
|
|
// if no level given, return full list
|
|
if (empty($level)) {
|
|
return $this->default_acl_list;
|
|
}
|
|
// if level given and exist return this array block (name/level)
|
|
if (!empty($this->default_acl_list[$level])) {
|
|
return $this->default_acl_list[$level];
|
|
} else {
|
|
// else return empty array
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return level number in int from acl list depending on level
|
|
* if not found return false
|
|
*
|
|
* @param string $type Type name to look in the acl list
|
|
* @return int|bool Either int level or false for not found
|
|
*/
|
|
public function loginGetAclListFromType(string $type): int|bool
|
|
{
|
|
if (!isset($this->default_acl_list_type[$type])) {
|
|
return false;
|
|
}
|
|
return (int)$this->default_acl_list_type[$type];
|
|
}
|
|
|
|
/**
|
|
* checks if this edit access id is valid
|
|
*
|
|
* @param int|null $edit_access_id access id pk to check
|
|
* @return bool true/false: if the edit access is not
|
|
* in the valid list: false
|
|
*/
|
|
public function loginCheckEditAccess(?int $edit_access_id): bool
|
|
{
|
|
if ($edit_access_id === null) {
|
|
return false;
|
|
}
|
|
if (array_key_exists($edit_access_id, $this->acl['unit'])) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* checks that the given edit access id is valid for this user
|
|
* return null if nothing set, or the edit access id
|
|
*
|
|
* @param int|null $edit_access_id edit access id to check
|
|
* @return int|null same edit access id if ok
|
|
* or the default edit access id
|
|
* if given one is not valid
|
|
*/
|
|
public function loginCheckEditAccessId(?int $edit_access_id): ?int
|
|
{
|
|
if (
|
|
$edit_access_id !== null &&
|
|
isset($_SESSION['UNIT']) &&
|
|
is_array($_SESSION['UNIT']) &&
|
|
!array_key_exists($edit_access_id, $_SESSION['UNIT'])
|
|
) {
|
|
$edit_access_id = null;
|
|
if (is_numeric($_SESSION['UNIT_DEFAULT'])) {
|
|
$edit_access_id = (int)$_SESSION['UNIT_DEFAULT'];
|
|
}
|
|
}
|
|
return $edit_access_id;
|
|
}
|
|
|
|
/**
|
|
* return a set entry from the UNIT session for an edit access_id
|
|
* if not found return false
|
|
*
|
|
* @param int $edit_access_id edit access id
|
|
* @param string|int $data_key key value to search for
|
|
* @return bool|string false for not found or string for found data
|
|
*/
|
|
public function loginGetEditAccessData(
|
|
int $edit_access_id,
|
|
string|int $data_key
|
|
): bool|string {
|
|
if (!isset($_SESSION['UNIT'][$edit_access_id]['data'][$data_key])) {
|
|
return false;
|
|
}
|
|
return $_SESSION['UNIT'][$edit_access_id]['data'][$data_key];
|
|
}
|
|
|
|
/**
|
|
* Return edit access primary key id from edit access uid
|
|
* false on not found
|
|
*
|
|
* @param string $uid Edit Access UID to look for
|
|
* @return int|bool Either primary key in int or false in bool for not found
|
|
*/
|
|
public function loginGetEditAccessIdFromUid(string $uid): int|bool
|
|
{
|
|
if (!isset($_SESSION['UNIT_UID'][$uid])) {
|
|
return false;
|
|
}
|
|
return (int)$_SESSION['UNIT_UID'][$uid];
|
|
}
|
|
|
|
/**
|
|
* Check if admin flag is set
|
|
*
|
|
* @return bool True if admin flag set
|
|
*/
|
|
public function loginIsAdmin(): bool
|
|
{
|
|
if (!empty($this->acl['admin'])) {
|
|
return true;
|
|
}
|
|
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
|
|
*
|
|
* @return string loginUserId or empty string for not set
|
|
*/
|
|
public function loginGetLoginUserId(): string
|
|
{
|
|
return $this->login_user_id;
|
|
}
|
|
|
|
/**
|
|
* Returns GET/POST for where the loginUserId was set
|
|
*
|
|
* @return string GET or POST or empty string for not set
|
|
*/
|
|
public function loginGetLoginUserIdSource(): string
|
|
{
|
|
return $this->login_user_id_source;
|
|
}
|
|
|
|
/**
|
|
* Returns unclear login user id state. If true then illegal characters
|
|
* where present in the loginUserId parameter
|
|
*
|
|
* @return bool False for clear, True if illegal characters found
|
|
*/
|
|
public function loginGetLoginUserIdUnclean(): bool
|
|
{
|
|
return $this->login_user_id_unclear;
|
|
}
|
|
|
|
/**
|
|
* old name for loginGetEditAccessData
|
|
*
|
|
* @deprecated Use $login->loginGetEditAccessData()
|
|
* @param int $edit_access_id
|
|
* @param string|int $data_key
|
|
* @return bool|string
|
|
*/
|
|
public function loginSetEditAccessData(
|
|
int $edit_access_id,
|
|
string|int $data_key
|
|
): bool|string {
|
|
return $this->loginGetEditAccessData($edit_access_id, $data_key);
|
|
}
|
|
|
|
/**
|
|
* Return locale settings with
|
|
* locale
|
|
* domain
|
|
* encoding
|
|
* path
|
|
*
|
|
* empty string if not set
|
|
*
|
|
* @return array<string,string> Locale settings
|
|
*/
|
|
public function loginGetLocale(): array
|
|
{
|
|
return $this->locale;
|
|
}
|
|
|
|
/**
|
|
* return header color or null for not set
|
|
*
|
|
* @return string|null Header color in RGB hex with leading sharp
|
|
*/
|
|
public function loginGetHeaderColor(): ?string
|
|
{
|
|
return $_SESSION['HEADER_COLOR'] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Return the current loaded list of pages the user can access
|
|
*
|
|
* @return array<mixed>
|
|
*/
|
|
public function loginGetPages(): array
|
|
{
|
|
|
|
return $_SESSION['PAGES'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Get the current set EUID (edit user id)
|
|
*
|
|
* @return string EUID as string
|
|
*/
|
|
public function loginGetEuid(): string
|
|
{
|
|
return (string)$this->euid;
|
|
}
|
|
}
|
|
|
|
// __END__
|