Current File : /home/escuelai/public_html/it/src/Session.php |
<?php
/**
* ---------------------------------------------------------------------
*
* GLPI - Gestionnaire Libre de Parc Informatique
*
* http://glpi-project.org
*
* @copyright 2015-2022 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)
}
/**
* Init session for the user is defined
*
* @param Auth $auth Auth object to init session
*
* @return void
**/
public static function init(Auth $auth)
{
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) {
// Force session to use cookies and prevent JS scripts to access to them
ini_set('session.cookie_httponly', '1');
ini_set('session.use_only_cookies', '1');
session_name("glpi_" . md5(realpath(GLPI_ROOT)));
@session_start();
}
// Define current time for sync of action timing
$_SESSION["glpi_currenttime"] = date("Y-m-d H:i:s");
}
/**
* 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 = "")
{
global $AJAX_INCLUDE;
if (isset($AJAX_INCLUDE)) {
return;
}
if (empty($title)) {
$title = __('List');
}
$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|'All' $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 = [];
if (isset($_SESSION['glpiactiveprofile'])) {
if ($ID === "all") {
$ancestors = [];
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 {
/// 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 || $ID == "all") {
//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')
);
}
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)
{
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()
{
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)
{
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
{
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()
{
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')
|| strpos($_SERVER['PHP_SELF'], '/index.php')
|| 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()
{
if (
!isset($_SESSION['valid_id'])
|| ($_SESSION['valid_id'] !== session_id())
) {
Html::redirectToLogin('error=3');
}
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()
{
global $CFG_GLPI;
if (!$CFG_GLPI["use_public_faq"]) {
self::checkValidSessionId();
if (!self::haveRight('knowbase', KnowbaseItem::READFAQ)) {
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 only works for well-known rights.
* @param int $right The right
* @return string The right name
* @internal No backwards compatibility promise. Use in core only.
*/
public static function getRightNameForError(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',
];
if (in_array($right, $rights, true)) {
return $rights[$right];
}
return "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($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($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($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 0)
*
* @return boolean
**/
public static function haveAccessToEntity($ID, $is_recursive = 0)
{
// 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 an list
*
* @param array $tab list ID of entities
* @param boolean $is_recursive if recursive item (default 0)
*
* @return boolean
**/
public static function haveAccessToOneOfEntities($tab, $is_recursive = 0)
{
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)
{
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 integer $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)
{
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)
{
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");
// Output JSON if requested by client
if (strpos($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json') !== false) {
http_response_code(403);
die(json_encode(["message" => __("The action you have requested is not allowed.")]));
}
Html::displayErrorAndDie(__("The action you have requested is not allowed."), 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 indtended
*
* @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
{
$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']);
// check all stored data for the idor token are present (and identifical) 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;
}
/**
* 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 int $users_id ID of the user
*
* @return User|bool
*/
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) . "'";
}
}