TYPO3 CMS  TYPO3_7-6
FrontendUserAuthentication.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
20 
25 {
32  public $formfield_permanent = 'permalogin';
33 
38  protected $sessionDataLifetime = 86400;
39 
43  public $usergroup_column = 'usergroup';
44 
48  public $usergroup_table = 'fe_groups';
49 
53  public $groupData = [
54  'title' => [],
55  'uid' => [],
56  'pid' => []
57  ];
58 
63  public $TSdataArray = [];
64 
68  public $userTS = [];
69 
73  public $userTSUpdated = false;
74 
87  public $sesData = [];
88 
92  public $sesData_change = false;
93 
97  public $userData_change = false;
98 
103 
107  protected $sessionDataTimestamp = null;
108 
112  protected $loginHidden = false;
113 
117  public function __construct()
118  {
119  parent::__construct();
120 
121  // Disable cookie by default, will be activated if saveSessionData() is called,
122  // a user is logging-in or an existing session is found
123  $this->dontSetCookie = true;
124 
125  $this->session_table = 'fe_sessions';
126  $this->name = self::getCookieName();
127  $this->get_name = 'ftu';
128  $this->loginType = 'FE';
129  $this->user_table = 'fe_users';
130  $this->username_column = 'username';
131  $this->userident_column = 'password';
132  $this->userid_column = 'uid';
133  $this->lastLogin_column = 'lastlogin';
134  $this->enablecolumns = [
135  'deleted' => 'deleted',
136  'disabled' => 'disable',
137  'starttime' => 'starttime',
138  'endtime' => 'endtime'
139  ];
140  $this->formfield_uname = 'user';
141  $this->formfield_uident = 'pass';
142  $this->formfield_status = 'logintype';
143  $this->auth_timeout_field = 6000;
144  $this->sendNoCacheHeaders = false;
145  $this->getFallBack = true;
146  $this->getMethodEnabled = true;
147  }
148 
154  public static function getCookieName()
155  {
156  $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['FE']['cookieName']);
157  if (empty($configuredCookieName)) {
158  $configuredCookieName = 'fe_typo_user';
159  }
160  return $configuredCookieName;
161  }
162 
169  public function start()
170  {
171  if ((int)$this->auth_timeout_field > 0 && (int)$this->auth_timeout_field < $this->lifetime) {
172  // If server session timeout is non-zero but less than client session timeout: Copy this value instead.
173  $this->auth_timeout_field = $this->lifetime;
174  }
175  $this->sessionDataLifetime = (int)$GLOBALS['TYPO3_CONF_VARS']['FE']['sessionDataLifetime'];
176  if ($this->sessionDataLifetime <= 0) {
177  $this->sessionDataLifetime = 86400;
178  }
179  parent::start();
180  }
181 
188  public function getNewSessionRecord($tempuser)
189  {
190  $insertFields = parent::getNewSessionRecord($tempuser);
191  $insertFields['ses_permanent'] = $this->is_permanent;
192  return $insertFields;
193  }
194 
201  public function isSetSessionCookie()
202  {
203  return ($this->newSessionID || $this->forceSetCookie)
204  && ($this->lifetime == 0 || !isset($this->user['ses_permanent']) || !$this->user['ses_permanent']);
205  }
206 
213  public function isRefreshTimeBasedCookie()
214  {
215  return $this->lifetime > 0 && isset($this->user['ses_permanent']) && $this->user['ses_permanent'];
216  }
217 
224  public function getLoginFormData()
225  {
226  $loginData = parent::getLoginFormData();
227  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 0 || $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 1) {
228  if ($this->getMethodEnabled) {
229  $isPermanent = GeneralUtility::_GP($this->formfield_permanent);
230  } else {
231  $isPermanent = GeneralUtility::_POST($this->formfield_permanent);
232  }
233  if (strlen($isPermanent) != 1) {
234  $isPermanent = $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'];
235  } elseif (!$isPermanent) {
236  // To make sure the user gets a session cookie and doesn't keep a possibly existing time based cookie,
237  // we need to force setting the session cookie here
238  $this->forceSetCookie = true;
239  }
240  $isPermanent = $isPermanent ? 1 : 0;
241  } elseif ($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 2) {
242  $isPermanent = 1;
243  } else {
244  $isPermanent = 0;
245  }
246  $loginData['permanent'] = $isPermanent;
247  $this->is_permanent = $isPermanent;
248  return $loginData;
249  }
250 
259  public function createUserSession($tempuser)
260  {
261  // At this point we do not know if we need to set a session or a "permanant" cookie
262  // So we force the cookie to be set after authentication took place, which will
263  // then call setSessionCookie(), which will set a cookie with correct settings.
264  $this->dontSetCookie = false;
265  return parent::createUserSession($tempuser);
266  }
267 
275  public function fetchGroupData()
276  {
277  $this->TSdataArray = [];
278  $this->userTS = [];
279  $this->userTSUpdated = false;
280  $this->groupData = [
281  'title' => [],
282  'uid' => [],
283  'pid' => []
284  ];
285  // Setting default configuration:
286  $this->TSdataArray[] = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultUserTSconfig'];
287  // Get the info data for auth services
288  $authInfo = $this->getAuthInfoArray();
289  if ($this->writeDevLog) {
290  if (is_array($this->user)) {
291  GeneralUtility::devLog('Get usergroups for user: ' . GeneralUtility::arrayToLogString($this->user, [$this->userid_column, $this->username_column]), __CLASS__);
292  } else {
293  GeneralUtility::devLog('Get usergroups for "anonymous" user', __CLASS__);
294  }
295  }
296  $groupDataArr = [];
297  // Use 'auth' service to find the groups for the user
298  $serviceChain = '';
299  $subType = 'getGroups' . $this->loginType;
300  while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
301  $serviceChain .= ',' . $serviceObj->getServiceKey();
302  $serviceObj->initAuth($subType, [], $authInfo, $this);
303  $groupData = $serviceObj->getGroups($this->user, $groupDataArr);
304  if (is_array($groupData) && !empty($groupData)) {
305  // Keys in $groupData should be unique ids of the groups (like "uid") so this function will override groups.
306  $groupDataArr = $groupData + $groupDataArr;
307  }
308  unset($serviceObj);
309  }
310  if ($this->writeDevLog && $serviceChain) {
311  GeneralUtility::devLog($subType . ' auth services called: ' . $serviceChain, __CLASS__);
312  }
313  if ($this->writeDevLog && empty($groupDataArr)) {
314  GeneralUtility::devLog('No usergroups found by services', __CLASS__);
315  }
316  if ($this->writeDevLog && !empty($groupDataArr)) {
317  GeneralUtility::devLog(count($groupDataArr) . ' usergroup records found by services', __CLASS__);
318  }
319  // Use 'auth' service to check the usergroups if they are really valid
320  foreach ($groupDataArr as $groupData) {
321  // By default a group is valid
322  $validGroup = true;
323  $serviceChain = '';
324  $subType = 'authGroups' . $this->loginType;
325  while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
326  $serviceChain .= ',' . $serviceObj->getServiceKey();
327  $serviceObj->initAuth($subType, [], $authInfo, $this);
328  if (!$serviceObj->authGroup($this->user, $groupData)) {
329  $validGroup = false;
330  if ($this->writeDevLog) {
331  GeneralUtility::devLog($subType . ' auth service did not auth group: ' . GeneralUtility::arrayToLogString($groupData, 'uid,title'), __CLASS__, 2);
332  }
333  break;
334  }
335  unset($serviceObj);
336  }
337  unset($serviceObj);
338  if ($validGroup && (string)$groupData['uid'] !== '') {
339  $this->groupData['title'][$groupData['uid']] = $groupData['title'];
340  $this->groupData['uid'][$groupData['uid']] = $groupData['uid'];
341  $this->groupData['pid'][$groupData['uid']] = $groupData['pid'];
342  $this->groupData['TSconfig'][$groupData['uid']] = $groupData['TSconfig'];
343  }
344  }
345  if (!empty($this->groupData) && !empty($this->groupData['TSconfig'])) {
346  // TSconfig: collect it in the order it was collected
347  foreach ($this->groupData['TSconfig'] as $TSdata) {
348  $this->TSdataArray[] = $TSdata;
349  }
350  $this->TSdataArray[] = $this->user['TSconfig'];
351  // Sort information
352  ksort($this->groupData['title']);
353  ksort($this->groupData['uid']);
354  ksort($this->groupData['pid']);
355  }
356  return !empty($this->groupData['uid']) ? count($this->groupData['uid']) : 0;
357  }
358 
365  public function getUserTSconf()
366  {
367  if (!$this->userTSUpdated) {
368  // Parsing the user TS (or getting from cache)
369  $this->TSdataArray = TypoScriptParser::checkIncludeLines_array($this->TSdataArray);
370  $userTS = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
371  $parseObj = GeneralUtility::makeInstance(TypoScriptParser::class);
372  $parseObj->parse($userTS);
373  $this->userTS = $parseObj->setup;
374  $this->userTSUpdated = true;
375  }
376  return $this->userTS;
377  }
378 
379  /*****************************************
380  *
381  * Session data management functions
382  *
383  ****************************************/
393  public function fetchSessionData()
394  {
395  // Gets SesData if any AND if not already selected by session fixation check in ->isExistingSessionRecord()
396  if ($this->id && empty($this->sesData)) {
397  $statement = $this->db->prepare_SELECTquery('*', 'fe_session_data', 'hash = :hash');
398  $statement->execute([':hash' => $this->id]);
399  if (($sesDataRow = $statement->fetch()) !== false) {
400  $this->sesData = unserialize($sesDataRow['content']);
401  $this->sessionDataTimestamp = $sesDataRow['tstamp'];
402  }
403  $statement->free();
404  }
405  }
406 
416  public function storeSessionData()
417  {
418  // Saves UC and SesData if changed.
419  if ($this->userData_change) {
420  $this->writeUC('');
421  }
422  if ($this->sesData_change && $this->id) {
423  if (empty($this->sesData)) {
424  // Remove session-data
425  $this->removeSessionData();
426  // Remove cookie if not logged in as the session data is removed as well
427  if (empty($this->user['uid']) && !$this->loginHidden && $this->isCookieSet()) {
428  $this->removeCookie($this->name);
429  }
430  } elseif ($this->sessionDataTimestamp === null) {
431  // Write new session-data
432  $insertFields = [
433  'hash' => $this->id,
434  'content' => serialize($this->sesData),
435  'tstamp' => $GLOBALS['EXEC_TIME']
436  ];
437  $this->sessionDataTimestamp = $GLOBALS['EXEC_TIME'];
438  $this->db->exec_INSERTquery('fe_session_data', $insertFields);
439  // Now set the cookie (= fix the session)
440  $this->setSessionCookie();
441  } else {
442  // Update session data
443  $updateFields = [
444  'content' => serialize($this->sesData),
445  'tstamp' => $GLOBALS['EXEC_TIME']
446  ];
447  $this->sessionDataTimestamp = $GLOBALS['EXEC_TIME'];
448  $this->db->exec_UPDATEquery('fe_session_data', 'hash=' . $this->db->fullQuoteStr($this->id, 'fe_session_data'), $updateFields);
449  }
450  }
451  }
452 
458  public function removeSessionData()
459  {
460  $this->sessionDataTimestamp = null;
461  $this->db->exec_DELETEquery('fe_session_data', 'hash=' . $this->db->fullQuoteStr($this->id, 'fe_session_data'));
462  }
463 
472  public function logoff()
473  {
474  parent::logoff();
475  // Remove the cookie on log-off, but only if we do not have an anonymous session
476  if (!$this->isExistingSessionRecord($this->id) && $this->isCookieSet()) {
477  $this->removeCookie($this->name);
478  }
479  }
480 
485  protected function regenerateSessionId()
486  {
487  $oldSessionId = $this->id;
488  parent::regenerateSessionId();
489  // Update session data with new ID
490  $this->db->exec_UPDATEquery(
491  'fe_session_data',
492  'hash=' . $this->db->fullQuoteStr($oldSessionId, 'fe_session_data'),
493  ['hash' => $this->id]
494  );
495 
496  // We force the cookie to be set later in the authentication process
497  $this->dontSetCookie = false;
498  }
499 
506  public function gc()
507  {
508  $timeoutTimeStamp = (int)($GLOBALS['EXEC_TIME'] - $this->sessionDataLifetime);
509  $this->db->exec_DELETEquery('fe_session_data', 'tstamp < ' . $timeoutTimeStamp);
510  parent::gc();
511  }
512 
522  public function getKey($type, $key)
523  {
524  if (!$key) {
525  return null;
526  }
527  $value = null;
528  switch ($type) {
529  case 'user':
530  $value = $this->uc[$key];
531  break;
532  case 'ses':
533  $value = $this->sesData[$key];
534  break;
535  }
536  return $value;
537  }
538 
551  public function setKey($type, $key, $data)
552  {
553  if (!$key) {
554  return;
555  }
556  switch ($type) {
557  case 'user':
558  if ($this->user['uid']) {
559  if ($data === null) {
560  unset($this->uc[$key]);
561  } else {
562  $this->uc[$key] = $data;
563  }
564  $this->userData_change = true;
565  }
566  break;
567  case 'ses':
568  if ($data === null) {
569  unset($this->sesData[$key]);
570  } else {
571  $this->sesData[$key] = $data;
572  }
573  $this->sesData_change = true;
574  break;
575  }
576  }
577 
585  public function getSessionData($key)
586  {
587  return $this->getKey('ses', $key);
588  }
589 
597  public function setAndSaveSessionData($key, $data)
598  {
599  $this->setKey('ses', $key, $data);
600  $this->storeSessionData();
601  }
602 
612  public function record_registration($recs, $maxSizeOfSessionData = 0)
613  {
614  // Storing value ONLY if there is a confirmed cookie set,
615  // otherwise a shellscript could easily be spamming the fe_sessions table
616  // with bogus content and thus bloat the database
617  if (!$maxSizeOfSessionData || $this->isCookieSet()) {
618  if ($recs['clear_all']) {
619  $this->setKey('ses', 'recs', []);
620  }
621  $change = 0;
622  $recs_array = $this->getKey('ses', 'recs');
623  foreach ($recs as $table => $data) {
624  if (is_array($data)) {
625  foreach ($data as $rec_id => $value) {
626  if ($value != $recs_array[$table][$rec_id]) {
627  $recs_array[$table][$rec_id] = $value;
628  $change = 1;
629  }
630  }
631  }
632  }
633  if ($change && (!$maxSizeOfSessionData || strlen(serialize($recs_array)) < $maxSizeOfSessionData)) {
634  $this->setKey('ses', 'recs', $recs_array);
635  }
636  }
637  }
638 
648  public function isExistingSessionRecord($id)
649  {
650  // Perform check in parent function
651  $count = parent::isExistingSessionRecord($id);
652  // Check if there are any fe_session_data records for the session ID the client claims to have
653  if ($count == false) {
654  $statement = $this->db->prepare_SELECTquery('content,tstamp', 'fe_session_data', 'hash = :hash');
655  $res = $statement->execute([':hash' => $id]);
656  if ($res !== false) {
657  if ($sesDataRow = $statement->fetch()) {
658  $count = true;
659  $this->sesData = unserialize($sesDataRow['content']);
660  $this->sessionDataTimestamp = $sesDataRow['tstamp'];
661  }
662  $statement->free();
663  }
664  }
665  return $count;
666  }
667 
676  public function hideActiveLogin()
677  {
678  $this->user = null;
679  $this->loginHidden = true;
680  }
681 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
static arrayToLogString(array $arr, $valueList=[], $valueLength=20)
static makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=[])
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']