%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/src/ |
Current File : /var/www/projetos/suporte.iigd.com.br/src/Session.php |
<?php /** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2024 Teclib' and contributors. * @copyright 2003-2014 by the INDEPNET Development Team. * @licence https://www.gnu.org/licenses/gpl-3.0.html * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * * --------------------------------------------------------------------- */ use Glpi\Cache\CacheManager; use Glpi\Cache\I18nCache; use Glpi\Event; use Glpi\Plugin\Hooks; use Glpi\Toolbox\Sanitizer; /** * Session Class **/ class Session { // GLPI MODE const NORMAL_MODE = 0; const TRANSLATION_MODE = 1; // no more used const DEBUG_MODE = 2; /** * Destroy the current session * * @return void **/ public static function destroy() { self::start(); // Unset all of the session variables. session_unset(); // destroy may cause problems (no login / back to login page) $_SESSION = []; // write_close may cause troubles (no login / back to login page) } /** * Write and close session, but only if not in debug mode (allows proper use of the debug bar for AJAX calls). * @return void */ public static function writeClose() { if ($_SESSION['glpi_use_mode'] !== self::DEBUG_MODE) { session_write_close(); } } /** * Init session for the user is defined * * @param Auth $auth Auth object to init session * * @return void **/ public static function init(Auth $auth) { /** @var array $CFG_GLPI */ global $CFG_GLPI; if ($auth->auth_succeded) { // Restart GLPI session : complete destroy to prevent lost datas $tosave = ['glpi_plugins', 'glpicookietest', 'phpCAS', 'glpicsrftokens', 'glpiskipMaintenance' ]; $save = []; foreach ($tosave as $t) { if (isset($_SESSION[$t])) { $save[$t] = $_SESSION[$t]; } } self::destroy(); session_regenerate_id(); self::start(); $_SESSION = $save; $_SESSION['valid_id'] = session_id(); // Define default time : $_SESSION["glpi_currenttime"] = date("Y-m-d H:i:s"); // Normal mode for this request $_SESSION["glpi_use_mode"] = self::NORMAL_MODE; // Check ID exists and load complete user from DB (plugins...) if ( isset($auth->user->fields['id']) && $auth->user->getFromDB($auth->user->fields['id']) ) { if ( !$auth->user->fields['is_deleted'] && ($auth->user->fields['is_active'] && (($auth->user->fields['begin_date'] < $_SESSION["glpi_currenttime"]) || is_null($auth->user->fields['begin_date'])) && (($auth->user->fields['end_date'] > $_SESSION["glpi_currenttime"]) || is_null($auth->user->fields['end_date']))) ) { $_SESSION["glpiID"] = $auth->user->fields['id']; $_SESSION["glpifriendlyname"] = $auth->user->getFriendlyName(); $_SESSION["glpiname"] = $auth->user->fields['name']; $_SESSION["glpirealname"] = $auth->user->fields['realname']; $_SESSION["glpifirstname"] = $auth->user->fields['firstname']; $_SESSION["glpidefault_entity"] = $auth->user->fields['entities_id']; $_SESSION["glpiextauth"] = $auth->extauth; if (isset($_SESSION['phpCAS']['user'])) { $_SESSION["glpiauthtype"] = Auth::CAS; $_SESSION["glpiextauth"] = 0; } else { $_SESSION["glpiauthtype"] = $auth->user->fields['authtype']; } $_SESSION["glpiroot"] = $CFG_GLPI["root_doc"]; $_SESSION["glpi_use_mode"] = $auth->user->fields['use_mode']; $_SESSION["glpi_plannings"] = importArrayFromDB($auth->user->fields['plannings']); $_SESSION["glpicrontimer"] = time(); // Default tab // $_SESSION['glpi_tab']=1; $_SESSION['glpi_tabs'] = []; $auth->user->computePreferences(); foreach ($CFG_GLPI['user_pref_field'] as $field) { if ($field == 'language' && isset($_POST['language']) && $_POST['language'] != '') { $_SESSION["glpi$field"] = $_POST[$field]; } else if (isset($auth->user->fields[$field])) { $_SESSION["glpi$field"] = $auth->user->fields[$field]; } } if (isset($_SESSION['glpidefault_central_tab']) && $_SESSION['glpidefault_central_tab']) { Session::setActiveTab("central", "Central$" . $_SESSION['glpidefault_central_tab']); } // Do it here : do not reset on each page, cause export issue if ($_SESSION["glpilist_limit"] > $CFG_GLPI['list_limit_max']) { $_SESSION["glpilist_limit"] = $CFG_GLPI['list_limit_max']; } // Init not set value for language if (empty($_SESSION["glpilanguage"])) { $_SESSION["glpilanguage"] = self::getPreferredLanguage(); } $_SESSION['glpi_dropdowntranslations'] = DropdownTranslation::getAvailableTranslations($_SESSION["glpilanguage"]); self::loadLanguage(); if ($auth->password_expired) { $_SESSION['glpi_password_expired'] = 1; // Do not init profiles, as user has to update its password to be able to use GLPI return; } // glpiprofiles -> other available profile with link to the associated entities Plugin::doHook(Hooks::INIT_SESSION); self::initEntityProfiles(self::getLoginUserID()); // Use default profile if exist if (isset($_SESSION['glpiprofiles'][$auth->user->fields['profiles_id']])) { self::changeProfile($auth->user->fields['profiles_id']); } else { // Else use first self::changeProfile(key($_SESSION['glpiprofiles'])); } if (!Session::getCurrentInterface()) { $auth->auth_succeded = false; $auth->addToError(__("You don't have right to connect")); } } else { $auth->auth_succeded = false; $auth->addToError(__("You don't have access to this application because your account was deactivated or removed")); } } else { $auth->auth_succeded = false; $auth->addToError(__("You don't have right to connect")); } } } /** * Set the directory where are store the session file * * @return void **/ public static function setPath() { if ( ini_get("session.save_handler") == "files" && session_status() !== PHP_SESSION_ACTIVE ) { session_save_path(GLPI_SESSION_DIR); } } /** * Start the GLPI php session * * @return void **/ public static function start() { if (session_status() === PHP_SESSION_NONE) { ini_set('session.use_only_cookies', '1'); // Force session to use cookies session_name(self::buildSessionName()); @session_start(); } // Define current time for sync of action timing $_SESSION["glpi_currenttime"] = date("Y-m-d H:i:s"); } /** * Build the session name based on GLPI's folder path + full domain + port * * Adding the full domain name prevent two GLPI instances on the same * domain (e.g. test.domain and prod.domain) with identical folder's * path (e.g. /var/www/glpi) to compete for the same cookie name * * Adding the port prevent some conflicts when using docker * * @param string|null $path Default to GLPI_ROOT * @param string|null $host Default to $_SERVER['HTTP_HOST'] * @param string|null $port Default to $_SERVER['SERVER_PORT'] * * @return string An unique session name */ public static function buildSessionName( ?string $path = null, ?string $host = null, ?string $port = null ): string { if (is_null($path)) { $path = realpath(GLPI_ROOT); } if (is_null($host)) { $host = $_SERVER['HTTP_HOST'] ?? ''; } if (is_null($port)) { $port = $_SERVER['SERVER_PORT'] ?? ''; } return "glpi_" . md5($path . $host . $port); } /** * Get root entity name * * @since 0.84 * * @return string **/ public function getRootEntityName() { if (isset($_SESSION['glpirootentityname'])) { return $_SESSION['glpirootentityname']; } $entity = new Entity(); if ($entity->getFromDB(0)) { $_SESSION['glpirootentityname'] = $entity->fields['name']; } else { $_SESSION['glpirootentityname'] = 'No root entity / DB troubles'; } return $_SESSION['glpirootentityname']; } /** * Is GLPI used in multi-entities mode? * * @return boolean **/ public static function isMultiEntitiesMode() { if (!isset($_SESSION['glpi_multientitiesmode'])) { if (countElementsInTable("glpi_entities") > 1) { $_SESSION['glpi_multientitiesmode'] = 1; } else { $_SESSION['glpi_multientitiesmode'] = 0; } } return $_SESSION['glpi_multientitiesmode']; } /** * Does user have right to see all entities? * * @since 9.3.2 * * @return boolean **/ public static function canViewAllEntities() { // Command line can see all entities return (isCommandLine() || ((countElementsInTable("glpi_entities")) == count($_SESSION["glpiactiveentities"] ?? []))); } /** Add an item to the navigate through search results list * * @param string $itemtype Device type * @param integer $ID ID of the item **/ public static function addToNavigateListItems($itemtype, $ID) { $_SESSION['glpilistitems'][$itemtype][] = $ID; } /** Initialise a list of items to use navigate through search results * * @param string $itemtype Device type * @param string $title List title (default '') **/ public static function initNavigateListItems($itemtype, $title = "", $url = null) { /** @var int $AJAX_INCLUDE */ global $AJAX_INCLUDE; if ($AJAX_INCLUDE && ($url === null)) { return; } if (empty($title)) { $title = __('List'); } if ($url === null) { $url = ''; if (!isset($_SERVER['REQUEST_URI']) || (strpos($_SERVER['REQUEST_URI'], "tabs") > 0)) { if (isset($_SERVER['HTTP_REFERER'])) { $url = $_SERVER['HTTP_REFERER']; } } else { $url = $_SERVER['REQUEST_URI']; } } $_SESSION['glpilisttitle'][$itemtype] = $title; $_SESSION['glpilistitems'][$itemtype] = []; $_SESSION['glpilisturl'][$itemtype] = $url; } /** * Change active entity to the $ID one. Update glpiactiveentities session variable. * Reload groups related to this entity. * * @param integer|string $ID ID of the new active entity ("all"=>load all possible entities) * (default 'all') * @param boolean $is_recursive Also display sub entities of the active entity? (false by default) * * @return boolean true on success, false on failure **/ public static function changeActiveEntities($ID = "all", $is_recursive = false) { $newentities = []; $ancestors = []; if (isset($_SESSION['glpiactiveprofile'])) { if ($ID === "all") { foreach ($_SESSION['glpiactiveprofile']['entities'] as $val) { $ancestors = array_unique(array_merge( getAncestorsOf( "glpi_entities", $val['id'] ), $ancestors )); $newentities[$val['id']] = $val['id']; if ($val['is_recursive']) { $entities = getSonsOf("glpi_entities", $val['id']); if (count($entities)) { foreach ($entities as $key2 => $val2) { $newentities[$key2] = $key2; } } } } } else { $ID = (int)$ID; /// Check entity validity $ancestors = getAncestorsOf("glpi_entities", $ID); $ok = false; foreach ($_SESSION['glpiactiveprofile']['entities'] as $val) { if (($val['id'] == $ID) || in_array($val['id'], $ancestors)) { // Not recursive or recursive and root entity is recursive if (!$is_recursive || $val['is_recursive']) { $ok = true; } } } if (!$ok) { return false; } $newentities[$ID] = $ID; if ($is_recursive) { $entities = getSonsOf("glpi_entities", $ID); if (count($entities)) { foreach ($entities as $key2 => $val2) { $newentities[$key2] = $key2; } } } } } if (count($newentities) > 0) { $_SESSION['glpiactiveentities'] = $newentities; $_SESSION['glpiactiveentities_string'] = "'" . implode("', '", $newentities) . "'"; $active = reset($newentities); $_SESSION['glpiparententities'] = $ancestors; $_SESSION['glpiparententities_string'] = implode("', '", $ancestors); if (!empty($_SESSION['glpiparententities_string'])) { $_SESSION['glpiparententities_string'] = "'" . $_SESSION['glpiparententities_string'] . "'"; } // Active entity loading $_SESSION["glpiactive_entity"] = $active; $_SESSION["glpiactive_entity_recursive"] = $is_recursive; $_SESSION["glpiactive_entity_name"] = Dropdown::getDropdownName( "glpi_entities", $active ); $_SESSION["glpiactive_entity_shortname"] = getTreeLeafValueName("glpi_entities", $active); if ($is_recursive) { //TRANS: %s is the entity name $_SESSION["glpiactive_entity_name"] = sprintf( __('%1$s (%2$s)'), $_SESSION["glpiactive_entity_name"], __('tree structure') ); $_SESSION["glpiactive_entity_shortname"] = sprintf( __('%1$s (%2$s)'), $_SESSION["glpiactive_entity_shortname"], __('tree structure') ); } elseif ($ID == "all") { //TRANS: %s is the entity name $_SESSION["glpiactive_entity_name"] = sprintf( __('%1$s (%2$s)'), $_SESSION["glpiactive_entity_name"], __('full structure') ); $_SESSION["glpiactive_entity_shortname"] = sprintf( __('%1$s (%2$s)'), $_SESSION["glpiactive_entity_shortname"], __('full structure') ); } if (countElementsInTable('glpi_entities') <= count($_SESSION['glpiactiveentities'])) { $_SESSION['glpishowallentities'] = 1; } else { $_SESSION['glpishowallentities'] = 0; } // Clean session variable to search system if (isset($_SESSION['glpisearch']) && count($_SESSION['glpisearch'])) { foreach ($_SESSION['glpisearch'] as $itemtype => $tab) { if (isset($tab['start']) && ($tab['start'] > 0)) { $_SESSION['glpisearch'][$itemtype]['start'] = 0; } } } self::loadGroups(); Plugin::doHook(Hooks::CHANGE_ENTITY); return true; } return false; } /** * Change active profile to the $ID one. Update glpiactiveprofile session variable. * * @param integer $ID ID of the new profile * * @return void **/ public static function changeProfile($ID) { if ( isset($_SESSION['glpiprofiles'][$ID]) && count($_SESSION['glpiprofiles'][$ID]['entities']) ) { $profile = new Profile(); if ($profile->getFromDB($ID)) { $profile->cleanProfile(); $data = $profile->fields; $data['entities'] = $_SESSION['glpiprofiles'][$ID]['entities']; $_SESSION['glpiactiveprofile'] = $data; $_SESSION['glpiactiveentities'] = []; Search::resetSaveSearch(); $active_entity_done = false; // Try to load default entity if it is a root entity foreach ($data['entities'] as $val) { if ($val['id'] == $_SESSION["glpidefault_entity"]) { if (self::changeActiveEntities($val['id'], $val['is_recursive'])) { $active_entity_done = true; } } } if (!$active_entity_done) { // Try to load default entity if (!self::changeActiveEntities($_SESSION["glpidefault_entity"], true)) { // Load all entities self::changeActiveEntities("all"); } } Plugin::doHook(Hooks::CHANGE_PROFILE); } } // Clean specific datas if (isset($_SESSION['glpimenu'])) { unset($_SESSION['glpimenu']); } } /** * Set the entities session variable. Load all entities from DB * * @param integer $userID ID of the user * * @return void **/ public static function initEntityProfiles($userID) { /** @var \DBmysql $DB */ global $DB; $_SESSION['glpiprofiles'] = []; if (!$DB->tableExists('glpi_profiles_users')) { //table does not exists in old GLPI versions return; } $iterator = $DB->request([ 'SELECT' => [ 'glpi_profiles.id', 'glpi_profiles.name' ], 'DISTINCT' => true, 'FROM' => 'glpi_profiles_users', 'INNER JOIN' => [ 'glpi_profiles' => [ 'ON' => [ 'glpi_profiles_users' => 'profiles_id', 'glpi_profiles' => 'id' ] ] ], 'WHERE' => [ 'glpi_profiles_users.users_id' => $userID ], 'ORDERBY' => 'glpi_profiles.name' ]); if (count($iterator)) { foreach ($iterator as $data) { $key = $data['id']; $_SESSION['glpiprofiles'][$key]['name'] = $data['name']; $entities_iterator = $DB->request([ 'SELECT' => [ 'glpi_profiles_users.entities_id AS eID', 'glpi_profiles_users.id AS kID', 'glpi_profiles_users.is_recursive', 'glpi_entities.*' ], 'FROM' => 'glpi_profiles_users', 'LEFT JOIN' => [ 'glpi_entities' => [ 'ON' => [ 'glpi_profiles_users' => 'entities_id', 'glpi_entities' => 'id' ] ] ], 'WHERE' => [ 'glpi_profiles_users.profiles_id' => $key, 'glpi_profiles_users.users_id' => $userID ], 'ORDERBY' => 'glpi_entities.completename' ]); foreach ($entities_iterator as $data) { // Do not override existing entity if define as recursive if ( !isset($_SESSION['glpiprofiles'][$key]['entities'][$data['eID']]) || $data['is_recursive'] ) { $_SESSION['glpiprofiles'][$key]['entities'][$data['eID']] = [ 'id' => $data['eID'], 'name' => $data['name'], 'is_recursive' => $data['is_recursive'] ]; } } } } } /** * Load current user's group on active entity * * @return void **/ public static function loadGroups() { /** @var \DBmysql $DB */ global $DB; $_SESSION["glpigroups"] = []; $iterator = $DB->request([ 'SELECT' => Group_User::getTable() . '.groups_id', 'FROM' => Group_User::getTable(), 'LEFT JOIN' => [ Group::getTable() => [ 'ON' => [ Group::getTable() => 'id', Group_User::getTable() => 'groups_id' ] ] ], 'WHERE' => [ Group_User::getTable() . '.users_id' => self::getLoginUserID() ] + getEntitiesRestrictCriteria( Group::getTable(), 'entities_id', $_SESSION['glpiactiveentities'], true ) ]); foreach ($iterator as $data) { $_SESSION["glpigroups"][] = $data["groups_id"]; } } /** * Include the good language dict. * * Get the default language from current user in $_SESSION["glpilanguage"]. * And load the dict that correspond. * * @param string $forcelang Force to load a specific lang * @param boolean $with_plugins Whether to load plugin languages or not * * @return void **/ public static function loadLanguage($forcelang = '', $with_plugins = true) { /** * @var array $CFG_GLPI * @var \Laminas\I18n\Translator\TranslatorInterface $TRANSLATE */ global $CFG_GLPI, $TRANSLATE; if (!isset($_SESSION["glpilanguage"])) { $_SESSION["glpilanguage"] = self::getPreferredLanguage(); } $trytoload = $_SESSION["glpilanguage"]; // Force to load a specific lang if (!empty($forcelang)) { $trytoload = $forcelang; } // If not set try default lang file if (empty($trytoload)) { $trytoload = $CFG_GLPI["language"]; } if (isset($CFG_GLPI["languages"][$trytoload])) { $newfile = "/" . $CFG_GLPI["languages"][$trytoload][1]; } if (empty($newfile) || !is_file(GLPI_I18N_DIR . $newfile)) { $newfile = "/en_GB.mo"; } if (isset($CFG_GLPI["languages"][$trytoload][5])) { $_SESSION['glpipluralnumber'] = $CFG_GLPI["languages"][$trytoload][5]; } // Redefine Translator caching logic to be able to drop laminas/laminas-cache dependency. $i18n_cache = !defined('TU_USER') ? new I18nCache((new CacheManager())->getTranslationsCacheInstance()) : null; $TRANSLATE = new class ($i18n_cache) extends Laminas\I18n\Translator\Translator { public function __construct(?I18nCache $cache) { $this->cache = $cache; } }; $TRANSLATE->setLocale($trytoload); if (class_exists('Locale')) { // Locale class may be missing if intl extension is not installed. // In this case, we may still want to be able to load translations (for instance for requirements checks). \Locale::setDefault($trytoload); } else { trigger_error('Missing required intl PHP extension', E_USER_WARNING); } $TRANSLATE->addTranslationFile('gettext', GLPI_I18N_DIR . $newfile, 'glpi', $trytoload); $core_folders = is_dir(GLPI_LOCAL_I18N_DIR) ? scandir(GLPI_LOCAL_I18N_DIR) : []; $core_folders = array_filter($core_folders, function ($dir) { if (!is_dir(GLPI_LOCAL_I18N_DIR . "/$dir")) { return false; } if ($dir == 'core') { return true; } return str_starts_with($dir, 'core_'); }); foreach ($core_folders as $core_folder) { $mofile = GLPI_LOCAL_I18N_DIR . "/$core_folder/" . $newfile; $phpfile = str_replace('.mo', '.php', $mofile); // Load local PHP file if it exists if (file_exists($phpfile)) { $TRANSLATE->addTranslationFile('phparray', $phpfile, 'glpi', $trytoload); } // Load local MO file if it exists -- keep last so it gets precedence if (file_exists($mofile)) { $TRANSLATE->addTranslationFile('gettext', $mofile, 'glpi', $trytoload); } } // Load plugin dicts if ($with_plugins) { foreach (Plugin::getPlugins() as $plug) { Plugin::loadLang($plug, $forcelang, $trytoload); } } return $trytoload; } /** * Return preffered language (from HTTP headers, fallback to default GLPI lang). * * @return string */ public static function getPreferredLanguage(): string { /** @var array $CFG_GLPI */ global $CFG_GLPI; // Extract accepted languages from headers // Accept-Language: fr-FR, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 $accepted_languages = []; $values = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''); foreach ($values as $value) { $parts = explode(';q=', trim($value)); $language = str_replace('-', '_', $parts[0]); $qfactor = $parts[1] ?? 1; //q-factor defaults to 1 $accepted_languages[$language] = $qfactor; } arsort($accepted_languages); // sort by qfactor foreach (array_keys($accepted_languages) as $language) { if (array_key_exists($language, $CFG_GLPI['languages'])) { return $language; } } if (isset($CFG_GLPI['language'])) { // Default config in GLPI >= 0.72 return $CFG_GLPI['language']; } else if (isset($CFG_GLPI['default_language'])) { // Default config in GLPI < 0.72 : keep it for upgrade process return $CFG_GLPI['default_language']; } return 'en_GB'; } /** * Get plural form number * * @return integer */ public static function getPluralNumber() { /** @var int $DEFAULT_PLURAL_NUMBER */ global $DEFAULT_PLURAL_NUMBER; if (isset($_SESSION['glpipluralnumber'])) { return $_SESSION['glpipluralnumber']; } else { return $DEFAULT_PLURAL_NUMBER; } } /** * Detect cron mode or interactive * * @since 0.84 * * @return boolean **/ public static function isCron() { return (self::isInventory() || isset($_SESSION["glpicronuserrunning"]) && (isCommandLine() || strpos($_SERVER['PHP_SELF'], '/cron.php'))); } /** * Detect inventory mode * * @return boolean **/ public static function isInventory(): bool { return (isset($_SESSION["glpiinventoryuserrunning"]) && ( strpos($_SERVER['PHP_SELF'], '/inventory.php') !== false || strpos($_SERVER['PHP_SELF'], '/index.php') !== false || defined('TU_USER') ) ); } /** * Get the Login User ID or return cron user ID for cron jobs * * @param boolean $force_human Force human / do not return cron user (true by default) * * @return false|int|string false if user is not logged in * int for user id, string for cron jobs **/ public static function getLoginUserID($force_human = true) { if (self::isInventory()) { // Check inventory return $_SESSION["glpiinventoryuserrunning"]; } if ( !$force_human && self::isCron() ) { // Check cron jobs return $_SESSION["glpicronuserrunning"] ?? $_SESSION['glpiinventoryuserrunning']; } if (isset($_SESSION["glpiID"])) { return $_SESSION["glpiID"]; } return false; } /** * Redirect User to login if not logged in * * @since 0.85 * * @return void **/ public static function redirectIfNotLoggedIn() { if (!self::getLoginUserID()) { Html::redirectToLogin(); } } /** * Global check of session to prevent PHP vulnerability * * @since 0.85 * * @see https://wiki.php.net/rfc/strict_sessions * * @return void|true **/ public static function checkValidSessionId() { /** @var \DBmysql $DB */ global $DB; if ( !isset($_SESSION['valid_id']) || ($_SESSION['valid_id'] !== session_id()) ) { Html::redirectToLogin('error=3'); } $user_id = self::getLoginUserID(); $profile_id = $_SESSION['glpiactiveprofile']['id'] ?? null; $entity_id = $_SESSION['glpiactive_entity'] ?? null; $valid_user = true; if (!is_numeric($user_id) || $profile_id === null || $entity_id === null) { $valid_user = false; } else { $user_table = User::getTable(); $pu_table = Profile_User::getTable(); $result = $DB->request( [ 'COUNT' => 'count', 'FROM' => $user_table, 'LEFT JOIN' => [ $pu_table => [ 'FKEY' => [ Profile_User::getTable() => 'users_id', $user_table => 'id' ] ] ], 'WHERE' => [ $user_table . '.id' => $user_id, $user_table . '.is_active' => 1, $user_table . '.is_deleted' => 0, $pu_table . '.profiles_id' => $profile_id, ] + getEntitiesRestrictCriteria($pu_table, 'entities_id', $entity_id, true), ] ); if ($result->current()['count'] === 0) { $valid_user = false; } } if (!$valid_user) { Session::destroy(); Auth::setRememberMeCookie(''); Html::redirectToLogin(); } return true; } /** * Check if I have access to the central interface * * @return void **/ public static function checkCentralAccess() { self::checkValidSessionId(); if (Session::getCurrentInterface() != "central") { // Gestion timeout session self::redirectIfNotLoggedIn(); Html::displayRightError("The current profile does not use the standard interface"); } } /** * Check if I have the right to access to the FAQ (profile or anonymous FAQ) * * @return void **/ public static function checkFaqAccess() { /** @var array $CFG_GLPI */ global $CFG_GLPI; if (!$CFG_GLPI["use_public_faq"]) { self::checkValidSessionId(); if (!Session::haveRightsOr('knowbase', [KnowbaseItem::READFAQ, READ])) { Html::displayRightError("Missing FAQ right"); } } } /** * Check if I have access to the helpdesk interface * * @return void **/ public static function checkHelpdeskAccess() { self::checkValidSessionId(); if (Session::getCurrentInterface() != "helpdesk") { // Gestion timeout session self::redirectIfNotLoggedIn(); Html::displayRightError("The current profile does not use the simplified interface"); } } /** * Check if I am logged in * * @return void **/ public static function checkLoginUser() { self::checkValidSessionId(); if (!isset($_SESSION["glpiname"])) { // Gestion timeout session self::redirectIfNotLoggedIn(); Html::displayRightError("User has no valid session but seems to be logged in"); } } /** * Get the name of the right. * This should only be used when it is expected that the request going to be terminated. * The session will be closed by this method. * * @param string $module The module * @param int $right The right * @return string The right name * @internal No backwards compatibility promise. Use in core only. */ public static function getRightNameForError(string $module, int $right): string { // Well known rights $rights = [ READ => 'READ', UPDATE => 'UPDATE', CREATE => 'CREATE', DELETE => 'DELETE', PURGE => 'PURGE', ALLSTANDARDRIGHT => 'ALLSTANDARDRIGHT', READNOTE => 'READNOTE', UPDATENOTE => 'UPDATENOTE', UNLOCK => 'UNLOCK', ]; // Close session and force the default language so the logged right name is standardized session_write_close(); $current_lang = $_SESSION['glpilanguage']; self::loadLanguage('en_GB'); $all_specific_rights = Profile::getRightsForForm(self::getCurrentInterface()); $specific_rights = []; foreach ($all_specific_rights as $forms) { foreach ($forms as $group_rights) { foreach ($group_rights as $right_definition) { if ($right_definition['field'] === $module) { $rights_arr = $right_definition['rights']; foreach ($rights_arr as $right_val => $right_label) { $label = $right_label; if (is_array($label) && isset($label['short'])) { $label = $label['short']; } if (!is_array($label)) { $specific_rights[$right_val] = $label; } } } } } } // Restore language so the error displayed is in the user language self::loadLanguage($current_lang); return $specific_rights[$right] ?? $rights[$right] ?? 'unknown right name'; } /** * Check if I have the right $right to module $module (conpare to session variable) * * @param string $module Module to check * @param integer $right Right to check * * @return void **/ public static function checkRight($module, $right) { self::checkValidSessionId(); if (!self::haveRight($module, $right)) { // Gestion timeout session self::redirectIfNotLoggedIn(); $right_name = self::getRightNameForError($module, $right); Html::displayRightError("User is missing the $right ($right_name) right for $module"); } } /** * Check if I one right of array $rights to module $module (conpare to session variable) * * @param string $module Module to check * @param array $rights Rights to check * * @return void **/ public static function checkRightsOr($module, $rights = []) { self::checkValidSessionId(); if (!self::haveRightsOr($module, $rights)) { self::redirectIfNotLoggedIn(); $info = "User is missing all of the following rights: "; foreach ($rights as $right) { $right_name = self::getRightNameForError($module, $right); $info .= $right . "($right_name), "; } $info = substr($info, 0, -2); $info .= " for $module"; Html::displayRightError($info); } } /** * Check if I have one of the right specified * * You can't use this function if several rights for same module name * * @param array $modules Array of modules where keys are modules and value are right * * @return void **/ public static function checkSeveralRightsOr($modules) { self::checkValidSessionId(); $valid = false; if (count($modules)) { foreach ($modules as $mod => $right) { // Itemtype if (preg_match('/[A-Z]/', $mod[0])) { if ($item = getItemForItemtype($mod)) { if ($item->canGlobal($right)) { $valid = true; } } } else if (self::haveRight($mod, $right)) { $valid = true; } } } if (!$valid) { // Gestion timeout session self::redirectIfNotLoggedIn(); $info = "User is missing all of the following rights: "; foreach ($modules as $mod => $right) { $right_name = self::getRightNameForError($mod, $right); $info .= $right . "($right_name) for module $mod, "; } $info = substr($info, 0, -2); Html::displayRightError($info); } } /** * Check if you could access to ALL the entities of an list * * @param array $tab List ID of entities * * @return boolean **/ public static function haveAccessToAllOfEntities($tab) { if (is_array($tab) && count($tab)) { foreach ($tab as $val) { if (!self::haveAccessToEntity($val)) { return false; } } } return true; } /** * Check if you could access (read) to the entity of id = $ID * * @param integer $ID ID of the entity * @param boolean $is_recursive if recursive item (default false) * * @return boolean **/ public static function haveAccessToEntity($ID, $is_recursive = false) { // Quick response when passing wrong ID : default value of getEntityID is -1 if ($ID < 0) { return false; } if (!isset($_SESSION['glpiactiveentities'])) { return false; } if (in_array($ID, $_SESSION['glpiactiveentities'])) { return true; } if (!$is_recursive) { return false; } /// Recursive object return in_array($ID, getAncestorsOf("glpi_entities", $_SESSION['glpiactiveentities'])); } /** * Check if you could access to one entity of a list * * @param array $tab list ID of entities * @param boolean $is_recursive if recursive item (default false) * * @return boolean **/ public static function haveAccessToOneOfEntities($tab, $is_recursive = false) { if (is_array($tab) && count($tab)) { foreach ($tab as $val) { if (self::haveAccessToEntity($val, $is_recursive)) { return true; } } } return false; } /** * Check if you could create recursive object in the entity of id = $ID * * @param integer $ID ID of the entity * * @return boolean **/ public static function haveRecursiveAccessToEntity($ID) { // Right by profile foreach ($_SESSION['glpiactiveprofile']['entities'] as $val) { if ($val['id'] == $ID) { return $val['is_recursive']; } } // Right is from a recursive profile if (isset($_SESSION['glpiactiveentities'])) { return in_array($ID, $_SESSION['glpiactiveentities']); } return false; } /** * Have I the right $right to module $module (conpare to session variable) * * @param string $module Module to check * @param integer $right Right to check * * @return boolean **/ public static function haveRight($module, $right) { /** @var \DBmysql $DB */ global $DB; if (Session::isInventory()) { return true; } //If GLPI is using the slave DB -> read only mode if ( $DB->isSlave() && ($right & (CREATE | UPDATE | DELETE | PURGE)) ) { return false; } if (isset($_SESSION["glpiactiveprofile"][$module])) { return intval($_SESSION["glpiactiveprofile"][$module]) & $right; } return false; } /** * Have I all rights of array $rights to module $module (conpare to session variable) * * @param string $module Module to check * @param integer[] $rights Rights to check * * @return boolean **/ public static function haveRightsAnd($module, $rights = []) { foreach ($rights as $right) { if (!Session::haveRight($module, $right)) { return false; } } return true; } /** * Have I one right of array $rights to module $module (conpare to session variable) * * @param string $module Module to check * @param integer[] $rights Rights to check * * @return boolean **/ public static function haveRightsOr($module, $rights = []) { foreach ($rights as $right) { if (Session::haveRight($module, $right)) { return true; } } return false; } /** * Get active Tab for an itemtype * * @param string $itemtype item type * * @return string **/ public static function getActiveTab($itemtype) { if (isset($_SESSION['glpi_tabs'][strtolower($itemtype)])) { return $_SESSION['glpi_tabs'][strtolower($itemtype)]; } return ""; } /** * Add a message to be displayed after redirect * * @param string $msg Message to add * @param boolean $check_once Check if the message is not already added (false by default) * @param integer $message_type Message type (INFO, WARNING, ERROR) (default INFO) * @param boolean $reset Clear previous added message (false by default) * * @return void **/ public static function addMessageAfterRedirect( $msg, $check_once = false, $message_type = INFO, $reset = false ) { if (!empty($msg)) { if (self::isCron()) { // We are in cron mode // Do not display message in user interface, but record error if ($message_type == ERROR) { Toolbox::logInFile('cron', $msg . "\n"); } } else { $array = &$_SESSION['MESSAGE_AFTER_REDIRECT']; if ($reset) { $array = []; } if (!isset($array[$message_type])) { $array[$message_type] = []; } if ( !$check_once || !isset($array[$message_type]) || in_array($msg, $array[$message_type]) === false ) { $array[$message_type][] = $msg; } } } } /** * Force active Tab for an itemtype * * @param string $itemtype item type * @param mixed $tab ID of the tab * * @return void **/ public static function setActiveTab($itemtype, $tab) { $_SESSION['glpi_tabs'][strtolower($itemtype)] = $tab; } /** * Get a saved option from request or session * if get from request, save it * * @since 0.83 * * @param string $itemtype name of itemtype * @param string $name name of the option * @param mixed $defvalue mixed default value for option * * @return mixed **/ public static function getSavedOption($itemtype, $name, $defvalue) { if (isset($_REQUEST[$name])) { return $_SESSION['glpi_saved'][$itemtype][$name] = $_REQUEST[$name]; } if (isset($_SESSION['glpi_saved'][$itemtype][$name])) { return $_SESSION['glpi_saved'][$itemtype][$name]; } return $defvalue; } /** * Is the current account read-only * * @since 0.83 * * @return boolean **/ public static function isReadOnlyAccount() { foreach ($_SESSION['glpiactiveprofile'] as $name => $val) { if ( is_numeric($val) && ($name != 'search_config') && ($val & ~READ) ) { return false; } } return true; } /** * Get new CSRF token * * @param bool $standalone * Generates a standalone token that will not be shared with other component of current request. * * @since 0.83.3 * * @return string **/ public static function getNewCSRFToken(bool $standalone = false) { /** @var string $CURRENTCSRFTOKEN */ global $CURRENTCSRFTOKEN; $token = $standalone ? '' : $CURRENTCSRFTOKEN; if (empty($token)) { do { $token = bin2hex(random_bytes(32)); } while ($token == ''); } if (!isset($_SESSION['glpicsrftokens'])) { $_SESSION['glpicsrftokens'] = []; } $_SESSION['glpicsrftokens'][$token] = time() + GLPI_CSRF_EXPIRES; if (!$standalone) { $CURRENTCSRFTOKEN = $token; } return $token; } /** * Clean expired CSRF tokens * * @since 0.83.3 * * @return void **/ public static function cleanCSRFTokens() { $now = time(); if (isset($_SESSION['glpicsrftokens']) && is_array($_SESSION['glpicsrftokens'])) { if (count($_SESSION['glpicsrftokens'])) { foreach ($_SESSION['glpicsrftokens'] as $token => $expires) { if ($expires < $now) { unset($_SESSION['glpicsrftokens'][$token]); } } $overflow = count($_SESSION['glpicsrftokens']) - GLPI_CSRF_MAX_TOKENS; if ($overflow > 0) { $_SESSION['glpicsrftokens'] = array_slice( $_SESSION['glpicsrftokens'], $overflow + 1, null, true ); } } } } /** * Validate that the page has a CSRF token in the POST data * and that the token is legit/not expired. If the token is valid * it will be removed from the list of valid tokens. * * @since 0.83.3 * * @param array $data $_POST data * * @return boolean **/ public static function validateCSRF($data) { if (!isset($data['_glpi_csrf_token'])) { Session::cleanCSRFTokens(); return false; } $requestToken = $data['_glpi_csrf_token']; if ( isset($_SESSION['glpicsrftokens'][$requestToken]) && ($_SESSION['glpicsrftokens'][$requestToken] >= time()) ) { if (!defined('GLPI_KEEP_CSRF_TOKEN')) { /* When post open a new windows */ unset($_SESSION['glpicsrftokens'][$requestToken]); } Session::cleanCSRFTokens(); return true; } Session::cleanCSRFTokens(); return false; } /** * Check CSRF data * * @since 0.84.2 * * @param array $data $_POST data * * @return void **/ public static function checkCSRF($data) { $message = __("The action you have requested is not allowed."); if ( ($requestToken = $data['_glpi_csrf_token'] ?? null) !== null && isset($_SESSION['glpicsrftokens'][$requestToken]) && ($_SESSION['glpicsrftokens'][$requestToken] < time()) ) { $message = __("Your session has expired."); } if ( GLPI_USE_CSRF_CHECK && (!Session::validateCSRF($data)) ) { $requested_url = (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : 'Unknown'); $user_id = self::getLoginUserID() ?? 'Anonymous'; Toolbox::logInFile('access-errors', "CSRF check failed for User ID: $user_id at $requested_url\n"); // Output JSON if requested by client if (strpos($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json') !== false) { http_response_code(403); die(json_encode(["message" => $message])); } Html::displayErrorAndDie($message, true); } } /** * Get new IDOR token * This token validates the itemtype used by an ajax request is the one asked by a dropdown. * So, we avoid IDOR request where an attacker asks for an another itemtype * than the originaly intended * * @since 9.5.3 * * @param string $itemtype * @param array $add_params more criteria to check validy of idor tokens * * @return string **/ public static function getNewIDORToken(string $itemtype = "", array $add_params = []): string { if ($itemtype === '' && count($add_params) === 0) { trigger_error('IDOR token cannot be generated with empty criteria.', E_USER_WARNING); return ''; } $token = ""; do { $token = bin2hex(random_bytes(32)); } while ($token == ''); if (!isset($_SESSION['glpiidortokens'])) { $_SESSION['glpiidortokens'] = []; } $_SESSION['glpiidortokens'][$token] = [ 'expires' => time() + GLPI_IDOR_EXPIRES ] + ($itemtype !== "" ? ['itemtype' => $itemtype] : []) + $add_params; return $token; } /** * Validate that the page has a IDOR token in the POST data * and that the token is legit/not expired. * Tokens are kept in session until their time is expired (by default 2h) * to permits multiple ajax calls for a dropdown * * @since 9.5.3 * * @param array $data $_POST data * * @return boolean **/ public static function validateIDOR(array $data = []): bool { self::cleanIDORTokens(); if (!isset($data['_idor_token'])) { return false; } $token = $data['_idor_token']; if ( isset($_SESSION['glpiidortokens'][$token]) && $_SESSION['glpiidortokens'][$token]['expires'] >= time() ) { $idor_data = $_SESSION['glpiidortokens'][$token]; unset($idor_data['expires']); // Ensure that `displaywith` and `condition` is checked if passed in data $mandatory_properties = [ 'displaywith' => [], 'condition' => [], ]; foreach ($mandatory_properties as $property_name => $default_value) { if (!array_key_exists($property_name, $data)) { $data[$property_name] = $default_value; } if (!array_key_exists($property_name, $idor_data)) { $idor_data[$property_name] = $default_value; } } // check all stored data for the idor token are present (and identical) in the posted data $match_expected = function ($expected, $given) use (&$match_expected) { if (is_array($expected)) { if (!is_array($given)) { return false; } foreach ($expected as $key => $value) { if (!array_key_exists($key, $given) || !$match_expected($value, $given[$key])) { return false; } } return true; } else { return $expected == $given; } }; // Check also unsanitized data, as sanitizing process may alter expected data. $unsanitized_data = Sanitizer::unsanitize($data); return $match_expected($idor_data, $data) || $match_expected($idor_data, $unsanitized_data); } return false; } /** * Clean expired IDOR tokens * * @since 9.5.3 * * @return void **/ public static function cleanIDORTokens() { $now = time(); if (isset($_SESSION['glpiidortokens']) && is_array($_SESSION['glpiidortokens'])) { foreach ($_SESSION['glpiidortokens'] as $footprint => $token) { if ($token['expires'] < $now) { unset($_SESSION['glpiidortokens'][$footprint]); } } } } /** * Is field having translations ? * * @since 0.85 * * @param string $itemtype itemtype * @param string $field field * * @return boolean **/ public static function haveTranslations($itemtype, $field) { return (isset($_SESSION['glpi_dropdowntranslations'][$itemtype]) && isset($_SESSION['glpi_dropdowntranslations'][$itemtype][$field])); } /** * Get current interface name extracted from session var (if exists) * * @since 9.2.2 * * @return string|false Returns "helpdesk" or "central" if there is a session and the interface property is set. * Returns false if there is no session or the interface property is not set. */ public static function getCurrentInterface() { return $_SESSION['glpiactiveprofile']['interface'] ?? false; } /** * Check if current user can impersonate another user having given id. * * @param integer $user_id * * @return boolean */ public static function canImpersonate($user_id, ?string &$message = null) { if ( $user_id <= 0 || self::getLoginUserID() == $user_id || (self::isImpersonateActive() && self::getImpersonatorId() == $user_id) ) { $message = __("You can't impersonate yourself."); return false; // Cannot impersonate invalid user, self, or already impersonated user } // Cannot impersonate if we don't have config right if (!self::haveRight(Config::$rightname, UPDATE)) { return false; } // Cannot impersonate inactive user $user = new User(); if (!$user->getFromDB($user_id) || !$user->getField('is_active')) { $message = __("The user is not active."); return false; } // Cannot impersonate user with no profile if (Profile_User::getUserProfiles($user_id) == []) { $message = __("The user doesn't have any profile."); return false; } return true; } /** * Impersonate user having given id. * * @param integer $user_id * * @return boolean */ public static function startImpersonating($user_id) { if (!self::canImpersonate($user_id)) { return false; } $user = new User(); if (!$user->getFromDB($user_id)) { return false; } //store user who impersonated another user $impersonator = $_SESSION['glpiname']; // Store current user values $impersonator_id = self::isImpersonateActive() ? $_SESSION['impersonator_id'] : self::getLoginUserID(); $lang = $_SESSION['glpilanguage']; $session_use_mode = $_SESSION['glpi_use_mode']; $auth = new Auth(); $auth->auth_succeded = true; $auth->user = $user; Session::init($auth); // Force usage of current user lang and session mode $_SESSION['glpilanguage'] = $lang; $_SESSION['glpi_use_mode'] = $session_use_mode; Session::loadLanguage(); $_SESSION['impersonator_id'] = $impersonator_id; Event::log(0, "system", 3, "Impersonate", sprintf( __('%1$s starts impersonating user %2$s'), $impersonator, $user->fields['name'] )); return true; } /** * Stop impersonating any user. * * @return boolean */ public static function stopImpersonating() { if (!self::isImpersonateActive()) { return true; // Nothing to do } $user = new User(); if (!$user->getFromDB($_SESSION['impersonator_id'])) { return false; } //store user which was impersonated by another user $impersonate_user = $_SESSION['glpiname']; $auth = new Auth(); $auth->auth_succeded = true; $auth->user = $user; Session::init($auth); Event::log(0, "system", 3, "Impersonate", sprintf( __('%1$s stops impersonating user %2$s'), $user->fields['name'], $impersonate_user )); return true; } /** * Check if impersonate feature is currently used. * * @return boolean */ public static function isImpersonateActive() { return array_key_exists('impersonator_id', $_SESSION); } /** * Return impersonator user id. * * @return string|null */ public static function getImpersonatorId() { return self::isImpersonateActive() ? $_SESSION['impersonator_id'] : null; } /** * Check if current connected user password has expired. * * @return boolean */ public static function mustChangePassword() { return array_key_exists('glpi_password_expired', $_SESSION); } /** * Get active entity id. * * @since 9.5 * * @return int */ public static function getActiveEntity() { return $_SESSION['glpiactive_entity'] ?? 0; } /** * Filter given entities ID list to return only these tht are matching current active entities in session. * * @since 10.0.13 * * @param int|int[] $entities_ids * * @return int|int[] */ public static function getMatchingActiveEntities(/*int|array*/ $entities_ids)/*: int|array*/ { if ( (int)$entities_ids === -1 || (is_array($entities_ids) && count($entities_ids) === 1 && (int)reset($entities_ids) === -1) ) { // Special value that is generally used to fallback to all active entities. return $entities_ids; } if ( !is_array($entities_ids) && !is_int($entities_ids) && (!is_string($entities_ids) || !ctype_digit($entities_ids)) ) { // Unexpected value type. return []; } $active_entities_ids = []; foreach ($_SESSION['glpiactiveentities'] ?? [] as $active_entity_id) { if ( !is_int($active_entity_id) && (!is_string($active_entity_id) || !ctype_digit($active_entity_id)) ) { // Ensure no unexpected value converted to int // as it would be converted to `0` and would permit access to root entity trigger_error( sprintf('Unexpected value `%s` found in `$_SESSION[\'glpiactiveentities\']`.', $active_entity_id ?? 'null'), E_USER_WARNING ); continue; } $active_entities_ids[] = (int)$active_entity_id; } if (!is_array($entities_ids) && in_array((int)$entities_ids, $active_entities_ids, true)) { return (int)$entities_ids; } $filtered = []; foreach ((array)$entities_ids as $entity_id) { if ( (is_int($entity_id) || (is_string($entity_id) && ctype_digit($entity_id))) && in_array((int)$entity_id, $active_entities_ids, true) ) { $filtered[] = (int)$entity_id; } } return $filtered; } /** * Get recursive state of active entity selection. * * @since 9.5.5 * * @return bool */ public static function getIsActiveEntityRecursive(): bool { return $_SESSION['glpiactive_entity_recursive'] ?? false; } /** * Start session for a given user * * @param string $token * @param string $token_type * @param int|null $entities_id * @param bool|null $is_recursive * * @return User|false */ public static function authWithToken( string $token, string $token_type, ?int $entities_id, ?bool $is_recursive ) { $user = new User(); // Try to load from token if (!$user->getFromDBByToken($token, $token_type)) { return false; } $auth = new Auth(); $auth->auth_succeded = true; $auth->user = $user; Session::init($auth); if (!is_null($entities_id) && !is_null($is_recursive)) { self::loadEntity($entities_id, $is_recursive); } return $user; } /** * Load given entity. * * @param integer $entities_id Entity to use * @param boolean $is_recursive Whether to load entities recursivly or not * * @return void */ public static function loadEntity($entities_id, $is_recursive): void { $_SESSION["glpiactive_entity"] = $entities_id; $_SESSION["glpiactive_entity_recursive"] = $is_recursive; if ($is_recursive) { $entities = getSonsOf("glpi_entities", $entities_id); } else { $entities = [$entities_id]; } $_SESSION['glpiactiveentities'] = $entities; $_SESSION['glpiactiveentities_string'] = "'" . implode("', '", $entities) . "'"; } /** * clean what needs to be cleaned on logout * * @since 10.0.4 * * @return void */ public static function cleanOnLogout() { Session::destroy(); //Remove cookie to allow new login Auth::setRememberMeCookie(''); } /** * Get the current language * * @return null|string */ public static function getLanguage(): ?string { return $_SESSION['glpilanguage'] ?? null; } /** * Helper function to get the date stored in $_SESSION['glpi_currenttime'] * * @return null|string */ public static function getCurrentTime(): ?string { // TODO (10.1 refactoring): replace references to $_SESSION['glpi_currenttime'] by a call to this function return $_SESSION['glpi_currenttime'] ?? null; } }