TYPO3 CMS  TYPO3_7-6
AbstractUserAuthentication.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 
19 
30 {
35  public $session_table = '';
36 
41  public $name = '';
42 
47  public $get_name = '';
48 
53  public $user_table = '';
54 
59  public $usergroup_table = '';
60 
65  public $username_column = '';
66 
71  public $userident_column = '';
72 
77  public $userid_column = '';
78 
83  public $usergroup_column = '';
84 
89  public $lastLogin_column = '';
90 
95  public $enablecolumns = [
96  'rootLevel' => '',
97  // Boolean: If TRUE, 'AND pid=0' will be a part of the query...
98  'disabled' => '',
99  'starttime' => '',
100  'endtime' => '',
101  'deleted' => ''
102  ];
103 
107  public $showHiddenRecords = false;
108 
113  public $formfield_uname = '';
114 
119  public $formfield_uident = '';
120 
125  public $formfield_status = '';
126 
135 
143  public $lifetime = 0;
144 
151  public $gc_time = 0;
152 
157  public $gc_probability = 1;
158 
163  public $writeStdLog = false;
164 
169  public $writeAttemptLog = false;
170 
175  public $sendNoCacheHeaders = true;
176 
183  public $getFallBack = false;
184 
193  public $hash_length = 32;
194 
200  public $getMethodEnabled = false;
201 
207  public $lockIP = 4;
208 
215  public $lockHashKeyWords = 'useragent';
216 
220  public $warningEmail = '';
221 
226  public $warningPeriod = 3600;
227 
232  public $warningMax = 3;
233 
238  public $checkPid = true;
239 
244  public $checkPid_value = 0;
245 
251  public $id;
252 
257  public $loginFailure = false;
258 
263  public $loginSessionStarted = false;
264 
269  public $user = null;
270 
277  public $get_URL_ID = '';
278 
283  public $newSessionID = false;
284 
289  public $forceSetCookie = false;
290 
295  public $dontSetCookie = false;
296 
300  protected $cookieWasSetOnCurrentRequest = false;
301 
306  public $loginType = '';
307 
312  public $svConfig = [];
313 
318  public $writeDevLog = false;
319 
323  public $uc;
324 
328  protected $db = null;
329 
333  public function __construct()
334  {
335  $this->db = $this->getDatabaseConnection();
336  }
337 
350  public function start()
351  {
352  // Backend or frontend login - used for auth services
353  if (empty($this->loginType)) {
354  throw new \TYPO3\CMS\Core\Exception('No loginType defined, should be set explicitly by subclass');
355  }
356  // Enable dev logging if set
357  if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['writeDevLog']) {
358  $this->writeDevLog = true;
359  }
360  if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['writeDevLog' . $this->loginType]) {
361  $this->writeDevLog = true;
362  }
363  if (TYPO3_DLOG) {
364  $this->writeDevLog = true;
365  }
366  if ($this->writeDevLog) {
367  GeneralUtility::devLog('## Beginning of auth logging.', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
368  }
369  // Init vars.
370  $mode = '';
371  $this->newSessionID = false;
372  // $id is set to ses_id if cookie is present. Else set to FALSE, which will start a new session
373  $id = $this->getCookie($this->name);
374  $this->svConfig = $GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth'];
375 
376  // If fallback to get mode....
377  if (!$id && $this->getFallBack && $this->get_name) {
378  $id = isset($_GET[$this->get_name]) ? GeneralUtility::_GET($this->get_name) : '';
379  if (strlen($id) != $this->hash_length) {
380  $id = '';
381  }
382  $mode = 'get';
383  }
384 
385  // If new session or client tries to fix session...
386  if (!$id || !$this->isExistingSessionRecord($id)) {
387  // New random session-$id is made
388  $id = $this->createSessionId();
389  // New session
390  $this->newSessionID = true;
391  }
392  // Internal var 'id' is set
393  $this->id = $id;
394  // If fallback to get mode....
395  if ($mode == 'get' && $this->getFallBack && $this->get_name) {
396  $this->get_URL_ID = '&' . $this->get_name . '=' . $id;
397  }
398  // Set session hashKey lock keywords from configuration; currently only 'useragent' can be used.
399  $this->lockHashKeyWords = $GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['lockHashKeyWords'];
400  // Make certain that NO user is set initially
401  $this->user = null;
402  // Set all possible headers that could ensure that the script is not cached on the client-side
403  if ($this->sendNoCacheHeaders && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI)) {
404  header('Expires: 0');
405  header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
406  $cacheControlHeader = 'no-cache, must-revalidate';
407  $pragmaHeader = 'no-cache';
408  // Prevent error message in IE when using a https connection
409  // see http://forge.typo3.org/issues/24125
410  $clientInfo = GeneralUtility::clientInfo();
411  if ($clientInfo['BROWSER'] === 'msie' && GeneralUtility::getIndpEnv('TYPO3_SSL')) {
412  // Some IEs can not handle no-cache
413  // see http://support.microsoft.com/kb/323308/en-us
414  $cacheControlHeader = 'must-revalidate';
415  // IE needs "Pragma: private" if SSL connection
416  $pragmaHeader = 'private';
417  }
418  header('Cache-Control: ' . $cacheControlHeader);
419  header('Pragma: ' . $pragmaHeader);
420  }
421  // Load user session, check to see if anyone has submitted login-information and if so authenticate
422  // the user with the session. $this->user[uid] may be used to write log...
423  $this->checkAuthentication();
424  // Setting cookies
425  if (!$this->dontSetCookie) {
426  $this->setSessionCookie();
427  }
428  // Hook for alternative ways of filling the $this->user array (is used by the "timtaw" extension)
429  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postUserLookUp'])) {
430  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postUserLookUp'] as $funcName) {
431  $_params = [
432  'pObj' => &$this
433  ];
434  GeneralUtility::callUserFunction($funcName, $_params, $this);
435  }
436  }
437  // Set $this->gc_time if not explicitly specified
438  if ($this->gc_time == 0) {
439  // Default to 1 day if $this->auth_timeout_field is 0
440  $this->gc_time = $this->auth_timeout_field == 0 ? 86400 : $this->auth_timeout_field;
441  }
442  // If we're lucky we'll get to clean up old sessions....
443  if (rand() % 100 <= $this->gc_probability) {
444  $this->gc();
445  }
446  }
447 
454  protected function setSessionCookie()
455  {
456  $isSetSessionCookie = $this->isSetSessionCookie();
457  $isRefreshTimeBasedCookie = $this->isRefreshTimeBasedCookie();
458  if ($isSetSessionCookie || $isRefreshTimeBasedCookie) {
459  $settings = $GLOBALS['TYPO3_CONF_VARS']['SYS'];
460  // Get the domain to be used for the cookie (if any):
461  $cookieDomain = $this->getCookieDomain();
462  // If no cookie domain is set, use the base path:
463  $cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
464  // If the cookie lifetime is set, use it:
465  $cookieExpire = $isRefreshTimeBasedCookie ? $GLOBALS['EXEC_TIME'] + $this->lifetime : 0;
466  // Use the secure option when the current request is served by a secure connection:
467  $cookieSecure = (bool)$settings['cookieSecure'] && GeneralUtility::getIndpEnv('TYPO3_SSL');
468  // Deliver cookies only via HTTP and prevent possible XSS by JavaScript:
469  $cookieHttpOnly = (bool)$settings['cookieHttpOnly'];
470  // Do not set cookie if cookieSecure is set to "1" (force HTTPS) and no secure channel is used:
471  if ((int)$settings['cookieSecure'] !== 1 || GeneralUtility::getIndpEnv('TYPO3_SSL')) {
472  setcookie($this->name, $this->id, $cookieExpire, $cookiePath, $cookieDomain, $cookieSecure, $cookieHttpOnly);
473  $this->cookieWasSetOnCurrentRequest = true;
474  } else {
475  throw new \TYPO3\CMS\Core\Exception('Cookie was not set since HTTPS was forced in $TYPO3_CONF_VARS[SYS][cookieSecure].', 1254325546);
476  }
477  if ($this->writeDevLog) {
478  $devLogMessage = ($isRefreshTimeBasedCookie ? 'Updated Cookie: ' : 'Set Cookie: ') . $this->id;
479  GeneralUtility::devLog($devLogMessage . ($cookieDomain ? ', ' . $cookieDomain : ''), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
480  }
481  }
482  }
483 
490  protected function getCookieDomain()
491  {
492  $result = '';
493  $cookieDomain = $GLOBALS['TYPO3_CONF_VARS']['SYS']['cookieDomain'];
494  // If a specific cookie domain is defined for a given TYPO3_MODE,
495  // use that domain
496  if (!empty($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieDomain'])) {
497  $cookieDomain = $GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieDomain'];
498  }
499  if ($cookieDomain) {
500  if ($cookieDomain[0] == '/') {
501  $match = [];
502  $matchCnt = @preg_match($cookieDomain, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'), $match);
503  if ($matchCnt === false) {
504  GeneralUtility::sysLog('The regular expression for the cookie domain (' . $cookieDomain . ') contains errors. The session is not shared across sub-domains.', 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
505  } elseif ($matchCnt) {
506  $result = $match[0];
507  }
508  } else {
509  $result = $cookieDomain;
510  }
511  }
512  return $result;
513  }
514 
521  protected function getCookie($cookieName)
522  {
523  return isset($_COOKIE[$cookieName]) ? stripslashes($_COOKIE[$cookieName]) : '';
524  }
525 
532  public function isSetSessionCookie()
533  {
534  return ($this->newSessionID || $this->forceSetCookie) && $this->lifetime == 0;
535  }
536 
543  public function isRefreshTimeBasedCookie()
544  {
545  return $this->lifetime > 0;
546  }
547 
555  public function checkAuthentication()
556  {
557  // No user for now - will be searched by service below
558  $tempuserArr = [];
559  $tempuser = false;
560  // User is not authenticated by default
561  $authenticated = false;
562  // User want to login with passed login data (name/password)
563  $activeLogin = false;
564  // Indicates if an active authentication failed (not auto login)
565  $this->loginFailure = false;
566  if ($this->writeDevLog) {
567  GeneralUtility::devLog('Login type: ' . $this->loginType, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
568  }
569  // The info array provide additional information for auth services
570  $authInfo = $this->getAuthInfoArray();
571  // Get Login/Logout data submitted by a form or params
572  $loginData = $this->getLoginFormData();
573  if ($this->writeDevLog) {
574  GeneralUtility::devLog('Login data: ' . GeneralUtility::arrayToLogString($loginData), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
575  }
576  // Active logout (eg. with "logout" button)
577  if ($loginData['status'] === 'logout') {
578  if ($this->writeStdLog) {
579  // $type,$action,$error,$details_nr,$details,$data,$tablename,$recuid,$recpid
580  $this->writelog(255, 2, 0, 2, 'User %s logged out', [$this->user['username']], '', 0, 0);
581  }
582  // Logout written to log
583  if ($this->writeDevLog) {
584  GeneralUtility::devLog('User logged out. Id: ' . $this->id, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, -1);
585  }
586  $this->logoff();
587  }
588  // Determine whether we need to skip session update.
589  // This is used mainly for checking session timeout in advance without refreshing the current session's timeout.
590  $skipSessionUpdate = (bool)GeneralUtility::_GP('skipSessionUpdate');
591  $haveSession = false;
592  if (!$this->newSessionID) {
593  // Read user session
594  $authInfo['userSession'] = $this->fetchUserSession($skipSessionUpdate);
595  $haveSession = is_array($authInfo['userSession']);
596  }
597  // Active login (eg. with login form)
598  if (!$haveSession && $loginData['status'] === 'login') {
599  $activeLogin = true;
600  if ($this->writeDevLog) {
601  GeneralUtility::devLog('Active login (eg. with login form)', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
602  }
603  // check referer for submitted login values
604  if ($this->formfield_status && $loginData['uident'] && $loginData['uname']) {
605  // Delete old user session if any
606  $this->logoff();
607  }
608  // Refuse login for _CLI users, if not processing a CLI request type
609  // (although we shouldn't be here in case of a CLI request type)
610  if (strtoupper(substr($loginData['uname'], 0, 5)) == '_CLI_' && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI)) {
611  throw new \RuntimeException('TYPO3 Fatal Error: You have tried to login using a CLI user. Access prohibited!', 1270853931);
612  }
613  }
614  if ($this->writeDevLog) {
615  if ($haveSession) {
616  GeneralUtility::devLog('User session found: ' . GeneralUtility::arrayToLogString($authInfo['userSession'], [$this->userid_column, $this->username_column]), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, 0);
617  } else {
618  GeneralUtility::devLog('No user session found.', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, 2);
619  }
620  if (is_array($this->svConfig['setup'])) {
621  GeneralUtility::devLog('SV setup: ' . GeneralUtility::arrayToLogString($this->svConfig['setup']), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, 0);
622  }
623  }
624  // Fetch user if ...
625  if (
626  $activeLogin || $this->svConfig['setup'][$this->loginType . '_alwaysFetchUser']
627  || !$haveSession && $this->svConfig['setup'][$this->loginType . '_fetchUserIfNoSession']
628  ) {
629  // Use 'auth' service to find the user
630  // First found user will be used
631  $serviceChain = '';
632  $subType = 'getUser' . $this->loginType;
633  while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
634  $serviceChain .= ',' . $serviceObj->getServiceKey();
635  $serviceObj->initAuth($subType, $loginData, $authInfo, $this);
636  if ($row = $serviceObj->getUser()) {
637  $tempuserArr[] = $row;
638  if ($this->writeDevLog) {
639  GeneralUtility::devLog('User found: ' . GeneralUtility::arrayToLogString($row, [$this->userid_column, $this->username_column]), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, 0);
640  }
641  // User found, just stop to search for more if not configured to go on
642  if (!$this->svConfig['setup'][$this->loginType . '_fetchAllUsers']) {
643  break;
644  }
645  }
646  unset($serviceObj);
647  }
648  unset($serviceObj);
649  if ($this->writeDevLog && $this->svConfig['setup'][$this->loginType . '_alwaysFetchUser']) {
650  GeneralUtility::devLog($this->loginType . '_alwaysFetchUser option is enabled', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
651  }
652  if ($this->writeDevLog && $serviceChain) {
653  GeneralUtility::devLog($subType . ' auth services called: ' . $serviceChain, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
654  }
655  if ($this->writeDevLog && empty($tempuserArr)) {
656  GeneralUtility::devLog('No user found by services', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
657  }
658  if ($this->writeDevLog && !empty($tempuserArr)) {
659  GeneralUtility::devLog(count($tempuserArr) . ' user records found by services', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
660  }
661  }
662  // If no new user was set we use the already found user session
663  if (empty($tempuserArr) && $haveSession) {
664  $tempuserArr[] = $authInfo['userSession'];
665  $tempuser = $authInfo['userSession'];
666  // User is authenticated because we found a user session
667  $authenticated = true;
668  if ($this->writeDevLog) {
669  GeneralUtility::devLog('User session used: ' . GeneralUtility::arrayToLogString($authInfo['userSession'], [$this->userid_column, $this->username_column]), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
670  }
671  }
672  // Re-auth user when 'auth'-service option is set
673  if ($this->svConfig['setup'][$this->loginType . '_alwaysAuthUser']) {
674  $authenticated = false;
675  if ($this->writeDevLog) {
676  GeneralUtility::devLog('alwaysAuthUser option is enabled', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
677  }
678  }
679  // Authenticate the user if needed
680  if (!empty($tempuserArr) && !$authenticated) {
681  foreach ($tempuserArr as $tempuser) {
682  // Use 'auth' service to authenticate the user
683  // If one service returns FALSE then authentication failed
684  // a service might return 100 which means there's no reason to stop but the user can't be authenticated by that service
685  if ($this->writeDevLog) {
686  GeneralUtility::devLog('Auth user: ' . GeneralUtility::arrayToLogString($tempuser), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
687  }
688  $serviceChain = '';
689  $subType = 'authUser' . $this->loginType;
690  while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
691  $serviceChain .= ',' . $serviceObj->getServiceKey();
692  $serviceObj->initAuth($subType, $loginData, $authInfo, $this);
693  if (($ret = $serviceObj->authUser($tempuser)) > 0) {
694  // If the service returns >=200 then no more checking is needed - useful for IP checking without password
695  if ((int)$ret >= 200) {
696  $authenticated = true;
697  break;
698  } elseif ((int)$ret >= 100) {
699  } else {
700  $authenticated = true;
701  }
702  } else {
703  $authenticated = false;
704  break;
705  }
706  unset($serviceObj);
707  }
708  unset($serviceObj);
709  if ($this->writeDevLog && $serviceChain) {
710  GeneralUtility::devLog($subType . ' auth services called: ' . $serviceChain, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
711  }
712  if ($authenticated) {
713  // Leave foreach() because a user is authenticated
714  break;
715  }
716  }
717  }
718  // If user is authenticated a valid user is in $tempuser
719  if ($authenticated) {
720  // Reset failure flag
721  $this->loginFailure = false;
722  // Insert session record if needed:
723  if (!($haveSession && ($tempuser['ses_id'] == $this->id || $tempuser['uid'] == $authInfo['userSession']['ses_userid']))) {
724  $sessionData = $this->createUserSession($tempuser);
725  if ($sessionData) {
726  $this->user = array_merge(
727  $tempuser,
728  $sessionData
729  );
730  }
731  // The login session is started.
732  $this->loginSessionStarted = true;
733  if ($this->writeDevLog && is_array($this->user)) {
734  GeneralUtility::devLog('User session finally read: ' . GeneralUtility::arrayToLogString($this->user, [$this->userid_column, $this->username_column]), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, -1);
735  }
736  } elseif ($haveSession) {
737  $this->user = $authInfo['userSession'];
738  }
739  if ($activeLogin && !$this->newSessionID) {
740  $this->regenerateSessionId();
741  }
742  // User logged in - write that to the log!
743  if ($this->writeStdLog && $activeLogin) {
744  $this->writelog(255, 1, 0, 1, 'User %s logged in from %s (%s)', [$tempuser[$this->username_column], GeneralUtility::getIndpEnv('REMOTE_ADDR'), GeneralUtility::getIndpEnv('REMOTE_HOST')], '', '', '', -1, '', $tempuser['uid']);
745  }
746  if ($this->writeDevLog && $activeLogin) {
747  GeneralUtility::devLog('User ' . $tempuser[$this->username_column] . ' logged in from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR') . ' (' . GeneralUtility::getIndpEnv('REMOTE_HOST') . ')', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, -1);
748  }
749  if ($this->writeDevLog && !$activeLogin) {
750  GeneralUtility::devLog('User ' . $tempuser[$this->username_column] . ' authenticated from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR') . ' (' . GeneralUtility::getIndpEnv('REMOTE_HOST') . ')', \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, -1);
751  }
752  } elseif ($activeLogin || !empty($tempuserArr)) {
753  $this->loginFailure = true;
754  if ($this->writeDevLog && empty($tempuserArr) && $activeLogin) {
755  GeneralUtility::devLog('Login failed: ' . GeneralUtility::arrayToLogString($loginData), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, 2);
756  }
757  if ($this->writeDevLog && !empty($tempuserArr)) {
758  GeneralUtility::devLog('Login failed: ' . GeneralUtility::arrayToLogString($tempuser, [$this->userid_column, $this->username_column]), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, 2);
759  }
760  }
761  // If there were a login failure, check to see if a warning email should be sent:
762  if ($this->loginFailure && $activeLogin) {
763  if ($this->writeDevLog) {
764  GeneralUtility::devLog('Call checkLogFailures: ' . GeneralUtility::arrayToLogString(['warningEmail' => $this->warningEmail, 'warningPeriod' => $this->warningPeriod, 'warningMax' => $this->warningMax]), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, -1);
765  }
766 
767  // Hook to implement login failure tracking methods
768  if (
769  !empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postLoginFailureProcessing'])
770  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postLoginFailureProcessing'])
771  ) {
772  $_params = [];
773  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postLoginFailureProcessing'] as $_funcRef) {
774  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
775  }
776  } else {
777  // If no hook is implemented, wait for 5 seconds
778  sleep(5);
779  }
780 
781  $this->checkLogFailures($this->warningEmail, $this->warningPeriod, $this->warningMax);
782  }
783  }
784 
790  public function createSessionId()
791  {
792  return GeneralUtility::getRandomHexString($this->hash_length);
793  }
794 
800  protected function regenerateSessionId()
801  {
802  $oldSessionId = $this->id;
803  $this->id = $this->createSessionId();
804  // Update session record with new ID
805  $this->db->exec_UPDATEquery(
806  $this->session_table,
807  'ses_id = ' . $this->db->fullQuoteStr($oldSessionId, $this->session_table)
808  . ' AND ses_name = ' . $this->db->fullQuoteStr($this->name, $this->session_table),
809  ['ses_id' => $this->id]
810  );
811  $this->user['ses_id'] = $this->id;
812  $this->newSessionID = true;
813  }
814 
815  /*************************
816  *
817  * User Sessions
818  *
819  *************************/
827  public function createUserSession($tempuser)
828  {
829  if ($this->writeDevLog) {
830  GeneralUtility::devLog('Create session ses_id = ' . $this->id, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
831  }
832  // Delete session entry first
833  $this->db->exec_DELETEquery(
834  $this->session_table,
835  'ses_id = ' . $this->db->fullQuoteStr($this->id, $this->session_table)
836  . ' AND ses_name = ' . $this->db->fullQuoteStr($this->name, $this->session_table)
837  );
838  // Re-create session entry
839  $insertFields = $this->getNewSessionRecord($tempuser);
840  $inserted = (bool)$this->db->exec_INSERTquery($this->session_table, $insertFields);
841  if (!$inserted) {
842  $message = 'Session data could not be written to DB. Error: ' . $this->db->sql_error();
843  GeneralUtility::sysLog($message, 'core', GeneralUtility::SYSLOG_SEVERITY_WARNING);
844  if ($this->writeDevLog) {
845  GeneralUtility::devLog($message, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class, 2);
846  }
847  }
848  // Updating lastLogin_column carrying information about last login.
849  if ($this->lastLogin_column && $inserted) {
850  $this->db->exec_UPDATEquery(
851  $this->user_table,
852  $this->userid_column . '=' . $this->db->fullQuoteStr($tempuser[$this->userid_column], $this->user_table),
853  [$this->lastLogin_column => $GLOBALS['EXEC_TIME']]
854  );
855  }
856 
857  return $inserted ? $insertFields : [];
858  }
859 
867  public function getNewSessionRecord($tempuser)
868  {
869  $sessionIpLock = '[DISABLED]';
870  if ($this->lockIP && empty($tempuser['disableIPlock'])) {
871  $sessionIpLock = $this->ipLockClause_remoteIPNumber($this->lockIP);
872  }
873 
874  return [
875  'ses_id' => $this->id,
876  'ses_name' => $this->name,
877  'ses_iplock' => $sessionIpLock,
878  'ses_hashlock' => $this->hashLockClause_getHashInt(),
879  'ses_userid' => $tempuser[$this->userid_column],
880  'ses_tstamp' => $GLOBALS['EXEC_TIME'],
881  'ses_data' => ''
882  ];
883  }
884 
891  public function fetchUserSession($skipSessionUpdate = false)
892  {
893  $user = false;
894  if ($this->writeDevLog) {
895  GeneralUtility::devLog('Fetch session ses_id = ' . $this->id, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
896  }
897 
898  // Fetch the user session from the DB
899  $statement = $this->fetchUserSessionFromDB();
900 
901  if ($statement) {
902  $statement->execute();
903  $user = $statement->fetch();
904  $statement->free();
905  }
906  if ($user) {
907  // A user was found
908  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->auth_timeout_field)) {
909  // Get timeout from object
910  $timeout = (int)$this->auth_timeout_field;
911  } else {
912  // Get timeout-time from usertable
913  $timeout = (int)$user[$this->auth_timeout_field];
914  }
915  // If timeout > 0 (TRUE) and current time has not exceeded the latest sessions-time plus the timeout in seconds then accept user
916  // Use a gracetime-value to avoid updating a session-record too often
917  if ($timeout > 0 && $GLOBALS['EXEC_TIME'] < $user['ses_tstamp'] + $timeout) {
918  $sessionUpdateGracePeriod = 61;
919  if (!$skipSessionUpdate && $GLOBALS['EXEC_TIME'] > ($user['ses_tstamp'] + $sessionUpdateGracePeriod)) {
920  $this->db->exec_UPDATEquery($this->session_table, 'ses_id=' . $this->db->fullQuoteStr($this->id, $this->session_table)
921  . ' AND ses_name=' . $this->db->fullQuoteStr($this->name, $this->session_table), ['ses_tstamp' => $GLOBALS['EXEC_TIME']]);
922  // Make sure that the timestamp is also updated in the array
923  $user['ses_tstamp'] = $GLOBALS['EXEC_TIME'];
924  }
925  } else {
926  // Delete any user set...
927  $this->logoff();
928  $user = false;
929  }
930  }
931  return $user;
932  }
933 
941  public function logoff()
942  {
943  if ($this->writeDevLog) {
944  GeneralUtility::devLog('logoff: ses_id = ' . $this->id, \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
945  }
946  // Release the locked records
948  // Hook for pre-processing the logoff() method, requested and implemented by andreas.otto@dkd.de:
949  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'])) {
950  $_params = [];
951  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] as $_funcRef) {
952  if ($_funcRef) {
953  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
954  }
955  }
956  }
957  $this->db->exec_DELETEquery($this->session_table, 'ses_id = ' . $this->db->fullQuoteStr($this->id, $this->session_table) . '
958  AND ses_name = ' . $this->db->fullQuoteStr($this->name, $this->session_table));
959  $this->user = null;
960  // Hook for post-processing the logoff() method, requested and implemented by andreas.otto@dkd.de:
961  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_post_processing'])) {
962  $_params = [];
963  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_post_processing'] as $_funcRef) {
964  if ($_funcRef) {
965  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
966  }
967  }
968  }
969  }
970 
977  public function removeCookie($cookieName)
978  {
979  $cookieDomain = $this->getCookieDomain();
980  // If no cookie domain is set, use the base path
981  $cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
982  setcookie($cookieName, null, -1, $cookiePath, $cookieDomain);
983  }
984 
992  public function isExistingSessionRecord($id)
993  {
994  $statement = $this->db->prepare_SELECTquery('COUNT(*)', $this->session_table, 'ses_id = :ses_id');
995  $statement->execute([':ses_id' => $id]);
996  $row = $statement->fetch(\TYPO3\CMS\Core\Database\PreparedStatement::FETCH_NUM);
997  $statement->free();
998  return (bool)$row[0];
999  }
1000 
1007  public function isCookieSet()
1008  {
1009  return $this->cookieWasSetOnCurrentRequest || $this->getCookie($this->name);
1010  }
1011 
1012  /*************************
1013  *
1014  * SQL Functions
1015  *
1016  *************************/
1027  protected function fetchUserSessionFromDB()
1028  {
1029  $statement = null;
1030  $ipLockClause = $this->ipLockClause();
1031  $statement = $this->db->prepare_SELECTquery('*', $this->session_table . ',' . $this->user_table, $this->session_table . '.ses_id = :ses_id
1032  AND ' . $this->session_table . '.ses_name = :ses_name
1033  AND ' . $this->session_table . '.ses_userid = ' . $this->user_table . '.' . $this->userid_column . '
1034  ' . $ipLockClause['where'] . '
1035  ' . $this->hashLockClause() . '
1036  ' . $this->user_where_clause());
1037  $statement->bindValues([
1038  ':ses_id' => $this->id,
1039  ':ses_name' => $this->name
1040  ]);
1041  $statement->bindValues($ipLockClause['parameters']);
1042  return $statement;
1043  }
1044 
1052  protected function user_where_clause()
1053  {
1054  $whereClause = '';
1055  if ($this->enablecolumns['rootLevel']) {
1056  $whereClause .= 'AND ' . $this->user_table . '.pid=0 ';
1057  }
1058  if ($this->enablecolumns['disabled']) {
1059  $whereClause .= ' AND ' . $this->user_table . '.' . $this->enablecolumns['disabled'] . '=0';
1060  }
1061  if ($this->enablecolumns['deleted']) {
1062  $whereClause .= ' AND ' . $this->user_table . '.' . $this->enablecolumns['deleted'] . '=0';
1063  }
1064  if ($this->enablecolumns['starttime']) {
1065  $whereClause .= ' AND (' . $this->user_table . '.' . $this->enablecolumns['starttime'] . '<=' . $GLOBALS['EXEC_TIME'] . ')';
1066  }
1067  if ($this->enablecolumns['endtime']) {
1068  $whereClause .= ' AND (' . $this->user_table . '.' . $this->enablecolumns['endtime'] . '=0 OR '
1069  . $this->user_table . '.' . $this->enablecolumns['endtime'] . '>' . $GLOBALS['EXEC_TIME'] . ')';
1070  }
1071  return $whereClause;
1072  }
1073 
1080  protected function ipLockClause()
1081  {
1082  $statementClause = [
1083  'where' => '',
1084  'parameters' => []
1085  ];
1086  if ($this->lockIP) {
1087  $statementClause['where'] = 'AND (
1088  ' . $this->session_table . '.ses_iplock = :ses_iplock
1089  OR ' . $this->session_table . '.ses_iplock=\'[DISABLED]\'
1090  )';
1091  $statementClause['parameters'] = [
1092  ':ses_iplock' => $this->ipLockClause_remoteIPNumber($this->lockIP)
1093  ];
1094  }
1095  return $statementClause;
1096  }
1097 
1105  protected function ipLockClause_remoteIPNumber($parts)
1106  {
1107  $IP = GeneralUtility::getIndpEnv('REMOTE_ADDR');
1108  if ($parts >= 4) {
1109  return $IP;
1110  } else {
1112  $IPparts = explode('.', $IP);
1113  for ($a = 4; $a > $parts; $a--) {
1114  unset($IPparts[$a - 1]);
1115  }
1116  return implode('.', $IPparts);
1117  }
1118  }
1119 
1127  public function veriCode()
1128  {
1129  return substr(md5($this->id . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']), 0, 10);
1130  }
1131 
1138  protected function hashLockClause()
1139  {
1140  return 'AND ' . $this->session_table . '.ses_hashlock=' . $this->hashLockClause_getHashInt();
1141  }
1142 
1149  protected function hashLockClause_getHashInt()
1150  {
1151  $hashStr = '';
1152  if (GeneralUtility::inList($this->lockHashKeyWords, 'useragent')) {
1153  $hashStr .= ':' . GeneralUtility::getIndpEnv('HTTP_USER_AGENT');
1154  }
1155  return GeneralUtility::md5int($hashStr);
1156  }
1157 
1158  /*************************
1159  *
1160  * Session and Configuration Handling
1161  *
1162  *************************/
1171  public function writeUC($variable = '')
1172  {
1173  if (is_array($this->user) && $this->user[$this->userid_column]) {
1174  if (!is_array($variable)) {
1175  $variable = $this->uc;
1176  }
1177  if ($this->writeDevLog) {
1178  GeneralUtility::devLog('writeUC: ' . $this->userid_column . '=' . (int)$this->user[$this->userid_column], \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
1179  }
1180  $this->db->exec_UPDATEquery($this->user_table, $this->userid_column . '=' . (int)$this->user[$this->userid_column], ['uc' => serialize($variable)]);
1181  }
1182  }
1183 
1191  public function unpack_uc($theUC = '')
1192  {
1193  if (!$theUC && isset($this->user['uc'])) {
1194  $theUC = unserialize($this->user['uc']);
1195  }
1196  if (is_array($theUC)) {
1197  $this->uc = $theUC;
1198  }
1199  }
1200 
1211  public function pushModuleData($module, $data, $noSave = 0)
1212  {
1213  $this->uc['moduleData'][$module] = $data;
1214  $this->uc['moduleSessionID'][$module] = $this->id;
1215  if (!$noSave) {
1216  $this->writeUC();
1217  }
1218  }
1219 
1227  public function getModuleData($module, $type = '')
1228  {
1229  if ($type != 'ses' || (isset($this->uc['moduleSessionID'][$module]) && $this->uc['moduleSessionID'][$module] == $this->id)) {
1230  return $this->uc['moduleData'][$module];
1231  }
1232  return null;
1233  }
1234 
1242  public function getSessionData($key)
1243  {
1244  $sesDat = unserialize($this->user['ses_data']);
1245  return $sesDat[$key];
1246  }
1247 
1256  public function setAndSaveSessionData($key, $data)
1257  {
1258  $sesDat = unserialize($this->user['ses_data']);
1259  $sesDat[$key] = $data;
1260  $this->user['ses_data'] = serialize($sesDat);
1261  if ($this->writeDevLog) {
1262  GeneralUtility::devLog('setAndSaveSessionData: ses_id = ' . $this->user['ses_id'], \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
1263  }
1264  $this->db->exec_UPDATEquery($this->session_table, 'ses_id=' . $this->db->fullQuoteStr($this->user['ses_id'], $this->session_table), ['ses_data' => $this->user['ses_data']]);
1265  }
1266 
1267  /*************************
1268  *
1269  * Misc
1270  *
1271  *************************/
1278  public function getLoginFormData()
1279  {
1280  $loginData = [];
1281  $loginData['status'] = GeneralUtility::_GP($this->formfield_status);
1282  if ($this->getMethodEnabled) {
1283  $loginData['uname'] = GeneralUtility::_GP($this->formfield_uname);
1284  $loginData['uident'] = GeneralUtility::_GP($this->formfield_uident);
1285  } else {
1286  $loginData['uname'] = GeneralUtility::_POST($this->formfield_uname);
1287  $loginData['uident'] = GeneralUtility::_POST($this->formfield_uident);
1288  }
1289  // Only process the login data if a login is requested
1290  if ($loginData['status'] === 'login') {
1291  $loginData = $this->processLoginData($loginData);
1292  }
1293  $loginData = array_map('trim', $loginData);
1294  return $loginData;
1295  }
1296 
1306  public function processLoginData($loginData, $passwordTransmissionStrategy = '')
1307  {
1308  $loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['loginSecurityLevel']) ?: 'normal';
1309  $passwordTransmissionStrategy = $passwordTransmissionStrategy ?: $loginSecurityLevel;
1310  if ($this->writeDevLog) {
1311  GeneralUtility::devLog('Login data before processing: ' . GeneralUtility::arrayToLogString($loginData), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
1312  }
1313  $serviceChain = '';
1314  $subType = 'processLoginData' . $this->loginType;
1315  $authInfo = $this->getAuthInfoArray();
1316  $isLoginDataProcessed = false;
1317  $processedLoginData = $loginData;
1318  while (is_object($serviceObject = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
1319  $serviceChain .= ',' . $serviceObject->getServiceKey();
1320  $serviceObject->initAuth($subType, $loginData, $authInfo, $this);
1321  $serviceResult = $serviceObject->processLoginData($processedLoginData, $passwordTransmissionStrategy);
1322  if (!empty($serviceResult)) {
1323  $isLoginDataProcessed = true;
1324  // If the service returns >=200 then no more processing is needed
1325  if ((int)$serviceResult >= 200) {
1326  unset($serviceObject);
1327  break;
1328  }
1329  }
1330  unset($serviceObject);
1331  }
1332  if ($isLoginDataProcessed) {
1333  $loginData = $processedLoginData;
1334  if ($this->writeDevLog) {
1335  GeneralUtility::devLog('Processed login data: ' . GeneralUtility::arrayToLogString($processedLoginData), \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::class);
1336  }
1337  }
1338  return $loginData;
1339  }
1340 
1347  public function getAuthInfoArray()
1348  {
1349  $authInfo = [];
1350  $authInfo['loginType'] = $this->loginType;
1351  $authInfo['refInfo'] = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
1352  $authInfo['HTTP_HOST'] = GeneralUtility::getIndpEnv('HTTP_HOST');
1353  $authInfo['REMOTE_ADDR'] = GeneralUtility::getIndpEnv('REMOTE_ADDR');
1354  $authInfo['REMOTE_HOST'] = GeneralUtility::getIndpEnv('REMOTE_HOST');
1355  $authInfo['showHiddenRecords'] = $this->showHiddenRecords;
1356  // Can be overidden in localconf by SVCONF:
1357  $authInfo['db_user']['table'] = $this->user_table;
1358  $authInfo['db_user']['userid_column'] = $this->userid_column;
1359  $authInfo['db_user']['username_column'] = $this->username_column;
1360  $authInfo['db_user']['userident_column'] = $this->userident_column;
1361  $authInfo['db_user']['usergroup_column'] = $this->usergroup_column;
1362  $authInfo['db_user']['enable_clause'] = $this->user_where_clause();
1363  if ($this->checkPid && $this->checkPid_value !== null) {
1364  $authInfo['db_user']['checkPidList'] = $this->checkPid_value;
1365  $authInfo['db_user']['check_pid_clause'] = ' AND pid IN (' .
1366  $this->db->cleanIntList($this->checkPid_value) . ')';
1367  } else {
1368  $authInfo['db_user']['checkPidList'] = '';
1369  $authInfo['db_user']['check_pid_clause'] = '';
1370  }
1371  $authInfo['db_groups']['table'] = $this->usergroup_table;
1372  return $authInfo;
1373  }
1374 
1383  public function compareUident($user, $loginData, $passwordCompareStrategy = '')
1384  {
1385  return (string)$loginData['uident_text'] !== '' && (string)$loginData['uident_text'] === (string)$user[$this->userident_column];
1386  }
1387 
1394  public function gc()
1395  {
1396  $this->db->exec_DELETEquery($this->session_table, 'ses_tstamp < ' . (int)($GLOBALS['EXEC_TIME'] - $this->gc_time) . ' AND ses_name = ' . $this->db->fullQuoteStr($this->name, $this->session_table));
1397  }
1398 
1413  public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename, $recuid, $recpid)
1414  {
1415  }
1416 
1426  public function checkLogFailures($email, $secondsBack, $maxFailures)
1427  {
1428  }
1429 
1443  public function setBeUserByUid($uid)
1444  {
1445  $this->user = $this->getRawUserByUid($uid);
1446  }
1447 
1456  public function setBeUserByName($name)
1457  {
1458  $this->user = $this->getRawUserByName($name);
1459  }
1460 
1468  public function getRawUserByUid($uid)
1469  {
1470  $user = false;
1471  $dbres = $this->db->exec_SELECTquery('*', $this->user_table, 'uid=' . (int)$uid . ' ' . $this->user_where_clause());
1472  if ($dbres) {
1473  $user = $this->db->sql_fetch_assoc($dbres);
1474  $this->db->sql_free_result($dbres);
1475  }
1476  return $user;
1477  }
1478 
1487  public function getRawUserByName($name)
1488  {
1489  $user = false;
1490  $dbres = $this->db->exec_SELECTquery('*', $this->user_table, 'username=' . $this->db->fullQuoteStr($name, $this->user_table) . ' ' . $this->user_where_clause());
1491  if ($dbres) {
1492  $user = $this->db->sql_fetch_assoc($dbres);
1493  $this->db->sql_free_result($dbres);
1494  }
1495  return $user;
1496  }
1497 
1498  /*************************
1499  *
1500  * Create/update user - EXPERIMENTAL
1501  *
1502  *************************/
1512  public function fetchUserRecord($dbUser, $username, $extraWhere = '')
1513  {
1514  $user = false;
1515  $usernameClause = $username ? $dbUser['username_column'] . '=' . $this->db->fullQuoteStr($username, $dbUser['table']) : '1=1';
1516  if ($username || $extraWhere) {
1517  // Look up the user by the username and/or extraWhere:
1518  $dbres = $this->db->exec_SELECTquery('*', $dbUser['table'], $usernameClause . $dbUser['check_pid_clause'] . $dbUser['enable_clause'] . $extraWhere);
1519  if ($dbres) {
1520  $user = $this->db->sql_fetch_assoc($dbres);
1521  $this->db->sql_free_result($dbres);
1522  }
1523  }
1524  return $user;
1525  }
1526 
1531  protected function getDatabaseConnection()
1532  {
1533  return $GLOBALS['TYPO3_DB'];
1534  }
1535 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
static arrayToLogString(array $arr, $valueList=[], $valueLength=20)
compareUident($user, $loginData, $passwordCompareStrategy='')
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static lockRecords($table='', $uid=0, $pid=0)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
processLoginData($loginData, $passwordTransmissionStrategy='')
static makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=[])
$uid
Definition: server.php:38
writelog($type, $action, $error, $details_nr, $details, $data, $tablename, $recuid, $recpid)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']