From 5a81626e8cffb36db968ef0f9a2f36ff9354435b Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 11 Apr 2022 09:17:19 +0900 Subject: [PATCH] Clean up ACL/Login Add locale global variable in the format _.@ Default set to en_US.UTF-8 Also remove nested if callse and do early abort/method return for flatten code: loginUser logoutUser setAcl printLogin passwordChange --- www/lib/CoreLibs/ACL/Login.php | 1131 ++++++++++++++++---------------- 1 file changed, 578 insertions(+), 553 deletions(-) diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index bf424155..740db599 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -216,11 +216,16 @@ class Login // to the continue AJAX class for output back to the user $this->login_is_ajax_page = isset($GLOBALS['AJAX_PAGE']) && $GLOBALS['AJAX_PAGE'] ? true : false; // set the default lang + $locale = 'en_US.UTF-8'; $lang = 'en_utf8'; if (Session::getSessionId() !== false && !empty($_SESSION['DEFAULT_LANG'])) { $lang = $_SESSION['DEFAULT_LANG']; + $locale = $_SESSION['DEFAULT_LOCALE']; } else { - $lang = defined('SITE_LANG') ? SITE_LANG : DEFAULT_LANG; + $lang = defined('SITE_LANG') && !empty(SITE_LANG) ? + SITE_LANG : DEFAULT_LANG; + $locale = defined('SITE_LOCALE') && !empty(SITE_LOCALE) ? + SITE_LOCALE : DEFAULT_LOCALE; } $this->l = $l10n ?? new \CoreLibs\Language\L10n($lang); @@ -416,271 +421,283 @@ class Login */ private function loginLoginUser(): void { + // if pressed login at least and is not yet loggined in + // if (!(!$this->euid && $this->login)) { + if ($this->euid && !$this->login) { + return; + } + // if not username AND password where given + if (!($this->password && $this->username)) { + $this->login_error = 102; + $this->permission_okay = false; + return; + } // have to get the global stuff here for setting it later - if (!$this->euid && $this->login) { - if (!($this->password && $this->username)) { - $this->login_error = 102; - } else { - // 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, admin, " - . "eu.login_error_count, eu.login_error_date_last, " - . "eu.login_error_date_first, eu.strict, eu.locked, " - . "eu.debug, eu.db_debug, " - . "eareu.level AS user_level, eareu.type AS user_type, " - . "eareg.level AS group_level, eareg.type AS group_type, " - . "eu.enabled, el.short_name AS lang_short, el.iso_name AS lang_iso, " - . "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 " - // password match is done in script, against old plain or new blowfish encypted - . "(LOWER(username) = '" . $this->db->dbEscapeString(strtolower($this->username)) . "') "; - $res = $this->db->dbReturn($q); - if (!is_array($res)) { - $this->login_error = 1009; - $this->permission_okay = false; - } elseif (empty($this->db->dbGetCursorNumRows($q))) { - // username is wrong, but we throw for wrong username - // and wrong password the same error - $this->login_error = 1010; - } else { - // if login errors is half of max errors and the last login error - // was less than 10s ago, forbid any new login try + // 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, admin, " + . "eu.login_error_count, eu.login_error_date_last, " + . "eu.login_error_date_first, eu.strict, eu.locked, " + . "eu.debug, eu.db_debug, " + . "eareu.level AS user_level, eareu.type AS user_type, " + . "eareg.level AS group_level, eareg.type AS group_type, " + . "eu.enabled, el.short_name AS locale, el.iso_name AS encoding, " + . "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 " + // password match is done in script, against old plain or new blowfish encypted + . "(LOWER(username) = '" . $this->db->dbEscapeString(strtolower($this->username)) . "') "; + $res = $this->db->dbReturn($q); + // query was not run successful + if (!is_array($res)) { + $this->login_error = 1009; + $this->permission_okay = false; + return; + } elseif (empty($this->db->dbGetCursorNumRows($q))) { + // 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 + // check flow + // - user is enabled + // - user is not locked + // - password is readable + // - encrypted password matches + // - plain password matches - if (!$res['enabled']) { - // user is enabled - $this->login_error = 104; - } elseif ($res['locked']) { - // user is locked, either set or auto set - $this->login_error = 105; - } elseif (!$this->loginPasswordCheck($res['password'])) { - // none to be set, set in login password check - } 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 = $res['edit_user_id']; - // check if user is okay - $this->loginCheckPermissions(); - if ($this->login_error == 0) { - // 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['GROUP_ACL_LEVEL'] = $res['group_level']; - $_SESSION['GROUP_ACL_TYPE'] = $res['group_type']; - // deprecated TEMPLATE setting - $_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : ''; - $_SESSION['HEADER_COLOR'] = $res['second_header_color'] ? - $res['second_header_color'] : - $res['first_header_color']; - $_SESSION['LANG'] = $res['lang_short']; - $_SESSION['DEFAULT_CHARSET'] = $res['lang_iso']; - $_SESSION['DEFAULT_LANG'] = $res['lang_short'] . '_' - . strtolower(str_replace('-', '', $res['lang_iso'])); - // 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 " - . "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' => $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'], - 'data' => $ea_data - ]; - // set the default unit - if ($res['edit_default']) { - $_SESSION['UNIT_DEFAULT'] = $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']; - } - } - } - } // user was not found - } // if not username AND password where given - // if there was an login error, show login screen - if ($this->login_error) { - // reset the perm var, to confirm logout - $this->permission_okay = false; + if (!$res['enabled']) { + // user is enabled + $this->login_error = 104; + } elseif ($res['locked']) { + // user is locked, either set or auto set + $this->login_error = 105; + } elseif (!$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 + } 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); } - } // if he pressed login at least and is not yet loggined in + // normal user processing + // set class var and session var + $_SESSION['EUID'] = $this->euid = $res['edit_user_id']; + // check if user is okay + $this->loginCheckPermissions(); + if ($this->login_error == 0) { + // 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['GROUP_ACL_LEVEL'] = $res['group_level']; + $_SESSION['GROUP_ACL_TYPE'] = $res['group_type']; + // deprecated TEMPLATE setting + $_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : ''; + $_SESSION['HEADER_COLOR'] = $res['second_header_color'] ? + $res['second_header_color'] : + $res['first_header_color']; + $_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 " + . "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' => $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'], + 'data' => $ea_data + ]; + // set the default unit + if ($res['edit_default']) { + $_SESSION['UNIT_DEFAULT'] = $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; + } } /** @@ -721,45 +738,48 @@ class Login */ public function loginLogoutUser(): void { - if ($this->logout || $this->login_error) { - // unregister and destroy session vars - foreach ( - // TODO move this into some global array for easier update - [ - 'ADMIN', - 'BASE_ACL_LEVEL', - 'DB_DEBUG', - 'DEBUG_ALL', - 'DEFAULT_ACL_LIST', - 'DEFAULT_CHARSET', - 'DEFAULT_LANG', - 'EAID', - 'EUID', - 'GROUP_ACL_LEVEL', - 'GROUP_ACL_TYPE', - 'GROUP_NAME', - 'HEADER_COLOR', - 'LANG', - 'PAGES_ACL_LEVEL', - 'PAGES', - 'TEMPLATE', - 'UNIT_ACL_LEVEL', - 'UNIT_DEFAULT', - 'UNIT', - 'USER_ACL_LEVEL', - 'USER_ACL_TYPE', - 'USER_NAME', - ] as $session_var - ) { - unset($_SESSION[$session_var]); - } - // final unset all - session_unset(); - // final destroy session - session_destroy(); - // then prints the login screen again - $this->permission_okay = false; + // must be either logout or error + if (!$this->logout && !$this->login_error) { + return; } + // unregister and destroy session vars + foreach ( + // TODO move this into some global array for easier update + [ + 'ADMIN', + 'BASE_ACL_LEVEL', + 'DB_DEBUG', + 'DEBUG_ALL', + 'DEFAULT_ACL_LIST', + 'DEFAULT_CHARSET', + 'DEFAULT_LANG', + 'DEFAULT_LOCALE', + 'EAID', + 'EUID', + 'GROUP_ACL_LEVEL', + 'GROUP_ACL_TYPE', + 'GROUP_NAME', + 'HEADER_COLOR', + 'LANG', + 'PAGES_ACL_LEVEL', + 'PAGES', + 'TEMPLATE', + 'UNIT_ACL_LEVEL', + 'UNIT_DEFAULT', + 'UNIT', + 'USER_ACL_LEVEL', + 'USER_ACL_TYPE', + 'USER_NAME', + ] as $session_var + ) { + unset($_SESSION[$session_var]); + } + // final unset all + session_unset(); + // final destroy session + session_destroy(); + // then prints the login screen again + $this->permission_okay = false; } /** @@ -782,95 +802,96 @@ class Login private function loginSetAcl(): void { // only set acl if we have permission okay - if ($this->permission_okay) { - // username (login), group name - $this->acl['user_name'] = $_SESSION['USER_NAME']; - $this->acl['group_name'] = $_SESSION['GROUP_NAME']; - // we start with the default acl - $this->acl['base'] = DEFAULT_ACL_LEVEL; - - // set admin flag and base to 100 - if ($_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 ($_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 default acl - // set group if not -1, overrides default - // set page if not -1, overrides group set - $this->acl['page'] = DEFAULT_ACL_LEVEL; - 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]; - } - - // PER ACCOUNT (UNIT/edit access)-> - foreach ($_SESSION['UNIT'] as $ea_id => $unit) { - // if admin flag is set, all units are set to 100 - if ($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'], - 'default' => $unit['default'], - 'data' => $unit['data'] - ]; - // set default - if ($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']; - // integrate the type acl list, but only for the keyword -> level - foreach ($this->default_acl_list as $level => $data) { - $this->acl['min'][$data['type']] = $level; - } - // set the full acl list too - $this->acl['acl_list'] = $_SESSION['DEFAULT_ACL_LIST']; - // debug - // $this->debug('ACL', $this->print_ar($this->acl)); + if (!$this->permission_okay) { + return; } + // username (login), group name + $this->acl['user_name'] = $_SESSION['USER_NAME']; + $this->acl['group_name'] = $_SESSION['GROUP_NAME']; + // we start with the default acl + $this->acl['base'] = DEFAULT_ACL_LEVEL; + + // set admin flag and base to 100 + if ($_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 ($_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 default acl + // set group if not -1, overrides default + // set page if not -1, overrides group set + $this->acl['page'] = DEFAULT_ACL_LEVEL; + 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]; + } + + // PER ACCOUNT (UNIT/edit access)-> + foreach ($_SESSION['UNIT'] as $ea_id => $unit) { + // if admin flag is set, all units are set to 100 + if ($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'], + 'default' => $unit['default'], + 'data' => $unit['data'] + ]; + // set default + if ($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']; + // integrate the type acl list, but only for the keyword -> level + foreach ($this->default_acl_list as $level => $data) { + $this->acl['min'][$data['type']] = $level; + } + // set the full acl list too + $this->acl['acl_list'] = $_SESSION['DEFAULT_ACL_LIST']; + // debug + // $this->debug('ACL', $this->print_ar($this->acl)); } /** @@ -946,96 +967,98 @@ class Login */ private function loginPasswordChange(): void { - if ($this->change_password) { - $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) || - (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) || - (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; + // 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.'; } - // log this password change attempt - $this->writeLog($event, $data, $this->login_error, $this->pw_username); - } // button pressed + // 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) || + (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) || + (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); } /** @@ -1045,98 +1068,100 @@ class Login private function loginPrintLogin() { $html_string = null; - if (!$this->permission_okay) { - // set the templates now - $this->loginSetTemplates(); - // if there is a global logout target ... - if (file_exists($this->logout_target) && $this->logout_target) { - $LOGOUT_TARGET = $this->logout_target; - } else { - $LOGOUT_TARGET = ""; - } + // 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) && $this->logout_target) { + $LOGOUT_TARGET = $this->logout_target; + } else { + $LOGOUT_TARGET = ""; + } - $html_string = (string)$this->login_template['template']; + $html_string = (string)$this->login_template['template']; - // if password change is okay - if ($this->password_change) { - $html_string_password_change = $this->login_template['password_change']; + // 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) { + // 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( - '{ERROR_MSG}', - $this->login_error_msg[$this->login_error] . '
', - $html_string_password_change - ); - } else { - $html_string_password_change = str_replace( - '{ERROR_MSG}', - '
', + '{' . $string . '}', + $data, $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}', - '', - $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}', - '', - $html_string - ); - } else { - $html_string = str_replace('{LOGOUT_TARGET}', '', $html_string); - } - // print error messagae if ($this->login_error) { - $html_string = str_replace( + $html_string_password_change = str_replace( '{ERROR_MSG}', $this->login_error_msg[$this->login_error] . '
', - $html_string - ); - } elseif ($this->password_change_ok && $this->password_change) { - $html_string = str_replace( - '{ERROR_MSG}', - $this->login_error_msg[300] . '
', - $html_string + $html_string_password_change ); } else { - $html_string = str_replace('{ERROR_MSG}', '
', $html_string); + $html_string_password_change = str_replace( + '{ERROR_MSG}', + '
', + $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}', + '', + $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; + } - // create the replace array context - foreach ($this->login_template['strings'] as $string => $data) { - $html_string = str_replace('{' . $string . '}', $data, $html_string); - } - } // if permission is 0 then print out login - // return the created HTML here or null for nothing + // put in the logout redirect string + if ($this->logout && $LOGOUT_TARGET) { + $html_string = str_replace( + '{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->login_error_msg[$this->login_error] . '
', + $html_string + ); + } elseif ($this->password_change_ok && $this->password_change) { + $html_string = str_replace( + '{ERROR_MSG}', + $this->login_error_msg[300] . '
', + $html_string + ); + } else { + $html_string = str_replace('{ERROR_MSG}', '
', $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; }