getSitePath(); session_set_cookie_params($lifetime, $path); $currentCookieParams = session_get_cookie_params(); session_name(SESSION_NAME); session_start(); // Currently, setcookie() is only called to really extend the lifetime. All other parameter needs to be given again. setcookie(SESSION_NAME, session_id(), time() + $lifetime, $path, $currentCookieParams['domain'], $currentCookieParams['secure'], true); self::$sessionId = session_id(); } self::$sessionOpen = true; self::checkFeUserUid(); } /** * Extract the SitePath of the current T3 installation. * * @return bool|string - with a trailing '/' * @throws CodeException */ private static function getSitePath() { if (empty($_SERVER['SCRIPT_NAME'])) { throw new CodeException('Missing _SERVER[SCRIPT_NAME]', ERROR_SESSION_BROKEN_SCRIPT_PATH); } $path = $_SERVER['SCRIPT_NAME']; $pos = strrpos($path, '/'); if ($pos === false) { throw new CodeException("Broken _SERVER[SCRIPT_NAME]: $path", ERROR_SESSION_BROKEN_SCRIPT_PATH); } // Remove PHP script $path = substr($path, 0, $pos + 1); // QFQ might be called by API - justify to the SitePath $pos = strpos($path, 'typo3conf/'); if ($pos !== false) { $path = substr($path, 0, $pos - strlen($path)); } if (empty($path)) { throw new CodeException("Broken _SERVER[SCRIPT_NAME]: $path", ERROR_SESSION_BROKEN_SCRIPT_PATH); } return $path; } /** * Free a lock on the current session */ public static function close() { if (self::$sessionOpen) { session_write_close(); } self::$sessionOpen = false; } /** * Destroy a session - this is only needed in case of attacks */ public static function destroy() { session_destroy(); $_SESSION = array(); //TODO: FE User ausloggen bei Attack - funktioniert so nicht - vermutlich sollte ein T3 Funktion aufgerufen werden! // session_name('fe_typo_user'); // session_start(); // session_destroy(); // $_SESSION = array(); } /** * By default the session is opened during the bootstrap. In case the session has been closed manually, reopen it here. * This code has never been tested - it should not happen that a session needs manually closed and reopen than. * Nevertheless: This code is therefore a fallback in case the reopen really happens somewhat. Maybe this happens in * upcoming logic. * */ public static function open() { if (self::$sessionOpen != true && self::$sessionId != null) { session_id(self::$sessionId); session_start(); self::$sessionOpen = true; } } /** * Check if the feUserUid is stored in the session (even with 'false' which indicates not logged in user). * If not, * - clear the session * - save the feUser, feUserUid in the session. * * Check if the recent logged in feUserUid is equal to the one stored in session: If different, invalidate (clear) * the session and save the new feUser, feUserUid in the session. If isset($GLOBALS["TSFE"]), than we're in a T3 * environment, else we are called as API classes and need to fake feUser / feUserUid from previous stored session. * It's necessary to have feUser / feUserUid available in API classes, due to dynamic update which might reload * data based on feUser / feUserUid. */ private static function checkFeUserUid() { $feUserUidSession = Session::get(SESSION_FE_USER_UID); $feUserSession = Session::get(SESSION_FE_USER); $feUserGroup = false; // Session Timeout only exists for logged in FE users - the default is no user logged in, so set to false to switch of session expiration. self::$lastActivity = false; if (isset($GLOBALS["TSFE"])) { // if no one is logged in: 0 $feUidLoggedIn = $GLOBALS["TSFE"]->fe_user->user["uid"] ?? false; $feUserSession = $GLOBALS["TSFE"]->fe_user->user["username"] ?? false; $feUserGroup = $GLOBALS["TSFE"]->fe_user->user["usergroup"] ?? false; $beUser = $GLOBALS["BE_USER"]->user["username"] ?? false; // Cookie identifier $cookieFe = ($_COOKIE['fe_typo_user']) ?? false; if ($cookieFe !== self::get(SESSION_LAST_COOKIE_FE)) { self::$flagChangedCookieFe = true; // Set the flag that the FE_USER User has changed // Update SESSION_LAST_FE_COOKIE self::set(SESSION_LAST_COOKIE_FE, $cookieFe); } // Manage Custom Session Timeout for logged in users if (isset($GLOBALS["TSFE"]->fe_user->user["username"]) && isset($_COOKIE['fe_typo_user'])) { if (self::$flagChangedCookieFe) { // New user: start timeout timer self::$lastActivity = time(); } else { // ok, still the same user is logged in: get the last activity timestamp to compare later against timeout. self::$lastActivity = self::get(SESSION_LAST_ACTIVITY); } } } else { // If we are called through API there is no T3 environment. Assume nothing has changed, and fake the following check to always 'no change'. $feUidLoggedIn = $feUserUidSession; } if ($feUidLoggedIn != $feUserUidSession) { // save new feUserUid, feUserName Session::set(SESSION_FE_USER_UID, $feUidLoggedIn); Session::set(SESSION_FE_USER, $feUserSession); Session::set(SESSION_FE_USER_GROUP, $feUserGroup); Session::set(SESSION_BE_USER, $beUser); } } /** * Return content to given $key (=SIP). * Return 'false' if not found. * * @param $key * * @return bool */ public static function get($key) { if (!self::$sessionOpen) { self::open(); } if (self::$phpUnit) { $value = isset(self::$sessionLocal[$key]) ? self::$sessionLocal[$key] : false; } else { $value = isset($_SESSION[SESSION_NAME][$key]) ? $_SESSION[SESSION_NAME][$key] : false; } return $value; } /** * */ public static function clearAll() { if (!self::$sessionOpen) { self::open(); } if (self::$phpUnit) { self::$sessionLocal = array(); } else { $_SESSION[SESSION_NAME] = array(); } } /** * @param $key * @param $value */ public static function set($key, $value) { if (!self::$sessionOpen) { self::open(); } if (self::$phpUnit) { self::$sessionLocal[$key] = $value; } else { $_SESSION[SESSION_NAME][$key] = $value; } } /** * Unset the given $key * * @param $key */ public static function unsetItem($key) { if (!self::$sessionOpen) { self::open(); } if (isset($_SESSION[SESSION_NAME][$key])) { unset($_SESSION[SESSION_NAME][$key]); } } /** * @param bool|false $phpUnit * * @return Session class * @throws CodeException */ public static function getInstance($phpUnit = false) { // Design Pattern: Singleton if (self::$instance === null) { self::$instance = new self($phpUnit); } if (!self::$sessionOpen) { self::open(); } return self::$instance; } /** * Checks if the QFQ session is expired. * * @param $timeout * @throws UserFormException */ public static function checkSessionExpired($timeout) { // Just to be sure that the given $timeout is supported by the current php.ini setup config::checkSessionTimeout($timeout); if (self::$lastActivity === false || $timeout === false || $timeout == 0) { return; } if (time() - self::$lastActivity > $timeout) { throw new UserFormException(json_encode( [ERROR_MESSAGE_TO_USER => 'Your session is expired.', ERROR_MESSAGE_SUPPORT => "lastActivity:" . self::$lastActivity . ' Timeout:' . $timeout]), ERROR_SESSION_EXPIRED); } } /** * Returns $flagFeUserChanged. In case it's true, set it to false. * * @return bool */ public static function getAndDestroyFlagFeUserHasChanged() { $flag = self::$flagChangedCookieFe; self::$flagChangedCookieFe = false; return $flag; } }