Commit 538f0123 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Fixes #6866: On logout destroy STORE_USER. Session.php: check if...

Fixes #6866: On logout destroy STORE_USER. Session.php: check if _COOKIE['fe_typo_user'] has changed - yes: clear STORE_USER. Store.php: Rearrange functions.
parent fe39a99a
Pipeline #1089 passed with stage
in 1 minute and 38 seconds
...@@ -66,7 +66,7 @@ require_once(__DIR__ . '/form/DragAndDrop.php'); ...@@ -66,7 +66,7 @@ require_once(__DIR__ . '/form/DragAndDrop.php');
class QuickFormQuery { class QuickFormQuery {
/** /**
* @var \qfq\Store instantiated class * @var Store instantiated class
*/ */
protected $store = null; protected $store = null;
...@@ -166,6 +166,11 @@ class QuickFormQuery { ...@@ -166,6 +166,11 @@ class QuickFormQuery {
$bodytext = $this->t3data[T3DATA_BODYTEXT]; $bodytext = $this->t3data[T3DATA_BODYTEXT];
$this->store = Store::getInstance($bodytext, $phpUnit); $this->store = Store::getInstance($bodytext, $phpUnit);
if (Session::getAndDestroyFlagFeUserHasChanged()) {
$this->store->unsetStore(STORE_USER);
}
$this->store->setVar(TYPO3_TT_CONTENT_UID, $t3data[T3DATA_UID], STORE_TYPO3); $this->store->setVar(TYPO3_TT_CONTENT_UID, $t3data[T3DATA_UID], STORE_TYPO3);
$this->dbIndexData = $this->store->getVar(SYSTEM_DB_INDEX_DATA, STORE_SYSTEM); $this->dbIndexData = $this->store->getVar(SYSTEM_DB_INDEX_DATA, STORE_SYSTEM);
......
...@@ -21,6 +21,7 @@ class Session { ...@@ -21,6 +21,7 @@ class Session {
private static $sessionId = null; private static $sessionId = null;
private static $sessionOpen = false; private static $sessionOpen = false;
private static $lastActivity = false; private static $lastActivity = false;
private static $flagFeUserChanged = false;
/** /**
* @param bool|false $phpUnit * @param bool|false $phpUnit
...@@ -165,17 +166,27 @@ class Session { ...@@ -165,17 +166,27 @@ class Session {
$feUserGroup = isset($GLOBALS["TSFE"]->fe_user->user["usergroup"]) ? $GLOBALS["TSFE"]->fe_user->user["usergroup"] : false; $feUserGroup = isset($GLOBALS["TSFE"]->fe_user->user["usergroup"]) ? $GLOBALS["TSFE"]->fe_user->user["usergroup"] : false;
$beUser = isset($GLOBALS["BE_USER"]->user["username"]) ? $GLOBALS["BE_USER"]->user["username"] : false; $beUser = isset($GLOBALS["BE_USER"]->user["username"]) ? $GLOBALS["BE_USER"]->user["username"] : false;
$cookieFeUser = ($_COOKIE['fe_typo_user']) ?? false;
if ($cookieFeUser !== self::get(SESSION_LAST_FE_COOKIE)) {
self::$flagFeUserChanged = true; // Set the flag that the FE_USER User has changed
}
// Manage Custom Session Timeout for logged in users
if (isset($GLOBALS["TSFE"]->fe_user->user["username"]) && isset($_COOKIE['fe_typo_user'])) { if (isset($GLOBALS["TSFE"]->fe_user->user["username"]) && isset($_COOKIE['fe_typo_user'])) {
if ($_COOKIE['fe_typo_user'] == self::get(SESSION_LAST_FE_COOKIE)) { if ($_COOKIE['fe_typo_user'] == self::get(SESSION_LAST_FE_COOKIE)) {
self::$lastActivity = self::get(SESSION_LAST_ACTIVITY); // ok, still the same user is logged in: get the last activity timestamp. self::$lastActivity = self::get(SESSION_LAST_ACTIVITY); // ok, still the same user is logged in: get the last activity timestamp.
} else { } else {
// New user: remember user // New user: remember user
self::set(SESSION_LAST_FE_COOKIE, $_COOKIE['fe_typo_user']);
self::$lastActivity = time(); self::$lastActivity = time();
} }
} }
// Update SESSION_LAST_FE_COOKIE if cookie has changed.
if (self::$flagFeUserChanged) {
self::set(SESSION_LAST_FE_COOKIE, $cookieFeUser);
}
} else { } 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'. // 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; $feUidLoggedIn = $feUserUidSession;
...@@ -308,4 +319,18 @@ class Session { ...@@ -308,4 +319,18 @@ class Session {
throw new UserFormException("Your session is expired. Please logout and login again."); throw new UserFormException("Your session is expired. Please logout and login again.");
} }
} }
/**
* Returns $flagFeUserChanged. In case it's true, set it to false.
*
* @return bool
*/
public static function getAndDestroyFlagFeUserHasChanged() {
$flag = self::$flagFeUserChanged;
self::$flagFeUserChanged = false;
return $flag;
}
} }
\ No newline at end of file
...@@ -180,23 +180,57 @@ class Store { ...@@ -180,23 +180,57 @@ class Store {
} }
/** /**
* Fill the system store by reading config.qfq.ini. Also setup config defaults. * Returns a pointer to this Class.
* *
* @param string $bodytext
* @param bool|false $phpUnit
* @param string $fileConfigIni * @param string $fileConfigIni
*
* @return null|Store
* @throws CodeException * @throws CodeException
* @throws UserFormException * @throws UserFormException
* @throws UserReportException * @throws UserReportException
*/ */
private static function fillStoreSystem($fileConfigIni = '') { public static function getInstance($bodytext = '', $phpUnit = false, $fileConfigIni = '') {
$config = Config::readConfig($fileConfigIni); if ($phpUnit) {
$config = self::doSystemPath($config); if (self::$instance !== null) {
$config = self::adjustConfig($config); // fake to have a clean environment for the next test.
self::unsetStore(STORE_TYPO3);
self::fillStoreTypo3($bodytext);
self::checkMandatoryParameter($config); self::unsetStore(STORE_CLIENT);
self::fillStoreClient();
}
self::setStore($config, STORE_SYSTEM, true); // Testing different config files means initialize completely
if ($fileConfigIni != '') {
self::$instance = null;
}
}
// Design Pattern: Singleton
if (self::$instance === null) {
self::$phpUnit = $phpUnit;
self::$instance = new self($bodytext, $fileConfigIni);
} else {
// Class Store seems to be persistent over multiple QFQ instantiation. Set bodytext again, with every new request (if bodytext is given).
if ($bodytext !== '') {
self::fillStoreTypo3($bodytext);
self::unsetStore(STORE_RECORD); // At least in Multi DB Setup: STORE_RECORD of former tt-content QFQ Records interfere with loading a form.
}
}
// Disable TYPO3_DEBUG_SHOW_BODY_TEXT=1 if SYSTEM_SHOW_DEBUG_INFO!='yes'
if (self::getVar(TYPO3_DEBUG_SHOW_BODY_TEXT, STORE_TYPO3) === '1' &&
!Support::findInSet(SYSTEM_SHOW_DEBUG_INFO_YES, self::getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM))
) {
self::setVar(TYPO3_DEBUG_SHOW_BODY_TEXT, '0', STORE_TYPO3);
}
return self::$instance;
} }
/** /**
...@@ -343,19 +377,90 @@ class Store { ...@@ -343,19 +377,90 @@ class Store {
public static function setStore(array $dataArray, $store, $flagOverwrite = false) { public static function setStore(array $dataArray, $store, $flagOverwrite = false) {
// Check valid Storename // Check valid Storename
if (!isset(self::$sanitizeStore)) if (!isset(self::$sanitizeStore)) {
throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE); throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
}
if ($store === STORE_ZERO) if ($store === STORE_ZERO || $store === STORE_EMPTY) {
throw new CodeException("setVarArray() for STORE_ZERO is impossible - there are no values.", ERROR_SET_STORE_ZERO); throw new CodeException("setVarArray() for STORE_ZERO/STORE_EMPTY is impossible - there are no values.", ERROR_SET_STORE_ZERO);
}
if (!$flagOverwrite && isset(self::$raw[$store]) && count(self::$raw[$store]) > 0) { if (!$flagOverwrite && isset(self::$raw[$store]) && count(self::$raw[$store]) > 0) {
throw new CodeException("Raw values already been copied to store '$store'. Do this only one time.", ERROR_STORE_VALUE_ALREADY_CODPIED); throw new CodeException("Raw values already been copied to store '$store'. Do this only one time.", ERROR_STORE_VALUE_ALREADY_CODPIED);
} }
if ($store === STORE_EXTRA || $store === STORE_USER) {
Session::set($store, $dataArray);
}
self::$raw[$store] = $dataArray; self::$raw[$store] = $dataArray;
} }
/**
* Deletes a store assigning a new empty array to it.
*
* @param $store
*
* @throws UserFormException
* @throws \qfq\CodeException
*/
public static function unsetStore($store) {
self::setStore(array(), $store, true);
}
/**
* Returns a complete $store.
*
* @param $store
*
* @return array
* @throws UserFormException
* @throws \qfq\CodeException
*/
public static function getStore($store) {
$vars = array();
// Check valid Storename
if (!isset(self::$sanitizeStore[$store])) {
throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
}
if ($store === STORE_ZERO) {
throw new CodeException("getStore() for STORE_ZERO is impossible - there are no values saved.", ERROR_GET_STORE_ZERO);
}
if (isset(self::$raw[$store])) {
$vars = self::$raw[$store];
if ($store == STORE_SIP) {
$vars = self::checkDecodeBase64Arr($vars);
}
}
return $vars;
}
/**
* Fill the system store by reading config.qfq.ini. Also setup config defaults.
*
* @param string $fileConfigIni
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
private static function fillStoreSystem($fileConfigIni = '') {
$config = Config::readConfig($fileConfigIni);
$config = self::doSystemPath($config);
$config = self::adjustConfig($config);
self::checkMandatoryParameter($config);
self::setStore($config, STORE_SYSTEM, true);
}
/** /**
* Copy the BodyText as well as some T3 specific vars to STORE_TYPO3. * Copy the BodyText as well as some T3 specific vars to STORE_TYPO3.
* Attention: if called through API, there is no T3 environment. The only values which are available are fe_user * Attention: if called through API, there is no T3 environment. The only values which are available are fe_user
...@@ -423,6 +528,92 @@ class Store { ...@@ -423,6 +528,92 @@ class Store {
} }
} }
/**
* Fills the STORE_EXTRA.
*
* @param string $storeName STORE_EXTRA, STORE_USER
* @throws CodeException
* @throws UserFormException
*/
private static function fillStorePhpSession($storeName) {
$value = Session::get($storeName);
if (!isset($_SESSION[SESSION_NAME][$storeName]) || $_SESSION[SESSION_NAME][$storeName] === null) {
$value = false;
}
if ($value === false) {
self::setStore(array(), $storeName, true);
} else {
self::setStore($_SESSION[SESSION_NAME][$storeName], $storeName, true);
}
}
/**
* Fills STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES
*
* @param array $tableDefinition
*
* @throws CodeException
* @throws UserFormException
*/
public static function fillStoreTableDefaultColumnType(array $tableDefinition) {
self::setStore(array_column($tableDefinition, 'Default', 'Field'), STORE_TABLE_DEFAULT, true);
self::setStore(array_column($tableDefinition, 'Type', 'Field'), STORE_TABLE_COLUMN_TYPES, true);
}
/**
* Set's a single $key/$value pair $store.
*
* @param string $key
* @param string|array $value
* @param string $storeName
* @param bool|true $overWrite
*
* @throws UserFormException
* @throws \qfq\CodeException
*/
public static function setVar($key, $value, $storeName, $overWrite = true) {
// Check valid Storename
if (!isset(self::$sanitizeStore)) {
throw new UserFormException("Unknown Store: $storeName", ERROR_UNNOWN_STORE);
}
if ($storeName === STORE_ZERO) {
throw new CodeException("setVar() for STORE_ZERO is impossible - there are no values.", ERROR_SET_STORE_ZERO);
}
// Complain if already set and different.
if ($overWrite === false && isset(self::$raw[$storeName][$key]) && self::$raw[$storeName][$key] != $value) {
throw new UserFormException("Value of '$key' already set in store '$storeName'.", ERROR_STORE_KEY_EXIST);
}
self::$raw[$storeName][$key] = $value;
// The STORE_EXTRA / STORE_USER saves arrays and is persistent
if ($storeName === STORE_EXTRA || $storeName === STORE_USER) {
$data = Session::get($storeName);
if ($data === false) {
$data = array();
}
// Switch FeUser: log this to qfq.log
if ($storeName === STORE_USER && $key == TYPO3_FE_USER) {
$qfqLog = self::getVar(SYSTEM_QFQ_LOG, STORE_SYSTEM);
$feUserOld = isset($data[$key]) ? $data[$key] : self::getVar($key, STORE_TYPO3 . STORE_EMPTY);
Logger::logMessage(date('Y.m.d H:i:s ') . ": Switch feUser '$feUserOld' to '$value'", $qfqLog);
}
$data[$key] = $value;
Session::set($storeName, $data);
}
}
/** /**
* Unset a variable in store. * Unset a variable in store.
* *
...@@ -516,155 +707,6 @@ class Store { ...@@ -516,155 +707,6 @@ class Store {
return false; return false;
} }
/**
* Fills the STORE_EXTRA.
*
* @param string $storeName STORE_EXTRA, STORE_USER
* @throws CodeException
* @throws UserFormException
*/
private static function fillStorePhpSession($storeName) {
$value = Session::get($storeName);
if (!isset($_SESSION[SESSION_NAME][$storeName]) || $_SESSION[SESSION_NAME][$storeName] === null) {
$value = false;
}
if ($value === false) {
self::setStore(array(), $storeName, true);
} else {
self::setStore($_SESSION[SESSION_NAME][$storeName], $storeName, true);
}
}
/**
* Returns a pointer to this Class.
*
* @param string $bodytext
* @param bool|false $phpUnit
* @param string $fileConfigIni
*
* @return null|Store
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public static function getInstance($bodytext = '', $phpUnit = false, $fileConfigIni = '') {
if ($phpUnit) {
if (self::$instance !== null) {
// fake to have a clean environment for the next test.
self::unsetStore(STORE_TYPO3);
self::fillStoreTypo3($bodytext);
self::unsetStore(STORE_CLIENT);
self::fillStoreClient();
}
// Testing different config files means initialize completely
if ($fileConfigIni != '') {
self::$instance = null;
}
}
// Design Pattern: Singleton
if (self::$instance === null) {
self::$phpUnit = $phpUnit;
self::$instance = new self($bodytext, $fileConfigIni);
} else {
// Class Store seems to be persistent over multiple QFQ instantiation. Set bodytext again, with every new request (if bodytext is given).
if ($bodytext !== '') {
self::fillStoreTypo3($bodytext);
self::unsetStore(STORE_RECORD); // At least in Multi DB Setup: STORE_RECORD of former tt-content QFQ Records interfere with loading a form.
}
}
// Disable TYPO3_DEBUG_SHOW_BODY_TEXT=1 if SYSTEM_SHOW_DEBUG_INFO!='yes'
if (self::getVar(TYPO3_DEBUG_SHOW_BODY_TEXT, STORE_TYPO3) === '1' &&
!Support::findInSet(SYSTEM_SHOW_DEBUG_INFO_YES, self::getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM))
) {
self::setVar(TYPO3_DEBUG_SHOW_BODY_TEXT, '0', STORE_TYPO3);
}
return self::$instance;
}
/**
* Deletes a store assigning a new empty array to it.
*
* @param $store
*
* @throws UserFormException
* @throws \qfq\CodeException
*/
public static function unsetStore($store) {
// Check valid store name
if (!isset(self::$sanitizeStore)) {
throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
}
if ($store === STORE_ZERO) {
throw new CodeException("unsetStore() for STORE_ZERO is impossible - there are no values.", ERROR_SET_STORE_ZERO);
}
if (isset(self::$raw[$store])) {
self::$raw[$store] = array();
}
}
/**
* Set's a single $key/$value pair $store.
*
* @param string $key
* @param string|array $value
* @param string $storeName
* @param bool|true $overWrite
*
* @throws UserFormException
* @throws \qfq\CodeException
*/
public static function setVar($key, $value, $storeName, $overWrite = true) {
// Check valid Storename
if (!isset(self::$sanitizeStore)) {
throw new UserFormException("Unknown Store: $storeName", ERROR_UNNOWN_STORE);
}
if ($storeName === STORE_ZERO) {
throw new CodeException("setVar() for STORE_ZERO is impossible - there are no values.", ERROR_SET_STORE_ZERO);
}
// Complain if already set and different.
if ($overWrite === false && isset(self::$raw[$storeName][$key]) && self::$raw[$storeName][$key] != $value) {
throw new UserFormException("Value of '$key' already set in store '$storeName'.", ERROR_STORE_KEY_EXIST);
}
self::$raw[$storeName][$key] = $value;
// The STORE_EXTRA / STORE_USER saves arrays and is persistent
if ($storeName === STORE_EXTRA || $storeName === STORE_USER) {
$data = Session::get($storeName);
if ($data === false) {
$data = array();
}
// Switch FeUser: log this to qfq.log
if ($storeName === STORE_USER && $key == TYPO3_FE_USER) {
$qfqLog = self::getVar(SYSTEM_QFQ_LOG, STORE_SYSTEM);
$feUserOld = isset($data[$key]) ? $data[$key] : self::getVar($key, STORE_TYPO3 . STORE_EMPTY);
Logger::logMessage(date('Y.m.d H:i:s ') . ": Switch feUser '$feUserOld' to '$value'", $qfqLog);
}
$data[$key] = $value;
Session::set($storeName, $data);
}
}
/** /**
* Create a SIP after a form load. This is necessary on forms without a sip and on forms with r=0 (new record). * Create a SIP after a form load. This is necessary on forms without a sip and on forms with r=0 (new record).
* *
...@@ -734,37 +776,6 @@ class Store { ...@@ -734,37 +776,6 @@ class Store {
return $tmpParam; return $tmpParam;
} }
/**
* Returns a complete $store.
*
* @param $store
*
* @return array
* @throws UserFormException
* @throws \qfq\CodeException
*/
public static function getStore($store) {
$vars = array();
// Check valid Storename
if (!isset(self::$sanitizeStore[$store])) {
throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
}
if ($store === STORE_ZERO) {
throw new CodeException("getStore() for STORE_ZERO is impossible - there are no values saved.", ERROR_GET_STORE_ZERO);
}
if (isset(self::$raw[$store])) {
$vars = self::$raw[$store];
if ($store == STORE_SIP) {
$vars = self::checkDecodeBase64Arr($vars);
}
}
return $vars;
}
/** /**
* Iterate over an array and look for keynames starting with SIP_PREFIX_BASE64. * Iterate over an array and look for keynames starting with SIP_PREFIX_BASE64.
* Found elements will be base64_decode(). * Found elements will be base64_decode().
...@@ -795,20 +806,6 @@ class Store { ...@@ -795,20 +806,6 @@ class Store {
return self::$sip; return self::$sip;
} }
/**
* Fills STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES
*
* @param array $tableDefinition
*
* @throws CodeException
* @throws UserFormException
*/
public static function fillStoreTableDefaultColumnType(array $tableDefinition) {
self::setStore(array_column($tableDefinition, 'Default', 'Field'), STORE_TABLE_DEFAULT, true);
self::setStore(array_column($tableDefinition, 'Type', 'Field'), STORE_TABLE_COLUMN_TYPES, true);
}
/** /**
* Saves a subset of STORE_TYPO3 vars as a SIP. The SIP will be transmitted as hidden form element. * Saves a subset of STORE_TYPO3 vars as a SIP. The SIP will be transmitted as hidden form element.
* *
...@@ -866,15 +863,16 @@ class Store { ...@@ -866,15 +863,16 @@ class Store {
/** /**
* Load $recordId from $tableName using $db and saves it in $store.